Skip to main content

ab_proof_of_space/
lib.rs

1//! Proof of space implementation
2#![no_std]
3#![expect(incomplete_features, reason = "generic_const_exprs")]
4#![warn(rust_2018_idioms, missing_debug_implementations, missing_docs)]
5#![feature(
6    const_block_items,
7    const_convert,
8    const_trait_impl,
9    generic_const_exprs,
10    step_trait
11)]
12#![cfg_attr(test, feature(float_erf))]
13#![cfg_attr(feature = "parallel", feature(exact_size_is_empty, sync_unsafe_cell))]
14#![cfg_attr(feature = "alloc", feature(maybe_uninit_fill, ptr_as_uninit))]
15#![cfg_attr(any(feature = "alloc", test), feature(portable_simd))]
16
17pub mod chiapos;
18
19#[cfg(feature = "alloc")]
20extern crate alloc;
21
22#[cfg(feature = "alloc")]
23use subspace_core_primitives::pieces::Record;
24#[cfg(feature = "alloc")]
25use subspace_core_primitives::pos::PosProof;
26#[cfg(feature = "alloc")]
27use subspace_core_primitives::sectors::SBucket;
28
29// TODO: Return a single full proof and the rest as hashes instead to optimize memory usage and
30//  parallelize compute more easily
31/// Proof-of-space proofs
32#[derive(Debug)]
33#[cfg(feature = "alloc")]
34#[repr(C)]
35pub struct PosProofs {
36    /// S-buckets at which proofs were found.
37    ///
38    /// S-buckets are grouped by 8, within each `u8` bits right to left (LSB) indicate the presence
39    /// of a proof for corresponding s-bucket, so that the whole array of bytes can be thought as a
40    /// large set of bits.
41    ///
42    /// There will be at most [`Record::NUM_CHUNKS`] proofs produced/bits set to `1`.
43    pub found_proofs: [u8; Record::NUM_S_BUCKETS / u8::BITS as usize],
44    /// [`Record::NUM_CHUNKS`] proofs, corresponding to set bits of `found_proofs`.
45    pub proofs: [PosProof; Record::NUM_CHUNKS],
46}
47
48// TODO: A method that returns hashed proofs (with SIMD) for all s-buckets for plotting
49#[cfg(feature = "alloc")]
50impl PosProofs {
51    /// Get proof for specified s-bucket (if exists).
52    ///
53    /// Note that this is not the most efficient API possible, so prefer using the `proofs` field
54    /// directly if the use case allows.
55    #[inline]
56    pub fn for_s_bucket(&self, s_bucket: SBucket) -> Option<PosProof> {
57        let proof_index = Self::proof_index_for_s_bucket(&self.found_proofs, s_bucket)?;
58
59        Some(self.proofs[proof_index])
60    }
61
62    #[inline(always)]
63    fn proof_index_for_s_bucket(
64        found_proofs: &[u8; Record::NUM_S_BUCKETS / u8::BITS as usize],
65        s_bucket: SBucket,
66    ) -> Option<usize> {
67        let bits_offset = usize::from(s_bucket);
68        let found_proofs_byte_offset = bits_offset / u8::BITS as usize;
69        let found_proofs_bit_offset = bits_offset as u32 % u8::BITS;
70        let (found_proofs_before, found_proofs_after) =
71            found_proofs.split_at(found_proofs_byte_offset);
72        if (found_proofs_after[0] & (1 << found_proofs_bit_offset)) == 0 {
73            return None;
74        }
75        let proof_index = found_proofs_before
76            .iter()
77            .map(|&bits| bits.count_ones())
78            .sum::<u32>()
79            + found_proofs_after[0]
80                .unbounded_shl(u8::BITS - found_proofs_bit_offset)
81                .count_ones();
82
83        Some(proof_index as usize)
84    }
85}