subspace_proof_of_space/
shim.rs

1//! Shim proof of space implementation that works much faster than Chia and can be used for testing
2//! purposes to reduce memory and CPU usage
3
4#[cfg(feature = "alloc")]
5use crate::TableGenerator;
6use crate::{PosTableType, Table};
7use core::iter;
8use subspace_core_primitives::U256;
9use subspace_core_primitives::hashes::blake3_hash;
10use subspace_core_primitives::pos::{PosProof, PosSeed};
11
12/// Proof of space table generator.
13///
14/// Shim implementation.
15#[derive(Debug, Default, Clone)]
16#[cfg(feature = "alloc")]
17pub struct ShimTableGenerator;
18
19#[cfg(feature = "alloc")]
20impl TableGenerator<ShimTable> for ShimTableGenerator {
21    fn generate(&self, seed: &PosSeed) -> ShimTable {
22        ShimTable { seed: *seed }
23    }
24}
25
26/// Proof of space table.
27///
28/// Shim implementation.
29#[derive(Debug)]
30pub struct ShimTable {
31    #[cfg(feature = "alloc")]
32    seed: PosSeed,
33}
34
35impl subspace_core_primitives::solutions::SolutionPotVerifier for ShimTable {
36    fn is_proof_valid(seed: &PosSeed, challenge_index: u32, proof: &PosProof) -> bool {
37        let Some(correct_proof) = find_proof(seed, challenge_index) else {
38            return false;
39        };
40
41        &correct_proof == proof
42    }
43}
44
45impl Table for ShimTable {
46    const TABLE_TYPE: PosTableType = PosTableType::Shim;
47    #[cfg(feature = "alloc")]
48    type Generator = ShimTableGenerator;
49
50    #[cfg(feature = "alloc")]
51    fn find_proof(&self, challenge_index: u32) -> Option<PosProof> {
52        find_proof(&self.seed, challenge_index)
53    }
54
55    fn is_proof_valid(seed: &PosSeed, challenge_index: u32, proof: &PosProof) -> bool {
56        <Self as subspace_core_primitives::solutions::SolutionPotVerifier>::is_proof_valid(
57            seed,
58            challenge_index,
59            proof,
60        )
61    }
62}
63
64fn find_proof(seed: &PosSeed, challenge_index: u32) -> Option<PosProof> {
65    let quality = blake3_hash(&challenge_index.to_le_bytes());
66    // Note: this is different to the ab-proof-of-space ShimTable implementation
67    if U256::from_le_bytes(*quality) % U256::from(3u32) > U256::zero() {
68        let mut proof = PosProof::default();
69        proof
70            .iter_mut()
71            .zip(seed.iter().chain(iter::repeat(quality.iter()).flatten()))
72            .for_each(|(output, input)| {
73                *output = *input;
74            });
75
76        Some(proof)
77    } else {
78        None
79    }
80}
81
82#[cfg(all(feature = "alloc", test))]
83mod tests {
84    use super::*;
85    use hex::FromHex;
86
87    type RawProof = [u8; PosProof::SIZE];
88
89    #[test]
90    fn basic() {
91        let seed = PosSeed::from([
92            35, 2, 52, 4, 51, 55, 23, 84, 91, 10, 111, 12, 13, 222, 151, 16, 228, 211, 254, 45, 92,
93            198, 204, 10, 9, 10, 11, 129, 139, 171, 15, 23,
94        ]);
95
96        let table = ShimTable::generator().generate(&seed);
97
98        let expected_results = [
99            (0, None),
100            (
101                1,
102                Some(PosProof::from(
103                    RawProof::from_hex(
104                        "23023404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17c610e85212d0697cb161d4ba431ba603f273feee7dcb7927c9ff5d74ae6cbfa3c610e85212d0697cb161d4ba431ba603f273feee7dcb7927c9ff5d74ae6cbfa3c610e85212d0697cb161d4ba431ba603f273feee7dcb7927c9ff5d74ae6cbfa3c610e85212d0697cb161d4ba431ba603f273feee7dcb7927c9ff5d74ae6cbfa3",
105                    )
106                    .unwrap(),
107                )),
108            ),
109            (2, Some(PosProof::from(
110                RawProof::from_hex(
111                    "23023404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17f03bf86f79d121cbfd774dec4a65912e99f5f17c33852bbc45e819160e62b53bf03bf86f79d121cbfd774dec4a65912e99f5f17c33852bbc45e819160e62b53bf03bf86f79d121cbfd774dec4a65912e99f5f17c33852bbc45e819160e62b53bf03bf86f79d121cbfd774dec4a65912e99f5f17c33852bbc45e819160e62b53b",
112                )
113                .unwrap(),
114            ))),
115            (3, None),
116            (4, Some(PosProof::from(
117                RawProof::from_hex(
118                    "23023404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17669c13550a3e727bb53d0d458f2e96e48571aa045dfabcfb4b7de16809484f11669c13550a3e727bb53d0d458f2e96e48571aa045dfabcfb4b7de16809484f11669c13550a3e727bb53d0d458f2e96e48571aa045dfabcfb4b7de16809484f11669c13550a3e727bb53d0d458f2e96e48571aa045dfabcfb4b7de16809484f11",
119                )
120                .unwrap(),
121            ))),
122            (5, Some(PosProof::from(
123                RawProof::from_hex(
124                    "23023404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17e84248fb50d0833361d0417df114b0b3b34408fff97c39cd0de963b09a9aebb8e84248fb50d0833361d0417df114b0b3b34408fff97c39cd0de963b09a9aebb8e84248fb50d0833361d0417df114b0b3b34408fff97c39cd0de963b09a9aebb8e84248fb50d0833361d0417df114b0b3b34408fff97c39cd0de963b09a9aebb8",
125                )
126                .unwrap(),
127            ))),
128            (6, Some(PosProof::from(
129                RawProof::from_hex(
130                    "23023404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17edaf1bd3d1c2ffcc44df55829c002f262426de2ffbea9be2cdf0075ec12c528dedaf1bd3d1c2ffcc44df55829c002f262426de2ffbea9be2cdf0075ec12c528dedaf1bd3d1c2ffcc44df55829c002f262426de2ffbea9be2cdf0075ec12c528dedaf1bd3d1c2ffcc44df55829c002f262426de2ffbea9be2cdf0075ec12c528d",
131                )
132                .unwrap(),
133            ))),
134            (7, None),
135        ];
136
137        for (challenge_index, expected_proof) in expected_results {
138            let proof = table.find_proof(challenge_index);
139            assert_eq!(
140                proof, expected_proof,
141                "proof mismatch for challenge_index: {challenge_index}",
142            );
143
144            if let Some(proof) = proof {
145                assert!(
146                    ShimTable::is_proof_valid(&seed, challenge_index, &proof),
147                    "proof is not valid for challenge_index: {challenge_index}",
148                );
149            }
150        }
151    }
152}