1#[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#[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#[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 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}