nectar_primitives/bmt/
proof.rs1use alloy_primitives::{B256, Keccak256};
7
8use crate::bmt::{Hasher, constants::*, error::BmtError};
9use crate::error::Result;
10
11#[inline(always)]
16fn new_node_hasher(prefix: Option<&[u8]>) -> Keccak256 {
17 let mut hasher = Keccak256::new();
18 if let Some(p) = prefix {
19 hasher.update(p);
20 }
21 hasher
22}
23
24fn prefixed_zero_hash(prefix: Option<&[u8]>, level: usize) -> B256 {
29 let mut hash = {
30 let mut hasher = new_node_hasher(prefix);
31 hasher.update([0u8; 2 * SEGMENT_SIZE]);
32 B256::from_slice(hasher.finalize().as_slice())
33 };
34 for _ in 0..level {
35 let mut hasher = new_node_hasher(prefix);
36 hasher.update(hash.as_slice());
37 hasher.update(hash.as_slice());
38 hash = B256::from_slice(hasher.finalize().as_slice());
39 }
40 hash
41}
42
43#[derive(Clone, Debug)]
45pub struct Proof {
46 pub segment_index: usize,
48 pub segment: B256,
50 pub proof_segments: Vec<B256>,
52 pub span: u64,
54 pub prefix: Option<Vec<u8>>,
56}
57
58impl Proof {
59 pub const fn new(
61 segment_index: usize,
62 segment: B256,
63 proof_segments: Vec<B256>,
64 span: u64,
65 prefix: Option<Vec<u8>>,
66 ) -> Self {
67 Self {
68 segment_index,
69 segment,
70 proof_segments,
71 span,
72 prefix,
73 }
74 }
75
76 pub fn verify(&self, root_hash: &[u8]) -> Result<bool> {
78 if self.proof_segments.len() != PROOF_LENGTH {
79 return Err(
80 BmtError::invalid_proof_length(PROOF_LENGTH, self.proof_segments.len()).into(),
81 );
82 }
83
84 let mut current_hash = self.segment;
86 let mut current_index = self.segment_index;
87
88 let prefix = self.prefix.as_deref();
89
90 for proof_segment in &self.proof_segments {
92 let mut hasher = new_node_hasher(prefix);
96
97 if current_index.is_multiple_of(2) {
99 hasher.update(current_hash.as_slice());
100 hasher.update(proof_segment.as_slice());
101 } else {
102 hasher.update(proof_segment.as_slice());
103 hasher.update(current_hash.as_slice());
104 }
105
106 current_hash = B256::from_slice(hasher.finalize().as_slice());
108 current_index /= 2;
109 }
110
111 let mut hasher = Keccak256::new();
113
114 if let Some(prefix) = &self.prefix {
116 hasher.update(prefix);
117 }
118
119 hasher.update(self.span.to_le_bytes());
121
122 hasher.update(current_hash.as_slice());
124
125 let computed_root = B256::from_slice(hasher.finalize().as_slice());
126
127 Ok(computed_root.as_slice() == root_hash)
129 }
130}
131
132pub trait Prover {
134 fn generate_proof(&self, data: &[u8], segment_index: usize) -> Result<Proof>;
136
137 fn verify_proof(proof: &Proof, root_hash: &[u8]) -> Result<bool>;
139}
140
141impl Prover for Hasher {
142 fn generate_proof(&self, data: &[u8], segment_index: usize) -> Result<Proof> {
143 if segment_index >= BRANCHES {
144 return Err(self::BmtError::invalid_input_size(format!(
145 "Segment index {segment_index} out of bounds for BRANCHES"
146 ))
147 .into());
148 }
149
150 let data_len = data.len();
152
153 #[cfg(not(target_arch = "wasm32"))]
155 let segments = {
156 use rayon::prelude::*;
157 (0..BRANCHES)
158 .into_par_iter()
159 .map(|i| {
160 let start = i * SEGMENT_SIZE;
161 let mut segment = [0u8; SEGMENT_SIZE];
162
163 if start < data_len {
164 let end = (start + SEGMENT_SIZE).min(data_len);
165 let copy_len = end - start;
166 segment[..copy_len].copy_from_slice(&data[start..end]);
167 }
168
169 B256::from_slice(&segment)
170 })
171 .collect::<Vec<_>>()
172 };
173
174 #[cfg(target_arch = "wasm32")]
175 let segments = {
176 let mut segs = Vec::with_capacity(BRANCHES);
177 for i in 0..BRANCHES {
178 let start = i * SEGMENT_SIZE;
179 let mut segment = [0u8; SEGMENT_SIZE];
180
181 if start < data_len {
182 let end = (start + SEGMENT_SIZE).min(data_len);
183 let copy_len = end - start;
184 segment[..copy_len].copy_from_slice(&data[start..end]);
185 }
186
187 segs.push(B256::from_slice(&segment));
188 }
189
190 segs
191 };
192
193 let segment = segments[segment_index];
195
196 let prefix = if self.prefix().is_empty() {
198 None
199 } else {
200 Some(self.prefix().to_vec())
201 };
202 let prefix_ref = prefix.as_deref();
203
204 let mut proof_segments = Vec::with_capacity(PROOF_LENGTH);
206
207 let mut current_level = segments;
209 let mut current_index = segment_index;
210 let mut level: usize = 0;
214
215 while proof_segments.len() < PROOF_LENGTH {
217 let sibling_index = if current_index.is_multiple_of(2) {
219 current_index + 1
220 } else {
221 current_index - 1
222 };
223
224 if sibling_index < current_level.len() {
227 proof_segments.push(current_level[sibling_index]);
228 } else if level == 0 {
229 proof_segments.push(B256::ZERO);
231 } else {
232 proof_segments.push(prefixed_zero_hash(prefix_ref, level - 1));
233 }
234
235 let mut next_level = Vec::with_capacity(current_level.len().div_ceil(2));
237
238 for i in (0..current_level.len()).step_by(2) {
239 let left = ¤t_level[i];
240 let right = if i + 1 < current_level.len() {
241 current_level[i + 1]
242 } else if level == 0 {
243 B256::ZERO
244 } else {
245 prefixed_zero_hash(prefix_ref, level - 1)
246 };
247
248 let mut hasher = new_node_hasher(prefix_ref);
250 hasher.update(left.as_slice());
251 hasher.update(right.as_slice());
252
253 let parent = B256::from_slice(hasher.finalize().as_slice());
254 next_level.push(parent);
255 }
256
257 current_level = next_level;
259 current_index /= 2;
260 level += 1;
261
262 if current_level.len() <= 1 {
264 break;
265 }
266 }
267
268 while proof_segments.len() < PROOF_LENGTH {
270 proof_segments.push(B256::ZERO);
271 }
272
273 Ok(Proof::new(
274 segment_index,
275 segment,
276 proof_segments,
277 self.span(),
278 prefix,
279 ))
280 }
281
282 fn verify_proof(proof: &Proof, root_hash: &[u8]) -> Result<bool> {
283 proof.verify(root_hash)
284 }
285}