1use triton_vm::prelude::*;
2
3use super::leaf_index_to_mt_index_and_peak_index::MmrLeafIndexToMtIndexAndPeakIndex;
4use crate::hashing::merkle_step_u64_index::MerkleStepU64Index;
5use crate::list::get::Get;
6use crate::prelude::*;
7
8#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
11pub struct MmrVerifyFromSecretInSecretLeafIndex;
12
13impl BasicSnippet for MmrVerifyFromSecretInSecretLeafIndex {
14 fn inputs(&self) -> Vec<(DataType, String)> {
15 vec![(
16 DataType::Tuple(vec![
17 DataType::List(Box::new(DataType::Digest)), DataType::U64, DataType::Digest, ]),
21 "*peaks_leaf_count_and_leaf".to_owned(),
22 )]
23 }
24
25 fn outputs(&self) -> Vec<(DataType, String)> {
26 vec![]
27 }
28
29 fn entrypoint(&self) -> String {
30 "tasmlib_mmr_verify_from_secret_in_secret_leaf_index".into()
31 }
32
33 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
36 let entrypoint = self.entrypoint();
37 let while_loop_label = format!("{entrypoint}_while");
38
39 let leaf_index_to_mt_index = library.import(Box::new(MmrLeafIndexToMtIndexAndPeakIndex));
40 let merkle_step_u64_index = library.import(Box::new(MerkleStepU64Index));
41 let list_get = library.import(Box::new(Get::new(DataType::Digest)));
42
43 triton_asm!(
46 {entrypoint}:
47 divine 2
49 swap 8 swap 1 swap 9 swap 7
51 dup 9 dup 9
54 call {leaf_index_to_mt_index}
57 swap 7 swap 4 swap 1 swap 5 swap 2 swap 6 swap 3
60 call {while_loop_label}
69 dup 8 dup 8 call {list_get}
73 assert_vector error_id 20
76 pop 5
79 pop 5
80 pop 1
81 return
84
85 {while_loop_label}:
87 dup 6 dup 6 push 0 push 1 {&DataType::U64.compare()}
88 skiz return
91 call {merkle_step_u64_index}
95
96 recurse
99 )
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use num::One;
106 use tasm_lib::test_helpers::test_assertion_failure;
107 use twenty_first::math::other::random_elements;
108 use twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator;
109 use twenty_first::util_types::mmr::mmr_accumulator::util::mmra_with_mps;
110 use twenty_first::util_types::mmr::mmr_membership_proof::MmrMembershipProof;
111 use twenty_first::util_types::mmr::mmr_trait::Mmr;
112 use twenty_first::util_types::mmr::shared_basic::leaf_index_to_mt_index_and_peak_index;
113
114 use super::*;
115 use crate::rust_shadowing_helper_functions;
116 use crate::test_helpers::test_rust_equivalence_given_complete_state;
117 use crate::test_prelude::*;
118
119 #[test]
120 fn prop() {
121 for _ in 0..10 {
122 ShadowedProcedure::new(MmrVerifyFromSecretInSecretLeafIndex).test();
123 }
124 }
125
126 #[test]
127 fn mmra_ap_verify_test_one() {
128 let digest0 = Tip5::hash(&BFieldElement::new(4545));
129 let (mmra, _mps) = mmra_with_mps(1u64, vec![(0, digest0)]);
130 MmrVerifyFromSecretInSecretLeafIndex.prop_verify_from_secret_in_positive_test(
131 &mmra,
132 digest0,
133 0u64,
134 vec![],
135 );
136 }
137
138 #[test]
139 fn mmra_ap_verify_test_two() {
140 let digest0 = Tip5::hash(&BFieldElement::new(123));
141 let digest1 = Tip5::hash(&BFieldElement::new(456));
142
143 let leaf_count = 2u64;
144 let (mmr, _mps) = mmra_with_mps(leaf_count, vec![(0u64, digest0), (1u64, digest1)]);
145
146 let leaf_index_0 = 0;
147 MmrVerifyFromSecretInSecretLeafIndex.prop_verify_from_secret_in_positive_test(
148 &mmr,
149 digest0,
150 leaf_index_0,
151 vec![digest1],
152 );
153
154 let leaf_index_1 = 1;
155 MmrVerifyFromSecretInSecretLeafIndex.prop_verify_from_secret_in_positive_test(
156 &mmr,
157 digest1,
158 leaf_index_1,
159 vec![digest0],
160 );
161 }
162
163 #[test]
164 fn mmra_ap_verify_test_pbt() {
165 let max_size = 19;
166 let snippet = MmrVerifyFromSecretInSecretLeafIndex;
167
168 for leaf_count in 0..max_size {
169 let digests: Vec<Digest> = random_elements(leaf_count);
170
171 let (mmr, mps) = mmra_with_mps(
172 leaf_count as u64,
173 digests
174 .iter()
175 .cloned()
176 .enumerate()
177 .map(|(i, d)| (i as u64, d))
178 .collect_vec(),
179 );
180
181 let bad_leaf: Digest = rand::rng().random();
182 for (leaf_index, leaf_digest) in digests.into_iter().enumerate() {
183 let auth_path = mps[leaf_index].clone();
184
185 snippet.prop_verify_from_secret_in_positive_test(
187 &mmr,
188 leaf_digest,
189 leaf_index as u64,
190 auth_path.authentication_path.clone(),
191 );
192
193 snippet.prop_verify_from_secret_in_negative_test(
195 &mmr,
196 bad_leaf,
197 leaf_index as u64,
198 auth_path.authentication_path,
199 );
200 }
201 }
202 }
203
204 #[test]
205 fn mmra_ap_verify_many_leafs() {
206 for init_leaf_count in [
207 (1u64 << 40) + (1 << 21) + 510,
208 (1 << 32) - 1,
209 (1 << 61) - 3,
210 (1 << 61) - 2,
211 (1 << 61) - 1,
212 ] {
213 let init_peak_count = init_leaf_count.count_ones();
214
215 let fake_peaks: Vec<Digest> = random_elements(init_peak_count as usize);
218 let mut mmr: MmrAccumulator = MmrAccumulator::init(fake_peaks, init_leaf_count);
219
220 let second_to_last_leaf: Digest = rand::rng().random();
222 let second_to_last_leaf_index = init_leaf_count;
223 let mut real_membership_proof_second_to_last = mmr.append(second_to_last_leaf);
224
225 let last_leaf: Digest = rand::rng().random();
227 let last_leaf_index = second_to_last_leaf_index + 1;
228 MmrMembershipProof::update_from_append(
229 &mut real_membership_proof_second_to_last,
230 second_to_last_leaf_index,
231 mmr.num_leafs(),
232 last_leaf,
233 &mmr.peaks(),
234 );
235 let real_membership_proof_last = mmr.append(last_leaf);
236
237 MmrVerifyFromSecretInSecretLeafIndex.prop_verify_from_secret_in_positive_test(
239 &mmr,
240 second_to_last_leaf,
241 second_to_last_leaf_index,
242 real_membership_proof_second_to_last
243 .authentication_path
244 .clone(),
245 );
246 MmrVerifyFromSecretInSecretLeafIndex.prop_verify_from_secret_in_positive_test(
247 &mmr,
248 last_leaf,
249 last_leaf_index,
250 real_membership_proof_last.authentication_path.clone(),
251 );
252
253 let bad_leaf: Digest = rand::rng().random();
255 MmrVerifyFromSecretInSecretLeafIndex.prop_verify_from_secret_in_negative_test(
256 &mmr,
257 bad_leaf,
258 second_to_last_leaf_index,
259 real_membership_proof_second_to_last.authentication_path,
260 );
261 MmrVerifyFromSecretInSecretLeafIndex.prop_verify_from_secret_in_negative_test(
262 &mmr,
263 bad_leaf,
264 last_leaf_index,
265 real_membership_proof_last.authentication_path,
266 );
267 }
268 }
269
270 impl Procedure for MmrVerifyFromSecretInSecretLeafIndex {
271 fn rust_shadow(
272 &self,
273 stack: &mut Vec<BFieldElement>,
274 memory: &mut HashMap<BFieldElement, BFieldElement>,
275 nondeterminism: &NonDeterminism,
276 _public_input: &[BFieldElement],
277 _sponge: &mut Option<Tip5>,
278 ) -> Vec<BFieldElement> {
279 let mut leaf_digest = [BFieldElement::new(0); Digest::LEN];
280 for elem in leaf_digest.iter_mut() {
281 *elem = stack.pop().unwrap();
282 }
283
284 let leaf_digest = Digest::new(leaf_digest);
285 let leaf_count_lo: u32 = stack.pop().unwrap().try_into().unwrap();
286 let leaf_count_hi: u32 = stack.pop().unwrap().try_into().unwrap();
287 let leaf_count: u64 = ((leaf_count_hi as u64) << 32) + leaf_count_lo as u64;
288 let peaks_pointer = stack.pop().unwrap();
289 let peaks_count: u64 = memory[&peaks_pointer].value();
290 let mut peaks: Vec<Digest> = vec![];
291 for i in 0..peaks_count {
292 let digest_innards = rust_shadowing_helper_functions::list::list_get(
293 peaks_pointer,
294 i as usize,
295 memory,
296 Digest::LEN,
297 );
298 peaks.push(Digest::new(digest_innards.try_into().unwrap()));
299 }
300 let leaf_index_hi: u32 = nondeterminism.individual_tokens[0]
301 .value()
302 .try_into()
303 .unwrap();
304 let leaf_index_lo: u32 = nondeterminism.individual_tokens[1]
305 .value()
306 .try_into()
307 .unwrap();
308 let leaf_index: u64 = ((leaf_index_hi as u64) << 32) + leaf_index_lo as u64;
309 let (mut mt_index, _peak_index) =
310 leaf_index_to_mt_index_and_peak_index(leaf_index, leaf_count);
311
312 let mut auth_path: Vec<Digest> = vec![];
313 let mut i = 0;
314 while mt_index != 1 {
315 auth_path.push(nondeterminism.digests[i]);
316 mt_index /= 2;
317 i += 1;
318 }
319
320 let valid_mp = MmrMembershipProof::new(auth_path).verify(
321 leaf_index,
322 leaf_digest,
323 &peaks,
324 leaf_count,
325 );
326
327 assert!(valid_mp, "MMR leaf not authenticated");
328
329 vec![]
330 }
331
332 fn pseudorandom_initial_state(
333 &self,
334 seed: [u8; 32],
335 bench_case: Option<BenchmarkCase>,
336 ) -> ProcedureInitialState {
337 let mut rng = StdRng::from_seed(seed);
338 let leaf_count = rng.random_range(1..10000);
339 let leaf_index = rng.random_range(0..leaf_count);
340
341 match bench_case {
342 Some(BenchmarkCase::CommonCase) => {
343 self.prepare_state_for_benchmark(32, (1 << 32) - 1)
344 }
345 Some(BenchmarkCase::WorstCase) => {
346 self.prepare_state_for_benchmark(62, (1 << 62) - 1)
347 }
348 None => self.prepare_state_for_tests(leaf_count, leaf_index as u64, true),
349 }
350 }
351 }
352
353 impl MmrVerifyFromSecretInSecretLeafIndex {
354 fn prepare_state(
355 &self,
356 mmr: &MmrAccumulator,
357 claimed_leaf: Digest,
358 leaf_index: u64,
359 auth_path: Vec<Digest>,
360 ) -> ProcedureInitialState {
361 let ProcedureInitialState {
362 mut stack,
363 mut nondeterminism,
364 public_input: _,
365 sponge: _,
366 } = self.mmr_to_init_vm_state(mmr);
367
368 for value in claimed_leaf.values().iter().rev() {
370 stack.push(*value);
371 }
372
373 let leaf_index_hi = BFieldElement::new(leaf_index >> 32);
375 let leaf_index_lo = BFieldElement::new(leaf_index & u32::MAX as u64);
376 nondeterminism.individual_tokens = vec![leaf_index_hi, leaf_index_lo];
377 nondeterminism.digests = auth_path;
378
379 ProcedureInitialState {
380 stack,
381 nondeterminism,
382 ..Default::default()
383 }
384 }
385
386 fn prop_verify_from_secret_in_negative_test(
387 &self,
388 mmr: &MmrAccumulator,
389 claimed_leaf: Digest,
390 leaf_index: u64,
391 auth_path: Vec<Digest>,
392 ) {
393 let init_state = self.prepare_state(mmr, claimed_leaf, leaf_index, auth_path.clone());
394
395 test_assertion_failure(
396 &ShadowedProcedure::new(MmrVerifyFromSecretInSecretLeafIndex),
397 init_state.into(),
398 &[20],
399 );
400
401 assert!(!MmrMembershipProof::new(auth_path).verify(
403 leaf_index,
404 claimed_leaf,
405 &mmr.peaks(),
406 mmr.num_leafs()
407 ));
408 }
409
410 fn prop_verify_from_secret_in_positive_test(
413 &self,
414 mmr: &MmrAccumulator,
415 claimed_leaf: Digest,
416 leaf_index: u64,
417 auth_path: Vec<Digest>,
418 ) {
419 let init_state = self.prepare_state(mmr, claimed_leaf, leaf_index, auth_path.clone());
420 let expected_final_stack = self.init_stack_for_isolated_run();
421 test_rust_equivalence_given_complete_state(
422 &ShadowedProcedure::new(MmrVerifyFromSecretInSecretLeafIndex),
423 &init_state.stack,
424 &[],
425 &init_state.nondeterminism,
426 &None,
427 Some(&expected_final_stack),
428 );
429
430 assert!(MmrMembershipProof::new(auth_path).verify(
432 leaf_index,
433 claimed_leaf,
434 &mmr.peaks(),
435 mmr.num_leafs()
436 ));
437 }
438
439 fn prepare_state_for_tests(
442 &self,
443 size: usize,
444 leaf_index: u64,
445 generate_valid_proof: bool,
446 ) -> ProcedureInitialState {
447 let valid_leaf: Digest = rand::random();
448 let (mmr, mps) = mmra_with_mps(size as u64, vec![(leaf_index, valid_leaf)]);
449 let claimed_leaf = if generate_valid_proof {
450 valid_leaf
451 } else {
452 rand::random()
453 };
454
455 self.prepare_state(
456 &mmr,
457 claimed_leaf,
458 leaf_index,
459 mps[0].authentication_path.clone(),
460 )
461 }
462
463 fn prepare_state_for_benchmark(
464 &self,
465 log_2_leaf_count: u8,
466 leaf_index: u64,
467 ) -> ProcedureInitialState {
468 let leaf_count = 2u64.pow(log_2_leaf_count as u32);
469 let peaks: Vec<Digest> = random_elements(log_2_leaf_count as usize);
470 let mut mmra = MmrAccumulator::init(peaks, leaf_count - 1);
471 let new_leaf: Digest = rand::random();
472 let authentication_path = mmra.append(new_leaf).authentication_path;
473
474 let mut vm_init_state = self.mmr_to_init_vm_state(&mmra);
475
476 vm_init_state
478 .nondeterminism
479 .individual_tokens
480 .push(BFieldElement::new(leaf_index >> 32));
481 vm_init_state
482 .nondeterminism
483 .individual_tokens
484 .push(BFieldElement::new(leaf_index & u32::MAX as u64));
485
486 vm_init_state.nondeterminism.digests = authentication_path;
488
489 for value in new_leaf.values().iter().rev() {
490 vm_init_state.stack.push(*value);
491 }
492
493 vm_init_state
494 }
495
496 fn mmr_to_init_vm_state(&self, mmra: &MmrAccumulator) -> ProcedureInitialState {
500 let mut stack: Vec<BFieldElement> = self.init_stack_for_isolated_run();
501 let peaks_pointer = BFieldElement::one();
502 stack.push(peaks_pointer);
503
504 let leaf_count = mmra.num_leafs();
505 let leaf_count_hi = BFieldElement::new(leaf_count >> 32);
506 let leaf_count_lo = BFieldElement::new(leaf_count & u32::MAX as u64);
507 stack.push(leaf_count_hi);
508 stack.push(leaf_count_lo);
509
510 let mut memory: HashMap<BFieldElement, BFieldElement> = HashMap::default();
512 rust_shadowing_helper_functions::list::list_new(peaks_pointer, &mut memory);
513
514 for peak in mmra.peaks() {
515 rust_shadowing_helper_functions::list::list_push(
516 peaks_pointer,
517 peak.values().to_vec(),
518 &mut memory,
519 );
520 }
521
522 let nondeterminism = NonDeterminism::default().with_ram(memory);
523 ProcedureInitialState {
524 stack,
525 nondeterminism,
526 ..Default::default()
527 }
528 }
529 }
530}
531
532#[cfg(test)]
533mod benches {
534 use super::*;
535 use crate::test_prelude::*;
536
537 #[test]
538 fn benchmark() {
539 ShadowedProcedure::new(MmrVerifyFromSecretInSecretLeafIndex).bench();
540 }
541}