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 inputs(&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 outputs(&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"), 0xc35bd96dd2348c49.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 ) {
178 let auth_path_pointer = stack.pop().unwrap();
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().unwrap();
183
184 let auth_path = *Vec::<Digest>::decode_from_memory(memory, auth_path_pointer).unwrap();
185 let peaks = *Vec::<Digest>::decode_from_memory(memory, peaks_pointer).unwrap();
186
187 let proof = MmrMembershipProof::new(auth_path);
188 let is_valid = proof.verify(leaf_index, leaf, &peaks, leaf_count);
189 push_encodable(stack, &is_valid);
190 }
191
192 fn pseudorandom_initial_state(
193 &self,
194 seed: [u8; 32],
195 bench_case: Option<BenchmarkCase>,
196 ) -> FunctionInitialState {
197 let mut rng = StdRng::from_seed(seed);
198 let (leaf_index, leaf_count) = match bench_case {
199 Some(BenchmarkCase::CommonCase) => ((1 << 31) - 1, 1 << 31),
200 Some(BenchmarkCase::WorstCase) => ((1 << 62) - 1, 1 << 62),
201 None => {
202 let leaf_count = rng.random_range(1..=1 << 62);
203 let leaf_index = rng.random_range(0..leaf_count);
204 (leaf_index, leaf_count)
205 }
206 };
207
208 let leaf = rng.random();
209 let (mmra, mps) = mmra_with_mps(leaf_count, vec![(leaf_index, leaf)]);
210 let auth_path = mps[0].authentication_path.clone();
211
212 self.set_up_initial_state(&mmra, leaf, leaf_index, auth_path)
213 }
214 }
215
216 #[test]
217 fn rust_shadow() {
218 ShadowedFunction::new(MmrVerifyFromMemory).test();
219 }
220
221 #[test]
223 #[should_panic]
224 fn mmra_ap_verify_test_empty() {
225 let digest0 = Tip5::hash(&BFieldElement::new(4545));
226 let mmr = MmrAccumulator::new_from_leafs(vec![]);
227 let leaf_index = 0;
228 prop_verify_from_memory(&mmr, digest0, leaf_index, vec![], false);
229 }
230
231 #[test]
232 fn mmra_ap_verify_test_one() {
233 let digest0 = Tip5::hash(&BFieldElement::new(4545));
234 let mut mmr = MmrAccumulator::new_from_leafs(vec![]);
235 mmr.append(digest0);
236 let leaf_index = 0;
237 prop_verify_from_memory(&mmr, digest0, leaf_index, vec![], true);
238 }
239
240 #[test]
241 fn mmra_ap_verify_test_two() {
242 let digest0 = Tip5::hash(&BFieldElement::new(123));
243 let digest1 = Tip5::hash(&BFieldElement::new(456));
244
245 let mut mmr = MmrAccumulator::new_from_leafs(vec![]);
246 mmr.append(digest0);
247 mmr.append(digest1);
248
249 let leaf_index_0 = 0;
250 prop_verify_from_memory(&mmr, digest0, leaf_index_0, vec![digest1], true);
251
252 let leaf_index_1 = 1;
253 prop_verify_from_memory(&mmr, digest1, leaf_index_1, vec![digest0], true);
254 }
255
256 #[test]
257 fn mmra_ap_verify_test_pbt() {
258 let max_size = 19;
259
260 for leaf_count in 0..max_size {
261 let digests: Vec<Digest> = random_elements(leaf_count);
262
263 let (mmr, mps) = mmra_with_mps(
264 leaf_count as u64,
265 digests
266 .iter()
267 .cloned()
268 .enumerate()
269 .map(|(i, d)| (i as u64, d))
270 .collect_vec(),
271 );
272
273 let bad_leaf: Digest = rand::rng().random();
274 for (leaf_index, leaf_digest) in digests.into_iter().enumerate() {
275 let auth_path = mps[leaf_index].clone();
276
277 prop_verify_from_memory(
279 &mmr,
280 leaf_digest,
281 leaf_index as u64,
282 auth_path.authentication_path.clone(),
283 true,
284 );
285
286 let bad_index = (leaf_index + 1) % leaf_count;
288 if bad_index != leaf_index {
289 prop_verify_from_memory(
290 &mmr,
291 leaf_digest,
292 bad_index as u64,
293 auth_path.authentication_path.clone(),
294 false,
295 );
296 }
297 prop_verify_from_memory(
298 &mmr,
299 bad_leaf,
300 leaf_index as u64,
301 auth_path.authentication_path,
302 false,
303 );
304 }
305 }
306 }
307
308 #[test]
309 fn mmra_ap_verify_many_leafs() {
310 for init_leaf_count in [
311 (1u64 << 40) + (1 << 21) + 510,
312 (1 << 32) - 1,
313 (1 << 61) - 3,
314 (1 << 61) - 2,
315 (1 << 61) - 1,
316 1 << 61,
317 ] {
318 let init_peak_count = init_leaf_count.count_ones();
320 println!("init_peak_count = {init_peak_count}");
321
322 let fake_peaks: Vec<Digest> = random_elements(init_peak_count as usize);
325 let mut mmr = MmrAccumulator::init(fake_peaks, init_leaf_count);
326
327 let second_to_last_leaf: Digest = rand::rng().random();
329 let second_to_last_leaf_index = init_leaf_count;
330 let mut real_membership_proof_second_to_last = mmr.append(second_to_last_leaf);
331
332 let last_leaf: Digest = rand::rng().random();
334 let last_leaf_index = second_to_last_leaf_index + 1;
335 MmrMembershipProof::update_from_append(
336 &mut real_membership_proof_second_to_last,
337 second_to_last_leaf_index,
338 mmr.num_leafs(),
339 last_leaf,
340 &mmr.peaks(),
341 );
342 let real_membership_proof_last = mmr.append(last_leaf);
343
344 prop_verify_from_memory(
346 &mmr,
347 second_to_last_leaf,
348 second_to_last_leaf_index,
349 real_membership_proof_second_to_last
350 .authentication_path
351 .clone(),
352 true,
353 );
354 prop_verify_from_memory(
355 &mmr,
356 last_leaf,
357 last_leaf_index,
358 real_membership_proof_last.authentication_path.clone(),
359 true,
360 );
361
362 let bad_leaf: Digest = rand::rng().random();
364 prop_verify_from_memory(
365 &mmr,
366 bad_leaf,
367 second_to_last_leaf_index,
368 real_membership_proof_second_to_last.authentication_path,
369 false,
370 );
371 prop_verify_from_memory(
372 &mmr,
373 bad_leaf,
374 last_leaf_index,
375 real_membership_proof_last.authentication_path,
376 false,
377 );
378 }
379 }
380
381 fn prop_verify_from_memory(
382 mmr: &MmrAccumulator,
383 leaf: Digest,
384 leaf_index: u64,
385 auth_path: Vec<Digest>,
386 expect_validation_success: bool,
387 ) {
388 let exec_state =
389 MmrVerifyFromMemory.set_up_initial_state(mmr, leaf, leaf_index, auth_path.clone());
390
391 let mut expected_final_stack = empty_stack();
393 expected_final_stack.push(bfe!(expect_validation_success as u64));
394
395 test_rust_equivalence_given_complete_state(
396 &ShadowedFunction::new(MmrVerifyFromMemory),
397 &exec_state.stack,
398 &[],
399 &NonDeterminism::default().with_ram(exec_state.memory),
400 &None,
401 Some(&expected_final_stack),
402 );
403
404 assert_eq!(
406 expect_validation_success,
407 MmrMembershipProof::new(auth_path).verify(
408 leaf_index,
409 leaf,
410 &mmr.peaks(),
411 mmr.num_leafs(),
412 )
413 );
414 }
415}
416
417#[cfg(test)]
418mod benches {
419 use super::*;
420 use crate::test_prelude::*;
421
422 #[test]
423 fn benchmark() {
424 ShadowedFunction::new(MmrVerifyFromMemory).bench();
425 }
426}