1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4use twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator;
5use twenty_first::util_types::mmr::mmr_successor_proof::MmrSuccessorProof;
6
7use crate::arithmetic::u64 as u64_lib;
8use crate::hashing::merkle_step_mem_u64_index::MerkleStepMemU64Index;
9use crate::hashing::merkle_step_u64_index::MerkleStepU64Index;
10use crate::mmr::leaf_index_to_mt_index_and_peak_index::MmrLeafIndexToMtIndexAndPeakIndex;
11use crate::prelude::*;
12use crate::traits::basic_snippet::Reviewer;
13use crate::traits::basic_snippet::SignOffFingerprint;
14
15#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
42pub struct VerifyMmrSuccessor;
43
44impl VerifyMmrSuccessor {
45 pub(crate) const OLD_HAS_MORE_LEAFS_THAN_NEW_ERROR_ID: i128 = 150;
46 pub(crate) const INCONSISTENT_OLD_MMR_ERROR_ID: i128 = 151;
47 pub(crate) const INCONSISTENT_NEW_MMR_ERROR_ID: i128 = 152;
48 pub(crate) const DIFFERING_SHARED_PEAK_ERROR_ID: i128 = 153;
49 pub(crate) const DIFFERING_UNSHARED_PEAK_ERROR_ID: i128 = 154;
50}
51
52impl BasicSnippet for VerifyMmrSuccessor {
53 fn inputs(&self) -> Vec<(DataType, String)> {
54 ["*old_mmr", "*new_mmr"]
55 .map(|ptr_name| (DataType::VoidPointer, ptr_name.to_string()))
56 .to_vec()
57 }
58
59 fn outputs(&self) -> Vec<(DataType, String)> {
60 vec![]
61 }
62
63 fn entrypoint(&self) -> String {
64 "tasm_lib_mmr_verify_mmr_successor".to_string()
65 }
66
67 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
68 let sub_u64 = library.import(Box::new(u64_lib::sub::Sub));
69 let lt_u64 = library.import(Box::new(u64_lib::lt::Lt));
70 let popcount_u64 = library.import(Box::new(u64_lib::popcount::PopCount));
71 let trailing_zeros_u64 = library.import(Box::new(u64_lib::trailing_zeros::TrailingZeros));
72 let shl_u64 = library.import(Box::new(u64_lib::shift_left::ShiftLeft));
73 let shr_u64 = library.import(Box::new(u64_lib::shift_right::ShiftRight));
74
75 let leaf_index_to_mti_and_pki = library.import(Box::new(MmrLeafIndexToMtIndexAndPeakIndex));
76 let merkle_step_u64 = library.import(Box::new(MerkleStepU64Index));
77 let merkle_step_mem_u64 = library.import(Box::new(MerkleStepMemU64Index));
78
79 let entrypoint = self.entrypoint();
80 let clean_up_because_old_mmr_has_0_leafs =
81 format!("{entrypoint}_clean_up_because_old_mmr_has_0_leafs");
82 let assert_mmr_equality = format!("{entrypoint}_assert_mmr_equality");
83 let assert_unchanged_peaks_equality =
84 format!("{entrypoint}_assert_unchanged_peaks_equality");
85 let clean_up_because_new_leafs_dont_affect_old_peaks =
86 format!("{entrypoint}_clean_up_because_new_leafs_dont_affect_old_peaks");
87 let traverse_new_tree = format!("{entrypoint}_traverse_new_tree");
88 let traverse_new_tree_right_sibling = format!("{traverse_new_tree}_right_sibling");
89
90 triton_asm! {
91 {entrypoint}:
94 pick 1
96 {&MmrAccumulator::destructure()}
97 hint old_num_leafs: Pointer = stack[0]
98 hint old_peaks: Pointer = stack[1]
99 addi 1
102 read_mem 2 hint old_num_leafs: u64 = stack[1..3]
103 pop 1
104 pick 2
107 read_mem 1 hint old_peaks_len = stack[1]
108 addi 1
109 place 1
110 dup 3 dup 3
113 call {popcount_u64}
114 eq assert error_id {Self::INCONSISTENT_OLD_MMR_ERROR_ID}
117 pick 3
121 {&MmrAccumulator::destructure()}
122 hint new_num_leafs: Pointer = stack[0]
123 hint new_peaks: Pointer = stack[1]
124 addi 1
127 read_mem 2 hint new_num_leafs: u64 = stack[1..3]
128 pop 1
129 pick 2
132 read_mem 1 hint new_peaks_len = stack[1]
133 addi 1
134 place 1
135 dup 3 dup 3
138 call {popcount_u64}
139 eq assert error_id {Self::INCONSISTENT_NEW_MMR_ERROR_ID}
142 push 0
146 dup 6 dup 6
147 push 0 push 0 hint zero: u64 = stack[0..2]
150 {&DataType::U64.compare()}
151 skiz call {clean_up_because_old_mmr_has_0_leafs}
154 skiz return
155 push 0
161 dup 6 dup 6
162 dup 5 dup 5
163 {&DataType::U64.compare()}
164 skiz call {assert_mmr_equality}
167 skiz return
168 dup 5 dup 5
172 dup 4 dup 4
173 call {lt_u64}
174 push 0 eq
177 assert error_id {Self::OLD_HAS_MORE_LEAFS_THAN_NEW_ERROR_ID}
180 dup 2 dup 2
184 dup 7 dup 7
185 call {leaf_index_to_mti_and_pki} hint num_unchanged_peaks = stack[0]
188 hint merkle_tree_idx: u64 = stack[1..3]
189 pick 2 pick 2
192 place 8 place 8
193 pick 4
196 read_mem 1
197 addi 2
198 pick 1
201 addi -1
202 push {Digest::LEN}
203 mul
204 dup 1
207 add hint last_old_peak: Pointer = stack[0]
208 place 1
209 pick 3
212 addi 1
213 pick 3
214 call {assert_unchanged_peaks_equality}
217 pick 2
220 pop 2
221 place 5
224 place 5
225 dup 3 dup 3
228 call {trailing_zeros_u64} hint height_of_lowest_old_peak = stack[0]
229 push 0 push 1 hint one: u64 = stack[0..2]
232 dup 2
233 call {shl_u64} hint num_leafs_in_lowest_old_peak: u64 = stack[0..2]
234 pick 6 pick 6
237 pick 6 pick 6
238 call {sub_u64}
239 call {lt_u64}
242 push 0
245 place 1
246 skiz call {clean_up_because_new_leafs_dont_affect_old_peaks}
247 skiz return
248 pick 4 pick 4
251 pick 2
252 call {shr_u64} hint merkle_tree_idx: u64 = stack[0..2]
255 pick 2
258 place 3
259 divine 5
260 call {traverse_new_tree}
263 pick 8
266 addi {Digest::LEN - 1}
267 read_mem {Digest::LEN}
268 pop 1
269 assert_vector error_id {Self::DIFFERING_UNSHARED_PEAK_ERROR_ID}
272 pop 5
273 pop 3
276 return
277
278 {clean_up_because_old_mmr_has_0_leafs}:
281 pop 5
282 pop 2
283 push 1
284 return
285
286 {assert_mmr_equality}:
289 pick 4
290 addi 1
291 pick 2
294 addi 1
295 pick 4 pick 4
298 call {popcount_u64}
299 call {assert_unchanged_peaks_equality}
302 pop 5
305 pop 1
306 push 1
307 return
308
309 {assert_unchanged_peaks_equality}:
313 dup 0
314 push 0
315 eq
316 skiz return
317 pick 2
320 addi {Digest::LEN - 1}
321 read_mem {Digest::LEN}
322 addi {Digest::LEN + 1}
323 place 7
324 pick 6
327 addi {Digest::LEN - 1}
328 read_mem {Digest::LEN}
329 addi {Digest::LEN + 1}
330 place 11
331 assert_vector error_id {Self::DIFFERING_SHARED_PEAK_ERROR_ID}
334 pop 5
335 addi -1
338 recurse
339
340 {clean_up_because_new_leafs_dont_affect_old_peaks}:
343 pop 5
344 pop 1
345 push 1
346 return
347
348 {traverse_new_tree}:
350 dup 6 dup 6
351 push 0 push 1 hint merkle_tree_root_index: u64 = stack[0..2]
352 {&DataType::U64.compare()}
353 skiz return
354 push 1
357 dup 6
358 push 1
359 and
360 skiz call {traverse_new_tree_right_sibling}
363 skiz call {merkle_step_u64}
364 recurse
367
368 {traverse_new_tree_right_sibling}:
371 pop 1
372 call {merkle_step_mem_u64}
373 pick 7
376 addi -10 place 7
378 push 0
381 return
382 }
383 }
384
385 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
386 let mut sign_offs = HashMap::new();
387 sign_offs.insert(Reviewer("ferdinand"), 0xeb1e81bd042d7a0c.into());
388 sign_offs.insert(Reviewer("alan"), 0xeb1e81bd042d7a0c.into());
389 sign_offs
390 }
391}
392
393impl VerifyMmrSuccessor {
394 pub fn update_nondeterminism(
397 nondeterminism: &mut NonDeterminism,
398 mmr_successor_proof: &MmrSuccessorProof,
399 ) {
400 let mut auth_path = mmr_successor_proof.paths.iter();
401 if let Some(&first) = auth_path.next() {
402 nondeterminism
403 .individual_tokens
404 .extend(first.reversed().values());
405 nondeterminism.digests.extend(auth_path);
406 };
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use std::collections::VecDeque;
413
414 use twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator;
415 use twenty_first::util_types::mmr::mmr_successor_proof::MmrSuccessorProof;
416
417 use super::*;
418 use crate::empty_stack;
419 use crate::test_prelude::*;
420 use crate::twenty_first::prelude::Mmr;
421
422 impl ReadOnlyAlgorithm for VerifyMmrSuccessor {
423 fn rust_shadow(
424 &self,
425 stack: &mut Vec<BFieldElement>,
426 memory: &HashMap<BFieldElement, BFieldElement>,
427 nd_tokens: VecDeque<BFieldElement>,
428 nd_digests: VecDeque<Digest>,
429 ) {
430 let new_mmr_pointer = stack.pop().unwrap();
431 let old_mmr_pointer = stack.pop().unwrap();
432
433 let new_mmr = *MmrAccumulator::decode_from_memory(memory, new_mmr_pointer).unwrap();
434 let old_mmr = *MmrAccumulator::decode_from_memory(memory, old_mmr_pointer).unwrap();
435
436 let num_new_leafs = new_mmr.num_leafs() - old_mmr.num_leafs();
438 let new_dummy_leafs = vec![Digest::default(); num_new_leafs.try_into().unwrap()];
439 let dummy_proof = MmrSuccessorProof::new_from_batch_append(&old_mmr, &new_dummy_leafs);
440 let auth_path_len = dummy_proof.paths.len();
441
442 let mut path = vec![];
444 if auth_path_len > 0 {
445 let first_element = (0..Digest::LEN).rev().map(|i| nd_tokens[i]).collect_vec();
446 path.push(Digest::new(first_element.try_into().unwrap()));
447 }
448
449 if auth_path_len > 1 {
451 path.extend((0..auth_path_len - 1).map(|i| nd_digests[i]));
452 }
453
454 assert!(MmrSuccessorProof { paths: path }.verify(&old_mmr, &new_mmr));
455 }
456
457 fn pseudorandom_initial_state(
458 &self,
459 seed: [u8; 32],
460 bench_case: Option<BenchmarkCase>,
461 ) -> ReadOnlyAlgorithmInitialState {
462 let mut rng = StdRng::from_seed(seed);
463 let old_num_leafs = match bench_case {
464 Some(BenchmarkCase::CommonCase) => u32::MAX.into(),
465 Some(BenchmarkCase::WorstCase) => u64::MAX >> 2,
466 None => rng.next_u64() >> 1,
467 };
468 let old_peaks = (0..old_num_leafs.count_ones())
469 .map(|_| rng.random())
470 .collect();
471 let old = MmrAccumulator::init(old_peaks, old_num_leafs);
472
473 let num_new_leafs = match bench_case {
474 Some(BenchmarkCase::CommonCase) => 100,
475 Some(BenchmarkCase::WorstCase) => 1000,
476 None => 1 << rng.random_range(0..5),
477 };
478 let new_leafs = (0..num_new_leafs).map(|_| rng.random()).collect_vec();
479 let proof = MmrSuccessorProof::new_from_batch_append(&old, &new_leafs);
480
481 let mut new = old.clone();
482 for leaf in new_leafs {
483 new.append(leaf);
484 }
485
486 initial_state(&old, &new, &proof)
487 }
488
489 fn corner_case_initial_states(&self) -> Vec<ReadOnlyAlgorithmInitialState> {
490 let mut rng = rand::rng();
491 let mut initial_states = vec![];
492
493 for (num_old_leafs, num_inserted_leafs) in [0_u64, 1, 2, 3, 4, 8]
494 .into_iter()
495 .cartesian_product([0, 1, 2, 3, 4, 8])
496 {
497 let old_peaks = (0..num_old_leafs.count_ones())
498 .map(|_| rng.random())
499 .collect();
500 let old = MmrAccumulator::init(old_peaks, num_old_leafs);
501
502 let mut new = old.clone();
503 let new_leafs = (0..num_inserted_leafs).map(|_| rng.random()).collect_vec();
504 for &leaf in &new_leafs {
505 new.append(leaf);
506 }
507 let proof = MmrSuccessorProof::new_from_batch_append(&old, &new_leafs);
508
509 initial_states.push(initial_state(&old, &new, &proof));
510 }
511
512 initial_states
513 }
514 }
515
516 fn initial_state(
517 old: &MmrAccumulator,
518 new: &MmrAccumulator,
519 proof: &MmrSuccessorProof,
520 ) -> ReadOnlyAlgorithmInitialState {
521 let Digest(seed) = Tip5::hash_pair(Tip5::hash(old), Tip5::hash(new));
522 let seed = seed
523 .into_iter()
524 .flat_map(|bfe| bfe.raw_bytes())
525 .take(32)
526 .collect_vec();
527 let mut rng = StdRng::from_seed(seed.try_into().unwrap());
528
529 let address_for_old = bfe!(rng.random_range(0_u32..1 << 30));
530 let address_for_new =
531 address_for_old + bfe!(old.encode().len()) + bfe!(rng.random_range(0_u32..1 << 28));
532
533 let mut nondeterminism = NonDeterminism::default();
534 VerifyMmrSuccessor::update_nondeterminism(&mut nondeterminism, proof);
535 encode_to_memory(&mut nondeterminism.ram, address_for_old, old);
536 encode_to_memory(&mut nondeterminism.ram, address_for_new, new);
537
538 let mut stack = empty_stack();
539 stack.push(address_for_old);
540 stack.push(address_for_new);
541
542 ReadOnlyAlgorithmInitialState {
543 stack,
544 nondeterminism,
545 }
546 }
547
548 fn failing_initial_states() -> Vec<ReadOnlyAlgorithmInitialState> {
549 let one_leaf = MmrAccumulator::new_from_leafs(vec![Digest::default()]);
550 let empty = MmrAccumulator::new_from_leafs(vec![]);
551 let bogus_proof = MmrSuccessorProof { paths: vec![] };
552 let mut initial_states = vec![initial_state(&one_leaf, &empty, &bogus_proof)];
553
554 let mut rng = StdRng::seed_from_u64(0x18c78fc35da66859);
555 for (old_num_leafs, new_num_leafs) in
556 [1_u64, 2, 3, 8].into_iter().cartesian_product([0, 1, 8])
557 {
558 let old_peaks = (0..old_num_leafs.count_ones())
559 .map(|_| rng.random())
560 .collect();
561 let old = MmrAccumulator::init(old_peaks, old_num_leafs);
562
563 let new_leafs = (0..new_num_leafs).map(|_| rng.random()).collect_vec();
564 let mut new_mmr = old.clone();
565 for &leaf in &new_leafs {
566 new_mmr.append(leaf);
567 }
568 let new = new_mmr;
569 let proof = MmrSuccessorProof::new_from_batch_append(&old, &new_leafs);
570
571 let wrong_old = MmrAccumulator::init(old.peaks(), old.num_leafs().rotate_left(1));
572 initial_states.push(initial_state(&wrong_old, &new, &proof));
573
574 let wrong_new = MmrAccumulator::init(new.peaks(), new.num_leafs().rotate_left(1));
575 initial_states.push(initial_state(&old, &wrong_new, &proof));
576
577 let mut wrong_new_peaks = new.peaks();
578 wrong_new_peaks.push(rng.random());
579 let too_many_peaks_new = MmrAccumulator::init(wrong_new_peaks, new.num_leafs());
580 initial_states.push(initial_state(&old, &too_many_peaks_new, &proof));
581
582 for peak_idx in 0..old.peaks().len() {
583 let mut wrong_old_peaks = old.peaks();
584 let Digest(ref mut digest_to_disturb) = wrong_old_peaks[peak_idx];
585 let digest_to_disturb_innards_idx = rng.random_range(0..Digest::LEN);
586 digest_to_disturb[digest_to_disturb_innards_idx].increment();
587
588 let wrong_old = MmrAccumulator::init(wrong_old_peaks, old.num_leafs());
589 initial_states.push(initial_state(&wrong_old, &new, &proof));
590 }
591
592 for proof_path_idx in 0..proof.paths.len() {
593 let mut wrong_proof = proof.clone();
594 let proof_paths = &mut wrong_proof.paths;
595 let Digest(ref mut digest_to_disturb) = proof_paths[proof_path_idx];
596 let digest_to_disturb_innards_idx = rng.random_range(0..Digest::LEN);
597 digest_to_disturb[digest_to_disturb_innards_idx].increment();
598
599 initial_states.push(initial_state(&old, &new, &wrong_proof));
600 }
601 }
602
603 for state in &mut initial_states {
605 let non_determinism = &mut state.nondeterminism;
606 non_determinism.individual_tokens.extend(bfe_array![0; 5]);
607 non_determinism.digests.extend([Digest::default(); 1000]);
608 }
609
610 initial_states
611 }
612
613 #[test]
614 fn unit() {
615 ShadowedReadOnlyAlgorithm::new(VerifyMmrSuccessor).test();
616 }
617
618 #[test]
619 fn verify_mmr_successor_negative_test() {
620 let all_error_ids = [
621 VerifyMmrSuccessor::OLD_HAS_MORE_LEAFS_THAN_NEW_ERROR_ID,
622 VerifyMmrSuccessor::INCONSISTENT_OLD_MMR_ERROR_ID,
623 VerifyMmrSuccessor::INCONSISTENT_NEW_MMR_ERROR_ID,
624 VerifyMmrSuccessor::DIFFERING_SHARED_PEAK_ERROR_ID,
625 VerifyMmrSuccessor::DIFFERING_UNSHARED_PEAK_ERROR_ID,
626 ];
627
628 for (i, init_state) in failing_initial_states().into_iter().enumerate() {
629 dbg!(i);
630 test_assertion_failure(
631 &ShadowedReadOnlyAlgorithm::new(VerifyMmrSuccessor),
632 init_state.into(),
633 &all_error_ids,
634 );
635 }
636 }
637}
638
639#[cfg(test)]
640mod bench {
641 use super::*;
642 use crate::test_prelude::*;
643
644 #[test]
645 fn benchmark() {
646 ShadowedReadOnlyAlgorithm::new(VerifyMmrSuccessor).bench();
647 }
648}