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 parameters(&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 return_values(&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 #[macro_rules_attr::apply(test)]
120 fn prop() {
121 for _ in 0..10 {
122 ShadowedProcedure::new(MmrVerifyFromSecretInSecretLeafIndex).test();
123 }
124 }
125
126 #[macro_rules_attr::apply(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 #[macro_rules_attr::apply(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 #[macro_rules_attr::apply(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 #[macro_rules_attr::apply(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 ) -> Result<Vec<BFieldElement>, RustShadowError> {
279 let mut leaf_digest = [BFieldElement::new(0); Digest::LEN];
280 for elem in leaf_digest.iter_mut() {
281 *elem = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
282 }
283
284 let leaf_digest = Digest::new(leaf_digest);
285 let leaf_count_lo: u32 = stack
286 .pop()
287 .ok_or(RustShadowError::StackUnderflow)?
288 .try_into()
289 .map_err(|_| RustShadowError::U64ToU32Error)?;
290 let leaf_count_hi: u32 = stack
291 .pop()
292 .ok_or(RustShadowError::StackUnderflow)?
293 .try_into()
294 .map_err(|_| RustShadowError::U64ToU32Error)?;
295 let leaf_count: u64 = ((leaf_count_hi as u64) << 32) + leaf_count_lo as u64;
296 let peaks_pointer = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
297 let peaks_count: u64 = memory[&peaks_pointer].value();
298 let mut peaks: Vec<Digest> = vec![];
299 for i in 0..peaks_count {
300 let digest_innards = rust_shadowing_helper_functions::list::list_get(
301 peaks_pointer,
302 i as usize,
303 memory,
304 Digest::LEN,
305 )?;
306 peaks.push(Digest::new(digest_innards.try_into().unwrap()));
307 }
308 let leaf_index_hi: u32 = nondeterminism.individual_tokens[0]
309 .value()
310 .try_into()
311 .map_err(|_| RustShadowError::U64ToU32Error)?;
312 let leaf_index_lo: u32 = nondeterminism.individual_tokens[1]
313 .value()
314 .try_into()
315 .map_err(|_| RustShadowError::U64ToU32Error)?;
316 let leaf_index: u64 = ((leaf_index_hi as u64) << 32) + leaf_index_lo as u64;
317 let (mut mt_index, _peak_index) =
318 leaf_index_to_mt_index_and_peak_index(leaf_index, leaf_count);
319
320 let mut auth_path: Vec<Digest> = vec![];
321 let mut i = 0;
322 while mt_index != 1 {
323 auth_path.push(nondeterminism.digests[i]);
324 mt_index /= 2;
325 i += 1;
326 }
327
328 let valid_mp = MmrMembershipProof::new(auth_path).verify(
329 leaf_index,
330 leaf_digest,
331 &peaks,
332 leaf_count,
333 );
334
335 valid_mp.then(Vec::new).ok_or(RustShadowError::InvalidProof)
336 }
337
338 fn pseudorandom_initial_state(
339 &self,
340 seed: [u8; 32],
341 bench_case: Option<BenchmarkCase>,
342 ) -> ProcedureInitialState {
343 let mut rng = StdRng::from_seed(seed);
344 let leaf_count = rng.random_range(1..10000);
345 let leaf_index = rng.random_range(0..leaf_count);
346
347 match bench_case {
348 Some(BenchmarkCase::CommonCase) => {
349 self.prepare_state_for_benchmark(32, (1 << 32) - 1)
350 }
351 Some(BenchmarkCase::WorstCase) => {
352 self.prepare_state_for_benchmark(62, (1 << 62) - 1)
353 }
354 None => self.prepare_state_for_tests(leaf_count, leaf_index as u64, true),
355 }
356 }
357 }
358
359 impl MmrVerifyFromSecretInSecretLeafIndex {
360 fn prepare_state(
361 &self,
362 mmr: &MmrAccumulator,
363 claimed_leaf: Digest,
364 leaf_index: u64,
365 auth_path: Vec<Digest>,
366 ) -> ProcedureInitialState {
367 let ProcedureInitialState {
368 mut stack,
369 mut nondeterminism,
370 public_input: _,
371 sponge: _,
372 } = self.mmr_to_init_vm_state(mmr);
373
374 for value in claimed_leaf.values().iter().rev() {
376 stack.push(*value);
377 }
378
379 let leaf_index_hi = BFieldElement::new(leaf_index >> 32);
381 let leaf_index_lo = BFieldElement::new(leaf_index & u32::MAX as u64);
382 nondeterminism.individual_tokens = vec![leaf_index_hi, leaf_index_lo];
383 nondeterminism.digests = auth_path;
384
385 ProcedureInitialState {
386 stack,
387 nondeterminism,
388 ..Default::default()
389 }
390 }
391
392 fn prop_verify_from_secret_in_negative_test(
393 &self,
394 mmr: &MmrAccumulator,
395 claimed_leaf: Digest,
396 leaf_index: u64,
397 auth_path: Vec<Digest>,
398 ) {
399 let init_state = self.prepare_state(mmr, claimed_leaf, leaf_index, auth_path.clone());
400
401 test_assertion_failure(
402 &ShadowedProcedure::new(MmrVerifyFromSecretInSecretLeafIndex),
403 init_state.into(),
404 &[20],
405 );
406
407 assert!(!MmrMembershipProof::new(auth_path).verify(
409 leaf_index,
410 claimed_leaf,
411 &mmr.peaks(),
412 mmr.num_leafs()
413 ));
414 }
415
416 fn prop_verify_from_secret_in_positive_test(
419 &self,
420 mmr: &MmrAccumulator,
421 claimed_leaf: Digest,
422 leaf_index: u64,
423 auth_path: Vec<Digest>,
424 ) {
425 let init_state = self.prepare_state(mmr, claimed_leaf, leaf_index, auth_path.clone());
426 let expected_final_stack = self.init_stack_for_isolated_run();
427 test_rust_equivalence_given_complete_state(
428 &ShadowedProcedure::new(MmrVerifyFromSecretInSecretLeafIndex),
429 &init_state.stack,
430 &[],
431 &init_state.nondeterminism,
432 &None,
433 Some(&expected_final_stack),
434 );
435
436 assert!(MmrMembershipProof::new(auth_path).verify(
438 leaf_index,
439 claimed_leaf,
440 &mmr.peaks(),
441 mmr.num_leafs()
442 ));
443 }
444
445 fn prepare_state_for_tests(
448 &self,
449 size: usize,
450 leaf_index: u64,
451 generate_valid_proof: bool,
452 ) -> ProcedureInitialState {
453 let valid_leaf: Digest = rand::random();
454 let (mmr, mps) = mmra_with_mps(size as u64, vec![(leaf_index, valid_leaf)]);
455 let claimed_leaf = if generate_valid_proof {
456 valid_leaf
457 } else {
458 rand::random()
459 };
460
461 self.prepare_state(
462 &mmr,
463 claimed_leaf,
464 leaf_index,
465 mps[0].authentication_path.clone(),
466 )
467 }
468
469 fn prepare_state_for_benchmark(
470 &self,
471 log_2_leaf_count: u8,
472 leaf_index: u64,
473 ) -> ProcedureInitialState {
474 let leaf_count = 2u64.pow(log_2_leaf_count as u32);
475 let peaks: Vec<Digest> = random_elements(log_2_leaf_count as usize);
476 let mut mmra = MmrAccumulator::init(peaks, leaf_count - 1);
477 let new_leaf: Digest = rand::random();
478 let authentication_path = mmra.append(new_leaf).authentication_path;
479
480 let mut vm_init_state = self.mmr_to_init_vm_state(&mmra);
481
482 vm_init_state
484 .nondeterminism
485 .individual_tokens
486 .push(BFieldElement::new(leaf_index >> 32));
487 vm_init_state
488 .nondeterminism
489 .individual_tokens
490 .push(BFieldElement::new(leaf_index & u32::MAX as u64));
491
492 vm_init_state.nondeterminism.digests = authentication_path;
494
495 for value in new_leaf.values().iter().rev() {
496 vm_init_state.stack.push(*value);
497 }
498
499 vm_init_state
500 }
501
502 fn mmr_to_init_vm_state(&self, mmra: &MmrAccumulator) -> ProcedureInitialState {
506 let mut stack: Vec<BFieldElement> = self.init_stack_for_isolated_run();
507 let peaks_pointer = BFieldElement::one();
508 stack.push(peaks_pointer);
509
510 let leaf_count = mmra.num_leafs();
511 let leaf_count_hi = BFieldElement::new(leaf_count >> 32);
512 let leaf_count_lo = BFieldElement::new(leaf_count & u32::MAX as u64);
513 stack.push(leaf_count_hi);
514 stack.push(leaf_count_lo);
515
516 let mut memory: HashMap<BFieldElement, BFieldElement> = HashMap::default();
518 rust_shadowing_helper_functions::list::list_new(peaks_pointer, &mut memory);
519
520 for peak in mmra.peaks() {
521 rust_shadowing_helper_functions::list::list_push(
522 peaks_pointer,
523 peak.values().to_vec(),
524 &mut memory,
525 )
526 .unwrap();
527 }
528
529 let nondeterminism = NonDeterminism::default().with_ram(memory);
530 ProcedureInitialState {
531 stack,
532 nondeterminism,
533 ..Default::default()
534 }
535 }
536 }
537}
538
539#[cfg(test)]
540mod benches {
541 use super::*;
542 use crate::test_prelude::*;
543
544 #[macro_rules_attr::apply(test)]
545 fn benchmark() {
546 ShadowedProcedure::new(MmrVerifyFromSecretInSecretLeafIndex).bench();
547 }
548}