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