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 parameters(&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 return_values(&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 ) -> Result<Vec<BFieldElement>, RustShadowError> {
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)
175 .map_err(|_| RustShadowError::DecodingError)?;
176
177 let (mut mt_index, _peak_index) =
178 leaf_index_to_mt_index_and_peak_index(leaf_index, leaf_count);
179
180 let mut auth_path: Vec<Digest> = vec![];
181 let mut i = 0;
182 while mt_index != 1 {
183 auth_path.push(nondeterminism.digests[i]);
184 mt_index /= 2;
185 i += 1;
186 }
187
188 let valid_mp = MmrMembershipProof::new(auth_path).verify(
189 leaf_index,
190 leaf_digest,
191 &peaks,
192 leaf_count,
193 );
194
195 valid_mp.then(Vec::new).ok_or(RustShadowError::InvalidProof)
196 }
197
198 fn pseudorandom_initial_state(
199 &self,
200 seed: [u8; 32],
201 bench_case: Option<BenchmarkCase>,
202 ) -> ProcedureInitialState {
203 let mut rng = StdRng::from_seed(seed);
204
205 let (leaf_count, leaf_index) = match bench_case {
206 Some(BenchmarkCase::CommonCase) => (1u64 << 32, 1 << 31),
207 Some(BenchmarkCase::WorstCase) => (1u64 << 62, 1 << 61),
208 None => {
209 let leaf_count = rng.random_range(0..(1 << 62));
210 let leaf_index = rng.random_range(0..leaf_count);
211
212 (leaf_count, leaf_index)
213 }
214 };
215
216 let peaks_pointer: BFieldElement = rng.random();
217 let valid_leaf: Digest = rand::random();
218 let (mmr, mps) = mmra_with_mps(leaf_count, vec![(leaf_index, valid_leaf)]);
219 self.prepare_state(
220 &mmr,
221 peaks_pointer,
222 leaf_index,
223 valid_leaf,
224 mps[0].authentication_path.clone(),
225 )
226 }
227 }
228
229 #[macro_rules_attr::apply(test)]
230 fn rust_shadow() {
231 ShadowedProcedure::new(MmrVerifyFromSecretInLeafIndexOnStack).test();
232 }
233
234 #[macro_rules_attr::apply(proptest(cases = 32))]
235 fn negative_test_bad_leaf_index(
236 #[strategy(0_u64..1 << 62)] leaf_count: u64,
237 #[strategy(0_u64..#leaf_count)] real_leaf_index: u64,
238 #[strategy(0..#leaf_count)]
239 #[filter(#real_leaf_index != #bad_leaf_index)]
240 bad_leaf_index: u64,
241 #[strategy(arb())] leaf: Digest,
242 #[strategy(arb())] peaks_pointer: BFieldElement,
243 ) {
244 let (mmr, mps) = mmra_with_mps(leaf_count, vec![(real_leaf_index, leaf)]);
245 let auth_path = mps[0].authentication_path.clone();
246
247 let padded_auth_path = [auth_path.clone(), random_elements(64)].concat();
251 let init_state = MmrVerifyFromSecretInLeafIndexOnStack.prepare_state(
252 &mmr,
253 peaks_pointer,
254 bad_leaf_index,
255 leaf,
256 padded_auth_path,
257 );
258
259 test_assertion_failure(
260 &ShadowedProcedure::new(MmrVerifyFromSecretInLeafIndexOnStack),
261 init_state.into(),
262 &[10],
263 );
264
265 assert!(!MmrMembershipProof::new(auth_path).verify(
267 bad_leaf_index,
268 leaf,
269 &mmr.peaks(),
270 mmr.num_leafs()
271 ));
272 }
273}
274
275#[cfg(test)]
276mod benches {
277 use super::*;
278 use crate::test_prelude::*;
279
280 #[macro_rules_attr::apply(test)]
281 fn benchmark() {
282 ShadowedProcedure::new(MmrVerifyFromSecretInLeafIndexOnStack).bench();
283 }
284}