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    array_windows,
7    const_convert,
8    const_trait_impl,
9    exact_size_is_empty,
10    float_erf,
11    generic_const_exprs,
12    get_mut_unchecked,
13    iter_array_chunks,
14    maybe_uninit_fill,
15    maybe_uninit_slice,
16    maybe_uninit_write_slice,
17    new_zeroed_alloc,
18    portable_simd,
19    ptr_as_ref_unchecked,
20    ptr_as_uninit,
21    step_trait,
22    sync_unsafe_cell,
23    vec_into_raw_parts
24)]
25
26pub mod chia;
27pub mod chiapos;
28pub mod shim;
29
30#[cfg(feature = "alloc")]
31extern crate alloc;
32
33#[cfg(feature = "alloc")]
34use ab_core_primitives::pieces::Record;
35use ab_core_primitives::pos::{PosProof, PosSeed};
36use ab_core_primitives::sectors::SBucket;
37use ab_core_primitives::solutions::SolutionPotVerifier;
38#[cfg(feature = "alloc")]
39use alloc::boxed::Box;
40#[cfg(feature = "alloc")]
41use core::fmt;
42
43/// Proof of space table type
44#[derive(Debug, Clone, Copy)]
45pub enum PosTableType {
46    /// Chia table
47    Chia,
48    /// Shim table
49    Shim,
50}
51
52// TODO: Return a single full proof and the rest as hashes instead to optimize memory usage and
53//  parallelize compute more easily
54/// Proof-of-space proofs
55#[derive(Debug)]
56#[cfg(feature = "alloc")]
57#[repr(C)]
58pub struct PosProofs {
59    /// S-buckets at which proofs were found.
60    ///
61    /// S-buckets are grouped by 8, within each `u8` bits right to left (LSB) indicate the presence
62    /// of a proof for corresponding s-bucket, so that the whole array of bytes can be thought as a
63    /// large set of bits.
64    ///
65    /// There will be at most [`Record::NUM_CHUNKS`] proofs produced/bits set to `1`.
66    pub found_proofs: [u8; Record::NUM_S_BUCKETS / u8::BITS as usize],
67    /// [`Record::NUM_CHUNKS`] proofs, corresponding to set bits of `found_proofs`.
68    pub proofs: [PosProof; Record::NUM_CHUNKS],
69}
70
71// TODO: A method that returns hashed proofs (with SIMD) for all s-buckets for plotting
72#[cfg(feature = "alloc")]
73impl PosProofs {
74    /// Get proof for specified s-bucket (if exists).
75    ///
76    /// Note that this is not the most efficient API possible, so prefer using the `proofs` field
77    /// directly if the use case allows.
78    #[inline]
79    pub fn for_s_bucket(&self, s_bucket: SBucket) -> Option<PosProof> {
80        let proof_index = Self::proof_index_for_s_bucket(self.found_proofs, s_bucket)?;
81
82        Some(self.proofs[proof_index])
83    }
84
85    #[inline(always)]
86    fn proof_index_for_s_bucket(
87        found_proofs: [u8; Record::NUM_S_BUCKETS / u8::BITS as usize],
88        s_bucket: SBucket,
89    ) -> Option<usize> {
90        let bits_offset = usize::from(s_bucket);
91        let found_proofs_byte_offset = bits_offset / u8::BITS as usize;
92        let found_proofs_bit_offset = bits_offset as u32 % u8::BITS;
93        let (found_proofs_before, found_proofs_after) =
94            found_proofs.split_at(found_proofs_byte_offset);
95        if (found_proofs_after[0] & (1 << found_proofs_bit_offset)) == 0 {
96            return None;
97        }
98        let proof_index = found_proofs_before
99            .iter()
100            .map(|&bits| bits.count_ones())
101            .sum::<u32>()
102            + found_proofs_after[0]
103                .unbounded_shl(u8::BITS - found_proofs_bit_offset)
104                .count_ones();
105
106        Some(proof_index as usize)
107    }
108}
109
110// TODO: Think about redesigning this API now that proofs are the output rather than tables
111/// Stateful table generator with better performance.
112///
113/// Prefer cloning it over creating multiple separate generators.
114#[cfg(feature = "alloc")]
115pub trait TableGenerator<T: Table>:
116    fmt::Debug + Default + Clone + Send + Sync + Sized + 'static
117{
118    /// Create proofs with 32 bytes seed.
119    ///
120    /// There is also `Self::create_proofs_parallel()` that can achieve higher performance and
121    /// lower latency at the cost of lower CPU efficiency and higher memory usage.
122    fn create_proofs(&self, seed: &PosSeed) -> Box<PosProofs>;
123
124    /// Almost the same as [`Self::create_proofs()`], but uses parallelism internally for better
125    /// performance and lower latency at the cost of lower CPU efficiency and higher memory usage
126    #[cfg(feature = "parallel")]
127    fn create_proofs_parallel(&self, seed: &PosSeed) -> Box<PosProofs> {
128        self.create_proofs(seed)
129    }
130}
131
132/// Proof of space kind
133pub trait Table: SolutionPotVerifier + Sized + Send + Sync + 'static {
134    /// Proof of space table type
135    const TABLE_TYPE: PosTableType;
136    /// Instance that can be used to generate tables with better performance
137    #[cfg(feature = "alloc")]
138    type Generator: TableGenerator<Self>;
139
140    /// Check whether proof created earlier is valid
141    fn is_proof_valid(seed: &PosSeed, s_bucket: SBucket, proof: &PosProof) -> bool;
142
143    /// Returns a stateful table generator with better performance
144    #[cfg(feature = "alloc")]
145    fn generator() -> Self::Generator {
146        Self::Generator::default()
147    }
148}