1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4
5use super::leaf_index_to_mt_index_and_peak_index::MmrLeafIndexToMtIndexAndPeakIndex;
6use crate::hashing::merkle_step_mem_u64_index::MerkleStepMemU64Index;
7use crate::list::get::Get;
8use crate::prelude::*;
9use crate::traits::basic_snippet::Reviewer;
10use crate::traits::basic_snippet::SignOffFingerprint;
11
12#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
38pub struct MmrVerifyFromMemory;
39
40impl BasicSnippet for MmrVerifyFromMemory {
41 fn parameters(&self) -> Vec<(DataType, String)> {
42 let list_type = DataType::List(Box::new(DataType::Digest));
43
44 vec![
45 (list_type.clone(), "*peaks".to_string()),
46 (DataType::U64, "leaf_count".to_string()),
47 (DataType::U64, "leaf_index".to_string()),
48 (DataType::Digest, "leaf".to_string()),
49 (list_type, "*auth_path".to_string()),
50 ]
51 }
52
53 fn return_values(&self) -> Vec<(DataType, String)> {
54 vec![(DataType::Bool, "validation_result".to_string())]
55 }
56
57 fn entrypoint(&self) -> String {
58 "tasmlib_mmr_verify_from_memory".into()
59 }
60
61 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
62 let leaf_index_to_mt_index = library.import(Box::new(MmrLeafIndexToMtIndexAndPeakIndex));
63 let get_list_element = library.import(Box::new(Get::new(DataType::Digest)));
64 let merkle_step_mem = library.import(Box::new(MerkleStepMemU64Index));
65
66 let entrypoint = self.entrypoint();
67 let loop_label = format!("{entrypoint}_loop");
68
69 triton_asm!(
70 {entrypoint}:
73 pick 9 pick 9
74 pick 9 pick 9
75 call {leaf_index_to_mt_index}
76 place 8
79 place 7 place 7
82 addi 1
85 place 7
86 call {loop_label}
89 pick 9 pick 9
95 call {get_list_element}
98 {&DataType::Digest.compare()}
101 place 3
105 pop 3
106 return
109
110 {loop_label}:
112 dup 6 push 0 eq
113 dup 6 push 1 eq
114 mul
115 skiz return
118 call {merkle_step_mem}
121 recurse
122 )
123 }
124
125 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
126 let mut sign_offs = HashMap::new();
127 sign_offs.insert(Reviewer("ferdinand"), 0x79df3fecee2c597.into());
128 sign_offs
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use itertools::Itertools;
135 use rand::prelude::*;
136 use twenty_first::math::other::random_elements;
137 use twenty_first::util_types::mmr::mmr_accumulator::util::mmra_with_mps;
138
139 use super::*;
140 use crate::empty_stack;
141 use crate::mmr::MAX_MMR_HEIGHT;
142 use crate::test_prelude::*;
143 use crate::twenty_first::prelude::*;
144 use crate::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator;
145
146 impl MmrVerifyFromMemory {
147 fn set_up_initial_state(
148 &self,
149 mmr: &MmrAccumulator,
150 leaf: Digest,
151 leaf_index: u64,
152 auth_path: Vec<Digest>,
153 ) -> FunctionInitialState {
154 let peaks_pointer = bfe!(1);
155 let auth_path_pointer = bfe!(MAX_MMR_HEIGHT * Digest::LEN + 1);
156
157 let mut stack = self.init_stack_for_isolated_run();
158 stack.push(peaks_pointer);
159 push_encodable(&mut stack, &mmr.num_leafs());
160 push_encodable(&mut stack, &leaf_index);
161 push_encodable(&mut stack, &leaf);
162 stack.push(auth_path_pointer);
163
164 let mut memory = HashMap::default();
165 encode_to_memory(&mut memory, peaks_pointer, &mmr.peaks());
166 encode_to_memory(&mut memory, auth_path_pointer, &auth_path);
167
168 FunctionInitialState { stack, memory }
169 }
170 }
171
172 impl Function for MmrVerifyFromMemory {
173 fn rust_shadow(
174 &self,
175 stack: &mut Vec<BFieldElement>,
176 memory: &mut HashMap<BFieldElement, BFieldElement>,
177 ) -> Result<(), RustShadowError> {
178 let auth_path_pointer = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
179 let leaf = pop_encodable(stack)?;
180 let leaf_index = pop_encodable(stack)?;
181 let leaf_count = pop_encodable(stack)?;
182 let peaks_pointer = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
183
184 let auth_path = *Vec::<Digest>::decode_from_memory(memory, auth_path_pointer)
185 .map_err(|_| RustShadowError::DecodingError)?;
186 let peaks = *Vec::<Digest>::decode_from_memory(memory, peaks_pointer)
187 .map_err(|_| RustShadowError::DecodingError)?;
188
189 let proof = MmrMembershipProof::new(auth_path);
190 let is_valid = proof.verify(leaf_index, leaf, &peaks, leaf_count);
191 push_encodable(stack, &is_valid);
192 Ok(())
193 }
194
195 fn pseudorandom_initial_state(
196 &self,
197 seed: [u8; 32],
198 bench_case: Option<BenchmarkCase>,
199 ) -> FunctionInitialState {
200 let mut rng = StdRng::from_seed(seed);
201 let (leaf_index, leaf_count) = match bench_case {
202 Some(BenchmarkCase::CommonCase) => ((1 << 31) - 1, 1 << 31),
203 Some(BenchmarkCase::WorstCase) => ((1 << 62) - 1, 1 << 62),
204 None => {
205 let leaf_count = rng.random_range(1..=1 << 62);
206 let leaf_index = rng.random_range(0..leaf_count);
207 (leaf_index, leaf_count)
208 }
209 };
210
211 let leaf = rng.random();
212 let (mmra, mps) = mmra_with_mps(leaf_count, vec![(leaf_index, leaf)]);
213 let auth_path = mps[0].authentication_path.clone();
214
215 self.set_up_initial_state(&mmra, leaf, leaf_index, auth_path)
216 }
217 }
218
219 #[macro_rules_attr::apply(test)]
220 fn rust_shadow() {
221 ShadowedFunction::new(MmrVerifyFromMemory).test();
222 }
223
224 #[macro_rules_attr::apply(test)]
226 #[should_panic]
227 fn mmra_ap_verify_test_empty() {
228 let digest0 = Tip5::hash(&BFieldElement::new(4545));
229 let mmr = MmrAccumulator::new_from_leafs(vec![]);
230 let leaf_index = 0;
231 prop_verify_from_memory(&mmr, digest0, leaf_index, vec![], false);
232 }
233
234 #[macro_rules_attr::apply(test)]
235 fn mmra_ap_verify_test_one() {
236 let digest0 = Tip5::hash(&BFieldElement::new(4545));
237 let mut mmr = MmrAccumulator::new_from_leafs(vec![]);
238 mmr.append(digest0);
239 let leaf_index = 0;
240 prop_verify_from_memory(&mmr, digest0, leaf_index, vec![], true);
241 }
242
243 #[macro_rules_attr::apply(test)]
244 fn mmra_ap_verify_test_two() {
245 let digest0 = Tip5::hash(&BFieldElement::new(123));
246 let digest1 = Tip5::hash(&BFieldElement::new(456));
247
248 let mut mmr = MmrAccumulator::new_from_leafs(vec![]);
249 mmr.append(digest0);
250 mmr.append(digest1);
251
252 let leaf_index_0 = 0;
253 prop_verify_from_memory(&mmr, digest0, leaf_index_0, vec![digest1], true);
254
255 let leaf_index_1 = 1;
256 prop_verify_from_memory(&mmr, digest1, leaf_index_1, vec![digest0], true);
257 }
258
259 #[macro_rules_attr::apply(test)]
260 fn mmra_ap_verify_test_pbt() {
261 let max_size = 19;
262
263 for leaf_count in 0..max_size {
264 let digests: Vec<Digest> = random_elements(leaf_count);
265
266 let (mmr, mps) = mmra_with_mps(
267 leaf_count as u64,
268 digests
269 .iter()
270 .cloned()
271 .enumerate()
272 .map(|(i, d)| (i as u64, d))
273 .collect_vec(),
274 );
275
276 let bad_leaf: Digest = rand::rng().random();
277 for (leaf_index, leaf_digest) in digests.into_iter().enumerate() {
278 let auth_path = mps[leaf_index].clone();
279
280 prop_verify_from_memory(
282 &mmr,
283 leaf_digest,
284 leaf_index as u64,
285 auth_path.authentication_path.clone(),
286 true,
287 );
288
289 let bad_index = (leaf_index + 1) % leaf_count;
291 if bad_index != leaf_index {
292 prop_verify_from_memory(
293 &mmr,
294 leaf_digest,
295 bad_index as u64,
296 auth_path.authentication_path.clone(),
297 false,
298 );
299 }
300 prop_verify_from_memory(
301 &mmr,
302 bad_leaf,
303 leaf_index as u64,
304 auth_path.authentication_path,
305 false,
306 );
307 }
308 }
309 }
310
311 #[macro_rules_attr::apply(test)]
312 fn mmra_ap_verify_many_leafs() {
313 for init_leaf_count in [
314 (1u64 << 40) + (1 << 21) + 510,
315 (1 << 32) - 1,
316 (1 << 61) - 3,
317 (1 << 61) - 2,
318 (1 << 61) - 1,
319 1 << 61,
320 ] {
321 let init_peak_count = init_leaf_count.count_ones();
323 println!("init_peak_count = {init_peak_count}");
324
325 let fake_peaks: Vec<Digest> = random_elements(init_peak_count as usize);
328 let mut mmr = MmrAccumulator::init(fake_peaks, init_leaf_count);
329
330 let second_to_last_leaf: Digest = rand::rng().random();
332 let second_to_last_leaf_index = init_leaf_count;
333 let mut real_membership_proof_second_to_last = mmr.append(second_to_last_leaf);
334
335 let last_leaf: Digest = rand::rng().random();
337 let last_leaf_index = second_to_last_leaf_index + 1;
338 MmrMembershipProof::update_from_append(
339 &mut real_membership_proof_second_to_last,
340 second_to_last_leaf_index,
341 mmr.num_leafs(),
342 last_leaf,
343 &mmr.peaks(),
344 );
345 let real_membership_proof_last = mmr.append(last_leaf);
346
347 prop_verify_from_memory(
349 &mmr,
350 second_to_last_leaf,
351 second_to_last_leaf_index,
352 real_membership_proof_second_to_last
353 .authentication_path
354 .clone(),
355 true,
356 );
357 prop_verify_from_memory(
358 &mmr,
359 last_leaf,
360 last_leaf_index,
361 real_membership_proof_last.authentication_path.clone(),
362 true,
363 );
364
365 let bad_leaf: Digest = rand::rng().random();
367 prop_verify_from_memory(
368 &mmr,
369 bad_leaf,
370 second_to_last_leaf_index,
371 real_membership_proof_second_to_last.authentication_path,
372 false,
373 );
374 prop_verify_from_memory(
375 &mmr,
376 bad_leaf,
377 last_leaf_index,
378 real_membership_proof_last.authentication_path,
379 false,
380 );
381 }
382 }
383
384 fn prop_verify_from_memory(
385 mmr: &MmrAccumulator,
386 leaf: Digest,
387 leaf_index: u64,
388 auth_path: Vec<Digest>,
389 expect_validation_success: bool,
390 ) {
391 let exec_state =
392 MmrVerifyFromMemory.set_up_initial_state(mmr, leaf, leaf_index, auth_path.clone());
393
394 let mut expected_final_stack = empty_stack();
396 expected_final_stack.push(bfe!(expect_validation_success as u64));
397
398 test_rust_equivalence_given_complete_state(
399 &ShadowedFunction::new(MmrVerifyFromMemory),
400 &exec_state.stack,
401 &[],
402 &NonDeterminism::default().with_ram(exec_state.memory),
403 &None,
404 Some(&expected_final_stack),
405 );
406
407 assert_eq!(
409 expect_validation_success,
410 MmrMembershipProof::new(auth_path).verify(
411 leaf_index,
412 leaf,
413 &mmr.peaks(),
414 mmr.num_leafs(),
415 )
416 );
417 }
418}
419
420#[cfg(test)]
421mod benches {
422 use super::*;
423 use crate::test_prelude::*;
424
425 #[macro_rules_attr::apply(test)]
426 fn benchmark() {
427 ShadowedFunction::new(MmrVerifyFromMemory).bench();
428 }
429}