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 MmrVerifyFromSecretInLeafIndexOnStack;
12
13impl BasicSnippet for MmrVerifyFromSecretInLeafIndexOnStack {
14 fn inputs(&self) -> Vec<(DataType, String)> {
15 vec![(
16 DataType::Tuple(vec![
17 DataType::List(Box::new(DataType::Digest)), DataType::Digest, DataType::U64, DataType::U64, ]),
22 "peaks_leaf_count_leaf_index_and_leaf".to_owned(),
23 )]
24 }
25
26 fn outputs(&self) -> Vec<(DataType, String)> {
27 vec![]
28 }
29
30 fn entrypoint(&self) -> String {
31 "tasmlib_mmr_verify_from_secret_in_leaf_index_on_stack".into()
32 }
33
34 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
35 let entrypoint = self.entrypoint();
36 let auth_path_loop_label = format!("{entrypoint}_auth_path_loop");
37
38 let leaf_index_to_mt_index = library.import(Box::new(MmrLeafIndexToMtIndexAndPeakIndex));
39 let merkle_step_u64_index = library.import(Box::new(MerkleStepU64Index));
40 let list_get = library.import(Box::new(Get::new(DataType::Digest)));
41
42 let auth_path_loop_code = triton_asm!(
43 {auth_path_loop_label}:
44 dup 6 dup 6 push 0 push 1 {&DataType::U64.compare()}
45 skiz return
48 call {merkle_step_u64_index}
52
53 recurse
56 );
57
58 triton_asm!(
59 {entrypoint}:
60 call {leaf_index_to_mt_index}
63 place 7 place 6 place 6
66 call {auth_path_loop_label}
69 dup 8 dup 8 call {list_get}
72 assert_vector error_id 10
75 pop 5
78 pop 4
79 return
83
84 {&auth_path_loop_code}
85 )
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use twenty_first::math::other::random_elements;
92 use twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator;
93 use twenty_first::util_types::mmr::mmr_accumulator::util::mmra_with_mps;
94 use twenty_first::util_types::mmr::mmr_membership_proof::MmrMembershipProof;
95 use twenty_first::util_types::mmr::mmr_trait::Mmr;
96 use twenty_first::util_types::mmr::shared_basic::leaf_index_to_mt_index_and_peak_index;
97
98 use super::*;
99 use crate::rust_shadowing_helper_functions;
100 use crate::test_prelude::*;
101
102 impl MmrVerifyFromSecretInLeafIndexOnStack {
103 fn prepare_state(
104 &self,
105 mmr: &MmrAccumulator,
106 peaks_pointer: BFieldElement,
107 claimed_leaf_index: u64,
108 claimed_leaf: Digest,
109 auth_path: Vec<Digest>,
110 ) -> ProcedureInitialState {
111 let mut init_state = self.mmr_to_init_vm_state(mmr, peaks_pointer, claimed_leaf);
112 init_state.nondeterminism.digests = auth_path;
113 let leaf_index_encoded = [
114 bfe!(claimed_leaf_index >> 32),
115 bfe!(claimed_leaf_index & u32::MAX as u64),
116 ];
117 init_state.stack.extend(leaf_index_encoded);
118
119 init_state
120 }
121
122 fn mmr_to_init_vm_state(
125 &self,
126 mmra: &MmrAccumulator,
127 peaks_pointer: BFieldElement,
128 claimed_leaf: Digest,
129 ) -> ProcedureInitialState {
130 let mut stack: Vec<BFieldElement> = self.init_stack_for_isolated_run();
131 stack.push(peaks_pointer);
132
133 for word in claimed_leaf.0.into_iter().rev() {
134 stack.push(word);
135 }
136
137 let leaf_count = mmra.num_leafs();
138 let leaf_count_hi = BFieldElement::new(leaf_count >> 32);
139 let leaf_count_lo = BFieldElement::new(leaf_count & u32::MAX as u64);
140 stack.push(leaf_count_hi);
141 stack.push(leaf_count_lo);
142
143 let mut memory: HashMap<BFieldElement, BFieldElement> = HashMap::default();
145 rust_shadowing_helper_functions::list::list_insert(
146 peaks_pointer,
147 mmra.peaks(),
148 &mut memory,
149 );
150 let nondeterminism = NonDeterminism::default().with_ram(memory);
151
152 ProcedureInitialState {
153 stack,
154 nondeterminism,
155 ..Default::default()
156 }
157 }
158 }
159
160 impl Procedure for MmrVerifyFromSecretInLeafIndexOnStack {
161 fn rust_shadow(
162 &self,
163 stack: &mut Vec<BFieldElement>,
164 memory: &mut HashMap<BFieldElement, BFieldElement>,
165 nondeterminism: &NonDeterminism,
166 _: &[BFieldElement],
167 _: &mut Option<Tip5>,
168 ) -> Vec<BFieldElement> {
169 let leaf_index = pop_encodable(stack);
170 let leaf_count = pop_encodable(stack);
171 let leaf_digest = pop_encodable(stack);
172 let peaks_pointer = pop_encodable(stack);
173
174 let peaks = Vec::<Digest>::decode_from_memory(memory, peaks_pointer).unwrap();
175
176 let (mut mt_index, _peak_index) =
177 leaf_index_to_mt_index_and_peak_index(leaf_index, leaf_count);
178
179 let mut auth_path: Vec<Digest> = vec![];
180 let mut i = 0;
181 while mt_index != 1 {
182 auth_path.push(nondeterminism.digests[i]);
183 mt_index /= 2;
184 i += 1;
185 }
186
187 let valid_mp = MmrMembershipProof::new(auth_path).verify(
188 leaf_index,
189 leaf_digest,
190 &peaks,
191 leaf_count,
192 );
193
194 assert!(valid_mp, "MMR leaf must authenticate against peak");
195
196 vec![]
197 }
198
199 fn pseudorandom_initial_state(
200 &self,
201 seed: [u8; 32],
202 bench_case: Option<BenchmarkCase>,
203 ) -> ProcedureInitialState {
204 let mut rng = StdRng::from_seed(seed);
205
206 let (leaf_count, leaf_index) = match bench_case {
207 Some(BenchmarkCase::CommonCase) => (1u64 << 32, 1 << 31),
208 Some(BenchmarkCase::WorstCase) => (1u64 << 62, 1 << 61),
209 None => {
210 let leaf_count = rng.random_range(0..(1 << 62));
211 let leaf_index = rng.random_range(0..leaf_count);
212
213 (leaf_count, leaf_index)
214 }
215 };
216
217 let peaks_pointer: BFieldElement = rng.random();
218 let valid_leaf: Digest = rand::random();
219 let (mmr, mps) = mmra_with_mps(leaf_count, vec![(leaf_index, valid_leaf)]);
220 self.prepare_state(
221 &mmr,
222 peaks_pointer,
223 leaf_index,
224 valid_leaf,
225 mps[0].authentication_path.clone(),
226 )
227 }
228 }
229
230 #[test]
231 fn rust_shadow() {
232 ShadowedProcedure::new(MmrVerifyFromSecretInLeafIndexOnStack).test();
233 }
234
235 #[proptest(cases = 32)]
236 fn negative_test_bad_leaf_index(
237 #[strategy(0_u64..1 << 62)] leaf_count: u64,
238 #[strategy(0_u64..#leaf_count)] real_leaf_index: u64,
239 #[strategy(0..#leaf_count)]
240 #[filter(#real_leaf_index != #bad_leaf_index)]
241 bad_leaf_index: u64,
242 #[strategy(arb())] leaf: Digest,
243 #[strategy(arb())] peaks_pointer: BFieldElement,
244 ) {
245 let (mmr, mps) = mmra_with_mps(leaf_count, vec![(real_leaf_index, leaf)]);
246 let auth_path = mps[0].authentication_path.clone();
247
248 let padded_auth_path = [auth_path.clone(), random_elements(64)].concat();
252 let init_state = MmrVerifyFromSecretInLeafIndexOnStack.prepare_state(
253 &mmr,
254 peaks_pointer,
255 bad_leaf_index,
256 leaf,
257 padded_auth_path,
258 );
259
260 test_assertion_failure(
261 &ShadowedProcedure::new(MmrVerifyFromSecretInLeafIndexOnStack),
262 init_state.into(),
263 &[10],
264 );
265
266 assert!(!MmrMembershipProof::new(auth_path).verify(
268 bad_leaf_index,
269 leaf,
270 &mmr.peaks(),
271 mmr.num_leafs()
272 ));
273 }
274}
275
276#[cfg(test)]
277mod benches {
278 use super::*;
279 use crate::test_prelude::*;
280
281 #[test]
282 fn benchmark() {
283 ShadowedProcedure::new(MmrVerifyFromSecretInLeafIndexOnStack).bench();
284 }
285}