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