tasm_lib/hashing/
merkle_root.rs1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4
5use crate::prelude::*;
6use crate::traits::basic_snippet::Reviewer;
7use crate::traits::basic_snippet::SignOffFingerprint;
8
9#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
33pub struct MerkleRoot;
34
35impl MerkleRoot {
36 pub const NUM_LEAFS_NOT_POWER_OF_2_ERROR_ID: i128 = 431;
37}
38
39impl BasicSnippet for MerkleRoot {
40 fn parameters(&self) -> Vec<(DataType, String)> {
41 vec![(
42 DataType::List(Box::new(DataType::Digest)),
43 "*leafs".to_string(),
44 )]
45 }
46
47 fn return_values(&self) -> Vec<(DataType, String)> {
48 vec![(DataType::Digest, "root".to_string())]
49 }
50
51 fn entrypoint(&self) -> String {
52 "tasmlib_hashing_merkle_root".to_string()
53 }
54
55 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
56 let dyn_malloc = library.import(Box::new(DynMalloc));
57
58 let entrypoint = self.entrypoint();
59 let calculate_parent_digests = format!("{entrypoint}_calculate_parent_digests");
60 let next_layer_loop = format!("{entrypoint}_next_layer_loop");
61
62 triton_asm!(
63 {entrypoint}:
64 read_mem 1
67 addi 1
68 dup 1
72 pop_count
73 push 1
74 eq
75 assert error_id {Self::NUM_LEAFS_NOT_POWER_OF_2_ERROR_ID}
76
77 call {dyn_malloc}
78 dup 2
82 addi -1
83 push {Digest::LEN}
84 mul
85 add
86 pick 1
91 dup 2
92 push {Digest::LEN}
93 mul
94 add
95 call {next_layer_loop}
99 place 2
102 pop 2
103 read_mem {Digest::LEN}
106 pop 1
109 return
112
113 {next_layer_loop}:
115 dup 2
119 push 1
120 eq
121 skiz
122 return
123 pick 2
127 push {bfe!(2).inverse()}
128 hint one_half = stack[0]
129 mul
130 place 2
131 dup 1
137 dup 3
138 push {-(Digest::LEN as isize)}
139 mul
140 add
141 dup 2
145 push 0
146 push 0
147 push 0
148 push 0
149 pick 6
150 call {calculate_parent_digests}
153 pop 5
154 pop 1
155 pick 1
159 addi {Digest::LEN - 1}
162 recurse
165
166 {calculate_parent_digests}:
169 read_mem {Digest::LEN}
170 read_mem {Digest::LEN}
171 place 10
176 hash
179 pick 10
182 write_mem {Digest::LEN}
185 addi -10
188 place 5
193 recurse_or_return
196 )
197 }
198
199 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
200 let mut sign_offs = HashMap::new();
201 sign_offs.insert(Reviewer("ferdinand"), 0x19b073a58272366c.into());
202 sign_offs
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use proptest::collection::vec;
209 use twenty_first::util_types::merkle_tree::MerkleTree;
210
211 use super::*;
212 use crate::rust_shadowing_helper_functions::dyn_malloc::dynamic_allocator;
213 use crate::test_prelude::*;
214
215 impl MerkleRoot {
216 fn init_state(
217 &self,
218 leafs: Vec<Digest>,
219 digests_pointer: BFieldElement,
220 ) -> FunctionInitialState {
221 let mut memory = HashMap::new();
222 encode_to_memory(&mut memory, digests_pointer, &leafs);
223 let mut stack = self.init_stack_for_isolated_run();
224 stack.push(digests_pointer);
225
226 FunctionInitialState { stack, memory }
227 }
228 }
229
230 impl Function for MerkleRoot {
231 fn rust_shadow(
232 &self,
233 stack: &mut Vec<BFieldElement>,
234 memory: &mut HashMap<BFieldElement, BFieldElement>,
235 ) -> Result<(), RustShadowError> {
236 let leafs_pointer = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
237 let leafs = *Vec::decode_from_memory(memory, leafs_pointer)
238 .map_err(|_| RustShadowError::DecodingError)?;
239 let mt = MerkleTree::par_new(&leafs).map_err(|_| RustShadowError::Other)?;
240
241 let tree_pointer = dynamic_allocator(memory);
243 let num_internal_nodes = leafs.len();
244
245 for node_index in 1..num_internal_nodes {
246 let node = mt.node(node_index).ok_or(RustShadowError::Other)?;
247 let node_address = tree_pointer + bfe!(node_index * Digest::LEN);
248 encode_to_memory(memory, node_address, &node);
249 }
250
251 stack.extend(mt.root().reversed().values());
252 Ok(())
253 }
254
255 fn pseudorandom_initial_state(
256 &self,
257 seed: [u8; 32],
258 bench_case: Option<BenchmarkCase>,
259 ) -> FunctionInitialState {
260 let mut rng = StdRng::from_seed(seed);
261 let num_leafs = match bench_case {
262 Some(BenchmarkCase::CommonCase) => 512,
263 Some(BenchmarkCase::WorstCase) => 1024,
264 None => 1 << rng.random_range(0..=8),
265 };
266 let leafs = (0..num_leafs).map(|_| rng.random()).collect_vec();
267 let digests_pointer = rng.random();
268
269 self.init_state(leafs, digests_pointer)
270 }
271
272 fn corner_case_initial_states(&self) -> Vec<FunctionInitialState> {
273 let height_0 = self.init_state(vec![Digest::default()], bfe!(0));
274 let height_1 = self.init_state(vec![Digest::default(), Digest::default()], bfe!(0));
275
276 vec![height_0, height_1]
277 }
278 }
279
280 #[macro_rules_attr::apply(test)]
281 fn rust_shadow() {
282 ShadowedFunction::new(MerkleRoot).test();
283 }
284
285 #[macro_rules_attr::apply(test)]
286 fn computing_root_of_tree_of_height_0_crashes_vm() {
287 test_assertion_failure(
288 &ShadowedFunction::new(MerkleRoot),
289 MerkleRoot.init_state(vec![], bfe!(0)).into(),
290 &[MerkleRoot::NUM_LEAFS_NOT_POWER_OF_2_ERROR_ID],
291 );
292 }
293
294 #[macro_rules_attr::apply(proptest(cases = 100))]
295 fn computing_root_of_tree_of_height_not_power_of_2_crashes_vm(
296 #[strategy(vec(arb(), 0..2048))]
297 #[filter(!#leafs.len().is_power_of_two())]
298 leafs: Vec<Digest>,
299 #[strategy(arb())] address: BFieldElement,
300 ) {
301 test_assertion_failure(
302 &ShadowedFunction::new(MerkleRoot),
303 MerkleRoot.init_state(leafs, address).into(),
304 &[MerkleRoot::NUM_LEAFS_NOT_POWER_OF_2_ERROR_ID],
305 );
306 }
307}
308
309#[cfg(test)]
310mod benches {
311 use super::*;
312 use crate::test_prelude::*;
313
314 #[macro_rules_attr::apply(test)]
315 fn benchmark() {
316 ShadowedFunction::new(MerkleRoot).bench();
317 }
318}