nectar_primitives/bmt/
proof.rs1use alloy_primitives::{B256, Keccak256};
7
8use super::hasher::hash_pairs;
9use crate::bmt::{Hasher, constants::*, error::BmtError};
10use crate::error::Result;
11
12#[inline(always)]
17fn new_node_hasher(prefix: Option<&[u8]>) -> Keccak256 {
18 let mut hasher = Keccak256::new();
19 if let Some(p) = prefix {
20 hasher.update(p);
21 }
22 hasher
23}
24
25#[derive(Clone, Debug)]
27pub struct Proof {
28 pub segment_index: usize,
30 pub segment: B256,
32 pub proof_segments: Vec<B256>,
34 pub span: u64,
36 pub prefix: Option<Vec<u8>>,
38}
39
40impl Proof {
41 pub const fn new(
43 segment_index: usize,
44 segment: B256,
45 proof_segments: Vec<B256>,
46 span: u64,
47 prefix: Option<Vec<u8>>,
48 ) -> Self {
49 Self {
50 segment_index,
51 segment,
52 proof_segments,
53 span,
54 prefix,
55 }
56 }
57
58 pub fn verify(&self, root_hash: &[u8]) -> Result<bool> {
60 if self.proof_segments.len() != PROOF_LENGTH {
61 return Err(
62 BmtError::invalid_proof_length(PROOF_LENGTH, self.proof_segments.len()).into(),
63 );
64 }
65
66 let mut current_hash = self.segment;
68 let mut current_index = self.segment_index;
69
70 let prefix = self.prefix.as_deref();
71
72 for proof_segment in &self.proof_segments {
74 let mut hasher = new_node_hasher(prefix);
78
79 if current_index.is_multiple_of(2) {
81 hasher.update(current_hash.as_slice());
82 hasher.update(proof_segment.as_slice());
83 } else {
84 hasher.update(proof_segment.as_slice());
85 hasher.update(current_hash.as_slice());
86 }
87
88 current_hash = B256::from_slice(hasher.finalize().as_slice());
90 current_index /= 2;
91 }
92
93 let mut hasher = Keccak256::new();
95
96 if let Some(prefix) = &self.prefix {
98 hasher.update(prefix);
99 }
100
101 hasher.update(self.span.to_le_bytes());
103
104 hasher.update(current_hash.as_slice());
106
107 let computed_root = B256::from_slice(hasher.finalize().as_slice());
108
109 Ok(computed_root.as_slice() == root_hash)
111 }
112}
113
114pub trait Prover {
116 fn generate_proof(&self, data: &[u8], segment_index: usize) -> Result<Proof>;
118
119 fn verify_proof(proof: &Proof, root_hash: &[u8]) -> Result<bool>;
121}
122
123impl Prover for Hasher {
124 fn generate_proof(&self, data: &[u8], segment_index: usize) -> Result<Proof> {
125 if segment_index >= BRANCHES {
126 return Err(self::BmtError::invalid_input_size(format!(
127 "Segment index {segment_index} out of bounds for BRANCHES"
128 ))
129 .into());
130 }
131
132 let mut leaves = [[0u8; SEGMENT_SIZE]; BRANCHES];
134 let n = data.len().min(BRANCHES * SEGMENT_SIZE);
135 for (leaf, chunk) in leaves.iter_mut().zip(data[..n].chunks(SEGMENT_SIZE)) {
136 leaf[..chunk.len()].copy_from_slice(chunk);
137 }
138
139 let segment = B256::from(leaves[segment_index]);
141
142 let prefix = if self.prefix().is_empty() {
144 None
145 } else {
146 Some(self.prefix().to_vec())
147 };
148 let prefix_ref = prefix.as_deref();
149
150 let mut levels: Vec<Vec<[u8; 32]>> = Vec::with_capacity(PROOF_LENGTH);
154 let mut current = leaves.to_vec();
155 while current.len() > 1 {
156 let mut next = vec![[0u8; 32]; current.len() / 2];
157 hash_pairs(prefix_ref, current.as_flattened(), &mut next);
158 levels.push(current);
159 current = next;
160 }
161
162 let mut proof_segments = Vec::with_capacity(PROOF_LENGTH);
164 let mut index = segment_index;
165 for level in &levels {
166 proof_segments.push(B256::from(level[index ^ 1]));
167 index /= 2;
168 }
169
170 Ok(Proof::new(
171 segment_index,
172 segment,
173 proof_segments,
174 self.span(),
175 prefix,
176 ))
177 }
178
179 fn verify_proof(proof: &Proof, root_hash: &[u8]) -> Result<bool> {
180 proof.verify(root_hash)
181 }
182}