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