tasm_lib/mmr/authentication_struct/root_from_authentication_struct.rs
1use triton_vm::prelude::*;
2use twenty_first::math::x_field_element::EXTENSION_DEGREE;
3
4use crate::data_type::DataType;
5use crate::library::Library;
6use crate::mmr::authentication_struct::derive_challenges::DeriveChallenges;
7use crate::prelude::BasicSnippet;
8
9pub struct RootFromAuthenticationStruct;
10
11impl RootFromAuthenticationStruct {
12 pub(crate) const LEAF_INDEX_GE_NUM_LEAFS_ERROR_ID: i128 = 630;
13
14 fn indexed_leaf_element_type() -> DataType {
15 DataType::Tuple(vec![DataType::U64, DataType::Digest])
16 }
17}
18
19impl BasicSnippet for RootFromAuthenticationStruct {
20 fn parameters(&self) -> Vec<(DataType, String)> {
21 vec![
22 (DataType::U32, "tree_height".to_owned()),
23 (
24 DataType::List(Box::new(DataType::Digest)),
25 "auth_struct".to_owned(),
26 ),
27 (
28 DataType::List(Box::new(Self::indexed_leaf_element_type())),
29 "indexed_leafs".to_owned(),
30 ),
31 ]
32 }
33
34 fn return_values(&self) -> Vec<(DataType, String)> {
35 vec![(DataType::Digest, "root".to_owned())]
36 }
37
38 fn entrypoint(&self) -> String {
39 "tasmlib_mmr_root_from_authentication_struct".to_owned()
40 }
41
42 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
43 let alpha_challenge_allocation = library.kmalloc(EXTENSION_DEGREE as u32);
44 let beta_challenge_allocation = library.kmalloc(EXTENSION_DEGREE as u32);
45 let beta_challenge_read_address = beta_challenge_allocation.read_address();
46 let gamma_challenge_allocation = library.kmalloc(EXTENSION_DEGREE as u32);
47 let gamma_challenge_read_address = gamma_challenge_allocation.read_address();
48 let t_digest_allocation = library.kmalloc(Digest::LEN as u32);
49 let t_digest_write_address = t_digest_allocation.write_address();
50 let t_digest_read_address = t_digest_allocation.read_address();
51 let right_digest_allocation = library.kmalloc(Digest::LEN as u32);
52 let left_digest_allocation = library.kmalloc(Digest::LEN as u32);
53
54 let indexed_leaf_element_size = Self::indexed_leaf_element_type().stack_size();
55 let derive_challenges = library.import(Box::new(DeriveChallenges));
56 let calculate_and_store_challenges = triton_asm!(
57 // _ *auth_struct *indexed_leafs
58
59 call {derive_challenges}
60 hint alpha: XFieldElement = stack[0..3]
61 hint minus_beta: XFieldElement = stack[3..6]
62 hint gamma: XFieldElement = stack[6..9]
63 // _ [gamma] [-beta] [alpha] <- rename
64
65 push {alpha_challenge_allocation.write_address()}
66 write_mem {EXTENSION_DEGREE}
67 pop 1
68 // _ [gamma] [-beta]
69
70 push {beta_challenge_allocation.write_address()}
71 write_mem {EXTENSION_DEGREE}
72 pop 1
73 // _ [gamma]
74
75 push {gamma_challenge_allocation.write_address()}
76 write_mem {EXTENSION_DEGREE}
77 pop 1
78 // _
79 );
80
81 let u64_size = DataType::U64.stack_size();
82 const TWO_POW_32: u64 = 1 << 32;
83 let u64_to_bfe = triton_asm!(
84 // _ leaf_idx_hi leaf_idx_lo
85
86 swap 1
87 push {TWO_POW_32}
88 mul
89 // _ leaf_idx_lo (leaf_idx_hi << 32)
90
91 add
92
93 // _ leaf_idx_as_bfe
94 );
95
96 let digest_to_xfe = triton_asm!(
97 // _ [digest]
98
99 push 1
100 push {alpha_challenge_allocation.read_address()}
101 read_mem {EXTENSION_DEGREE}
102 pop 1
103 // _ l4 l3 l2 l1 l0 1 [α; 3]
104
105 xx_mul
106 // _ l4 l3 l2 [(l1 l0 1) * α]
107
108 xx_add
109 // _ xfe
110 );
111
112 let entrypoint = self.entrypoint();
113 let accumulate_indexed_leafs_loop_label = format!("{entrypoint}_acc_indexed_leafs");
114 let accumulated_indexed_leafs_loop = triton_asm!(
115 // INVARIANT: _ num_leafs *auth_struct *idx_leafs *idx_leafs[n]_lw [num_leafs; 2] [p; 3]
116 {accumulate_indexed_leafs_loop_label}:
117 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n]_lw [num_leafs; 2] [p; 3]
118
119 /* Read leaf-index, convert it to BFE, and multiply it with `gamma` challenge */
120 push {gamma_challenge_read_address}
121 read_mem {EXTENSION_DEGREE}
122 pop 1
123 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n]_lw [num_leafs; 2] [p; 3] [gamma]
124 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n]_lw [num_leafs; 2] [p; 3] [γ] <-- rename
125
126 dup 8
127 read_mem {u64_size}
128 swap 11
129 pop 1
130 // _ num_leafs *auth_struct *idx_leafs (*idx_leafs[n]_lw - 2) [num_leafs; 2] [p; 3] [γ] [leaf_idx; 2]
131
132 /* Assert `leaf_idx < num_leafs`. Also guarantees that both words of
133 `leaf_idx` are valid u32s, since `lt` crashes the VM otherwise. */
134 dup 9
135 dup 2
136 lt
137 // _ num_leafs *auth_struct *idx_leafs (*idx_leafs[n]_lw - 2) [num_leafs; 2] [p; 3] [γ] [leaf_idx; 2] (leaf_idx_hi < num_leafs_hi)
138
139 dup 10
140 dup 3
141 eq
142 // _ num_leafs *auth_struct *idx_leafs (*idx_leafs[n]_lw - 2) [num_leafs; 2] [p; 3] [γ] [leaf_idx; 2] (leaf_idx_hi < num_leafs_hi) (leaf_idx_hi == num_leafs_hi)
143
144 dup 10
145 dup 3
146 lt
147 // _ num_leafs *auth_struct *idx_leafs (*idx_leafs[n]_lw - 2) [num_leafs; 2] [p; 3] [γ] [leaf_idx; 2] (leaf_idx_hi < num_leafs_hi) (leaf_idx_hi == num_leafs_hi) (leaf_idx_lo < num_leafs_lo)
148
149 mul
150 add
151 // _ num_leafs *auth_struct *idx_leafs (*idx_leafs[n]_lw - 2) [num_leafs; 2] [p; 3] [γ] [leaf_idx; 2] (leaf_idx < num_leafs)
152
153 assert error_id {Self::LEAF_INDEX_GE_NUM_LEAFS_ERROR_ID}
154 // _ num_leafs *auth_struct *idx_leafs (*idx_leafs[n]_lw - 2) [num_leafs; 2] [p; 3] [γ] [leaf_idx; 2]
155
156 {&u64_to_bfe}
157 // _ num_leafs *auth_struct *idx_leafs (*idx_leafs[n]_lw - 2) [num_leafs; 2] [p; 3] [γ] leaf_idx_bfe
158
159 dup 12
160 add
161 // _ num_leafs *auth_struct *idx_leafs (*idx_leafs[n]_lw - 2) [num_leafs; 2] [p; 3] [γ] node_idx_bfe
162
163 xb_mul
164 // _ num_leafs *auth_struct *idx_leafs (*idx_leafs[n]_lw - 2) [num_leafs; 2] [p; 3] [γ * node_idx; 3]
165
166 dup 8
167 read_mem {Digest::LEN}
168 swap 14
169 pop 1
170 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n-1]_lw [num_leafs; 2] [p; 3] [γ * node_idx; 3] [leaf; 5]
171 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n-1]_lw [num_leafs; 2] [p; 3] [γ * node_idx; 3] l4 l3 l2 l1 l0
172
173 /* Convert `leaf` to XFE, using challenge */
174 {&digest_to_xfe}
175 hint leaf_as_xfe: XFieldElement = stack[0..2]
176 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n-1]_lw [num_leafs; 2] [p; 3] [γ * node_idx; 3] [(l1 l0 1) * α + (l4 l3 l2)]
177 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n-1]_lw [num_leafs; 2] [p; 3] [γ * node_idx; 3] [leaf_as_xfe] <-- rename
178
179 push {beta_challenge_read_address}
180 read_mem {EXTENSION_DEGREE}
181 pop 1
182 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n-1]_lw [num_leafs; 2] [p; 3] [γ * node_idx; 3] [leaf_as_xfe] [-beta]
183
184 xx_add
185 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n-1]_lw [num_leafs; 2] [p; 3] [γ * node_idx; 3] [leaf_as_xfe - beta]
186
187 xx_add
188 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n-1]_lw [num_leafs; 2] [p; 3] [γ * node_idx + leaf_as_xfe - beta]
189
190 xx_mul
191 // _ num_leafs *auth_struct *idx_leafs *idx_leafs[n-1]_lw [num_leafs; 2] [p'; 3]
192
193 recurse_or_return
194 );
195 let accumulate_indexed_leafs_from_public_data = triton_asm!(
196 // _ num_leafs *auth_struct *indexed_leafs
197
198 dup 0
199 read_mem 1
200 push 1
201 add
202 swap 1
203 // _ num_leafs *auth_struct *indexed_leafs *indexed_leafs indexed_leafs_len
204
205 /* Disallow empty list of indexed leafs */
206 dup 0
207 push 0
208 eq
209 push 0
210 eq
211 assert
212 // _ num_leafs *auth_struct *indexed_leafs *indexed_leafs indexed_leafs_len
213
214 push {indexed_leaf_element_size}
215 mul
216 // _ num_leafs *auth_struct *indexed_leafs *indexed_leafs (indexed_leafs_size - 1)
217
218 add
219 // _ num_leafs *auth_struct *indexed_leafs *indexed_leafs_last_word
220
221 /* `num_leafs` as u64, for bounds-checking the leaf indices */
222 dup 3
223 split
224 // _ num_leafs *auth_struct *indexed_leafs *indexed_leafs_last_word [num_leafs; 2]
225
226 push 0
227 push 0
228 push 1
229 hint p: XFieldElement = stack[0..3]
230 // _ num_leafs *auth_struct *indexed_leafs *indexed_leafs_last_word [num_leafs; 2] [p; 3]
231
232 dup 6
233 dup 6
234 eq
235 push 0
236 eq
237 skiz
238 call {accumulate_indexed_leafs_loop_label}
239 // _ num_leafs *auth_struct *indexed_leafs *indexed_leafs [num_leafs; 2] [p; 3]
240
241 /* Next loop expects `prev`, initialized to 0, in place of `num_leafs` */
242 push 0
243 swap 4
244 pop 1
245 push 0
246 swap 5
247 pop 1
248 hint prev = stack[3..5]
249 // _ num_leafs *auth_struct *indexed_leafs *indexed_leafs [0u64; 2] [p; 3]
250 );
251
252 let accumulate_auth_struct_leafs_from_public_data_label =
253 format!("{entrypoint}_auth_struct_loop");
254
255 // let u64_lt = library.import(Box::new(LtU64PreserveArgs));
256 let u64_lt = triton_asm!(
257 // _ lhs_hi lhs_lo rhs_hi rhs_lo
258
259 /* calculate rhs_hi < lhs_hi || rhs_hi == lhs_hi && rhs_lo < lhs_lo */
260 dup 2
261 swap 1
262 // _ lhs_hi lhs_lo rhs_hi lhs_lo rhs_lo
263
264 lt
265 // _ lhs_hi lhs_lo rhs_hi (lhs_lo > rhs_lo)
266 // _ lhs_hi lhs_lo rhs_hi (rhs_lo < lhs_lo)
267
268 dup 1
269 dup 4
270 eq
271 // _ lhs_hi lhs_lo rhs_hi (rhs_lo < lhs_lo) (rhs_hi == lhs_hi)
272
273 mul
274 // _ lhs_hi lhs_lo rhs_hi (rhs_lo < lhs_lo && rhs_hi == lhs_hi)
275
276 dup 3
277 swap 1
278 swap 2
279 // _ lhs_hi lhs_lo (rhs_lo < lhs_lo && rhs_hi == lhs_hi) lhs_hi rhs_hi
280
281 lt
282 // _ lhs_hi lhs_lo (rhs_lo < lhs_lo && rhs_hi == lhs_hi) (lhs_hi > rhs_hi)
283 // _ lhs_hi lhs_lo (rhs_lo < lhs_lo && rhs_hi == lhs_hi) (rhs_hi < lhs_hi)
284
285 add
286 // _ lhs_hi lhs_lo (rhs_lo < lhs_lo && rhs_hi == lhs_hi || rhs_hi < lhs_hi)
287 );
288
289 let accumulate_auth_struct_leafs_from_public_data = triton_asm!(
290 // INVARIANT: _ *auth_struct *auth_struct[n]_lw [prev; 2] [p]
291 {accumulate_auth_struct_leafs_from_public_data_label}:
292 /* Divine in auth-struct node-index and verify ordering */
293
294 divine 2
295 hint auth_struct_elem_node_index: u64 = stack[0..2]
296 // _ *auth_struct *auth_struct[n]_lw [prev; 2] [p] [node_index]
297
298
299 /* Notice that the u64-lt snippet crashes if divined node-index
300 words are not valid u32s. So no need to check explicitly. */
301 dup 6
302 dup 6
303 {&u64_lt}
304 // _ *auth_struct *auth_struct[n]_lw [prev; 2] [p] [node_index] (prev < nodex_index)
305
306 assert
307 // _ *auth_struct *auth_struct[n]_lw [prev; 2] [p] [node_index]
308 // _ *auth_struct *auth_struct[n]_lw prev_hi prev_lo p2 p1 p0 node_index_hi node_index_lo
309
310 swap 5
311 pop 1
312 swap 5
313 pop 1
314 // _ *auth_struct *auth_struct[n]_lw [node_index] [p]
315
316 /* Calculate `node_index * challenge` */
317 push {gamma_challenge_read_address}
318 read_mem {EXTENSION_DEGREE}
319 pop 1
320 // _ *auth_struct *auth_struct[n]_lw [node_index] [p] [γ]
321
322 dup 7
323 push {TWO_POW_32}
324 mul
325 dup 7
326 add
327 // _ *auth_struct *auth_struct[n]_lw [node_index] [p] [γ] node_index_bfe
328
329 xb_mul
330 // _ *auth_struct *auth_struct[n]_lw [node_index] [p] [node_index * γ]
331
332 /* Read auth-struct element and convert to XFE */
333 dup 8
334 read_mem {Digest::LEN}
335 swap 14
336 pop 1
337 // _ *auth_struct *auth_struct[n]_lw [node_index] [p] [node_index * γ] [auth_struct_digest]
338
339 {&digest_to_xfe}
340 // _ *auth_struct *auth_struct[n]_lw [node_index] [p] [node_index * γ] [auth_struct_xfe]
341
342 push {beta_challenge_read_address}
343 read_mem {EXTENSION_DEGREE}
344 pop 1
345 xx_add
346 // _ *auth_struct *auth_struct[n]_lw [node_index] [p] [node_index * γ] [auth_struct_xfe - beta]
347
348 xx_add
349 // _ *auth_struct *auth_struct[n]_lw [node_index] [p] [node_index * γ + auth_struct_xfe - beta]
350
351 xx_mul
352 // _ *auth_struct *auth_struct[n]_lw [node_index] [p * (node_index * γ + auth_struct_xfe - beta)]
353 // _ *auth_struct *auth_struct[n]_lw [prev] [p'] <-- rename
354
355 recurse_or_return
356 );
357
358 let dup_top_digest = triton_asm![dup 4; Digest::LEN];
359 let store_t_digest_in_memory_label = format!("{entrypoint}_store_t_digest");
360 let store_t_digest_in_memory = triton_asm!(
361 {store_t_digest_in_memory_label}:
362 // _ [t]
363
364 {&dup_top_digest}
365 push {t_digest_write_address}
366 write_mem {Digest::LEN}
367 pop 1
368 // _ [t]
369
370 return
371 );
372
373 let nd_loop_label = format!("{entrypoint}_nd_loop");
374 let one_half = BFieldElement::new(2).inverse();
375 let nd_loop = triton_asm!(
376 // _ INVARIANT: _ [p]
377 {nd_loop_label}:
378 divine 2
379 // _ [p] left_index right_index
380
381 dup 1
382 push 1
383 add
384 eq
385 // _ [p] l_index_bfe (left_index + 1 == right_index)
386
387 assert
388 hint left_index: BFieldElement = stack[0..1]
389 // _ [p] l_index_bfe
390
391 swap 3
392 swap 2
393 swap 1
394 // _ l_index_bfe [p]
395
396 /* Calculate parent digest, preserving child digests */
397 divine {Digest::LEN}
398 hint right: Digest = stack[0..5]
399 // _ l_index_bfe [p] [right]
400
401 {&dup_top_digest}
402 push {right_digest_allocation.write_address()}
403 write_mem {Digest::LEN}
404 pop 1
405 // _ l_index_bfe [p] [right]
406
407 {&digest_to_xfe}
408 // _ l_index_bfe [p] [right_xfe]
409
410 push {beta_challenge_read_address}
411 read_mem {EXTENSION_DEGREE}
412 pop 1
413 xx_add
414 // _ l_index_bfe [p] [right_xfe - β]
415
416 push {gamma_challenge_read_address}
417 read_mem {EXTENSION_DEGREE}
418 pop 1
419 // _ l_index_bfe [p] [right_xfe - β] [γ]
420
421 dup 9
422 push 1
423 add
424 // _ l_index_bfe [p] [right_xfe - β] [γ] r_index_bfe
425
426 xb_mul
427 // _ l_index_bfe [p] [right_xfe - β] [γ * r_index_bfe]
428
429 xx_add
430 // _ l_index_bfe [p] [t_xfe - β + γ * parent_index]
431 // _ l_index_bfe [p] [fact_right]
432
433 divine {Digest::LEN}
434 hint left: Digest = stack[0..5]
435 // _ l_index_bfe [p] [fact_right] [left]
436
437 {&dup_top_digest}
438 push {left_digest_allocation.write_address()}
439 write_mem {Digest::LEN}
440 pop 1
441 // _ l_index_bfe [p] [fact_right] [left]
442
443 {&digest_to_xfe}
444 // _ l_index_bfe [p] [fact_right] [left_xfe]
445
446 push {beta_challenge_read_address}
447 read_mem {EXTENSION_DEGREE}
448 pop 1
449 xx_add
450 // _ l_index_bfe [p] [fact_right] [left_xfe - β]
451
452 push {gamma_challenge_read_address}
453 read_mem {EXTENSION_DEGREE}
454 pop 1
455 // _ l_index_bfe [p] [fact_right] [left_xfe - β] [γ]
456
457 dup 12
458 xb_mul
459 // _ l_index_bfe [p] [fact_right] [left_xfe - β] [l_index_bfe * γ]
460
461 xx_add
462 // _ l_index_bfe [p] [fact_right] [fact_left]
463
464 xx_mul
465 // _ l_index_bfe [p] [fact_right * fact_left]
466
467 x_invert
468 // _ l_index_bfe [p] [(fact_right*fact_left)^{-1}]
469
470 xx_mul
471 // _ l_index_bfe [p * (fact_right*fact_left)^{-1}]
472
473 /* Calculate t = hash(left, right) */
474 push {right_digest_allocation.read_address()}
475 read_mem {Digest::LEN}
476 pop 1
477 push {left_digest_allocation.read_address()}
478 read_mem {Digest::LEN}
479 pop 1
480 hash
481 // _ l_index_bfe [p * (fact_right*fact_left)^{-1}] [t]
482
483 /* Store [t] digest in memory if this is last loop iteration */
484 dup 8
485 push 2
486 eq
487 skiz
488 call {store_t_digest_in_memory_label}
489 // _ l_index_bfe [p * (fact_right*fact_left)^{-1}] [t]
490
491 {&digest_to_xfe}
492 // _ l_index_bfe [p * (fact_right*fact_left)^{-1}] [t_xfe]
493
494 push {beta_challenge_read_address}
495 read_mem {EXTENSION_DEGREE}
496 pop 1
497 xx_add
498 // _ l_index_bfe [p * (fact_right*fact_left)^{-1}] [t_xfe - β]
499
500 push {gamma_challenge_read_address}
501 read_mem {EXTENSION_DEGREE}
502 pop 1
503 // _ l_index_bfe [p * (fact_right*fact_left)^{-1}] [t_xfe - β] [γ]
504
505 dup 9
506 push {one_half}
507 mul
508 // _ l_index_bfe [p * (fact_right*fact_left)^{-1}] [t_xfe - β] [γ] parent_index
509
510 xb_mul
511 // _ l_index_bfe [p * (fact_right*fact_left)^{-1}] [t_xfe - β] [parent_index * γ]
512
513 xx_add
514 // _ l_index_bfe [p * (fact_right*fact_left)^{-1}] [fact_parent]
515
516 xx_mul
517 // _ l_index_bfe [p * (fact_right*fact_left)^{-1} * fact_parent]
518 // _ l_index_bfe [p']
519
520 swap 1
521 swap 2
522 swap 3
523 // _ [p'] l_index_bfe
524
525 /* Terminate loop when left_index == 2 <=> parent_index == 1 */
526 push 2
527 eq
528 skiz
529 return
530 // _ [p']
531
532 recurse
533 );
534
535 let compare_xfes = DataType::Xfe.compare();
536 triton_asm!(
537 {entrypoint}:
538 // _ tree_height *auth_struct *indexed_leafs
539
540 dup 1
541 dup 1
542 // _ tree_height *auth_struct *indexed_leafs *auth_struct *indexed_leafs
543
544 {&calculate_and_store_challenges}
545 // _ tree_height *auth_struct *indexed_leafs
546
547 /* Calculate number of leafs in Merkle tree
548 Notice that `tree_num_leafs` is not necessarily a u32, but is a
549 BFE and a power of two whose log_2 value is in the range
550 [0,63] */
551 swap 2
552 push 2
553 pow
554 swap 2
555 hint tree_num_leafs: BFieldElement = stack[2]
556 // _ tree_num_leafs *auth_struct *indexed_leafs
557
558 {&accumulate_indexed_leafs_from_public_data}
559 // _ tree_num_leafs *auth_struct *indexed_leafs *indexed_leafs [0; 2] [p; 3]
560 // _ tree_num_leafs *auth_struct *indexed_leafs *indexed_leafs [0; 2] p2 p1 p0 <-- rename
561
562 /* Prepare for next loop, absorption of auth-struct digests into accumulator */
563 swap 7
564 swap 6
565 pop 1
566 // _ tree_num_leafs p0 *auth_struct *indexed_leafs [0; 2] p2 p1
567
568 dup 5
569 read_mem 1
570 push 1
571 add
572 swap 1
573 // _ tree_num_leafs p0 *auth_struct *indexed_leafs [0; 2] p2 p1 *auth_struct auth_struct_len
574
575 push {Digest::LEN}
576 mul
577 add
578 // _ tree_num_leafs p0 *auth_struct *indexed_leafs [0; 2] p2 p1 *auth_struct_last_word
579
580 swap 5
581 swap 7
582 // _ tree_num_leafs *indexed_leafs *auth_struct *auth_struct_last_word [0; 2] p2 p1 p0
583 // _ tree_num_leafs *indexed_leafs *auth_struct *auth_struct_last_word [prev; 2] [p] <-- rename
584
585 dup 6
586 dup 6
587 eq
588 push 0
589 eq
590 // _ tree_num_leafs *indexed_leafs *auth_struct *auth_struct_last_word [prev; 2] [p] (*auth_struct_last_word != *auth_struct)
591
592 skiz
593 call {accumulate_auth_struct_leafs_from_public_data_label}
594 // _ tree_num_leafs *indexed_leafs *auth_struct *auth_struct_last_word [prev; 2] [p]
595
596 /* Cleanup stack before next loop */
597 swap 4
598 pop 1
599 swap 4
600 pop 1
601 swap 4
602 pop 2
603 // _ tree_num_leafs *indexed_leafs [p]
604
605 /* Set initial t values, from indexed_leafs[0] */
606 dup 3
607 push {Digest::LEN}
608 add
609 read_mem {Digest::LEN}
610 pop 1
611 // _ tree_num_leafs *indexed_leafs [p] [t; 5]
612
613 /* Write t value (in case we're not entering the loop) */
614 push {t_digest_write_address}
615 write_mem {Digest::LEN}
616 pop 1
617 // _ tree_num_leafs *indexed_leafs [p]
618
619 // _ tree_num_leafs *indexed_leafs p2 p1 p0
620 swap 2
621 swap 4
622 swap 1
623 swap 3
624 pop 1
625 // _ [p] tree_num_leafs
626
627 /* Call the ND-loop if tree_num_leafs != 1 */
628 push 1
629 eq
630 push 0
631 eq
632 // [p] (tree_num_leafs != 1)
633
634 skiz
635 call {nd_loop_label}
636 // _ [p]
637
638 /* Assert that p == t_xfe - beta + gamma */
639 push {t_digest_read_address}
640 read_mem {Digest::LEN}
641 pop 1
642 {&digest_to_xfe}
643 // _ [p] [t_xfe]
644
645 push {beta_challenge_read_address}
646 read_mem {EXTENSION_DEGREE}
647 pop 1
648 xx_add
649 // _ [p] [t_xfe - β]
650
651 push {gamma_challenge_read_address}
652 read_mem {EXTENSION_DEGREE}
653 pop 1
654 xx_add
655 // _ [p] [t_xfe - β + γ]
656
657 {&compare_xfes}
658 // _ (p == t_xfe - β + γ)
659
660 assert
661 // _
662
663 /* Return `t` (digest) */
664 push {t_digest_read_address}
665 read_mem {Digest::LEN}
666 pop 1
667 // _ [t]
668
669 return
670
671 {&accumulated_indexed_leafs_loop}
672 {&accumulate_auth_struct_leafs_from_public_data}
673 {&nd_loop}
674 {&store_t_digest_in_memory}
675 )
676 }
677}
678
679#[cfg(test)]
680mod tests {
681 use std::cmp::min;
682 use std::collections::HashMap;
683 use std::collections::VecDeque;
684
685 use itertools::Itertools;
686 use num::One;
687 use num::Zero;
688 use rand::RngExt;
689 use rand::SeedableRng;
690 use rand::rngs::StdRng;
691 use twenty_first::prelude::Sponge;
692 use twenty_first::util_types::mmr::mmr_accumulator::util::mmra_with_mps;
693
694 use super::*;
695 use crate::memory::encode_to_memory;
696 use crate::mmr::authentication_struct::shared::AuthStructIntegrityProof;
697 use crate::rust_shadowing_helper_functions::list::list_insert;
698 use crate::rust_shadowing_helper_functions::list::load_list_with_copy_elements;
699 use crate::snippet_bencher::BenchmarkCase;
700 use crate::test_helpers::test_assertion_failure;
701 use crate::traits::procedure::Procedure;
702 use crate::traits::procedure::ProcedureInitialState;
703 use crate::traits::procedure::ShadowedProcedure;
704 use crate::traits::rust_shadow::RustShadow;
705 use crate::traits::rust_shadow::RustShadowError;
706
707 /// Read and consume a digest from an input source, either public input or
708 /// secret input. Returns the digest.
709 pub(crate) fn read_digest_from_input(secret_in: &mut VecDeque<BFieldElement>) -> Digest {
710 let mut values = [BFieldElement::zero(); Digest::LEN];
711 let mut i = 0;
712 while i < Digest::LEN {
713 values[Digest::LEN - 1 - i] = secret_in.pop_front().unwrap();
714 i += 1;
715 }
716
717 Digest::new(values)
718 }
719
720 const SIZE_OF_INDEXED_LEAFS_ELEMENT: usize = Digest::LEN + 2;
721
722 #[test]
723 fn test() {
724 ShadowedProcedure::new(RootFromAuthenticationStruct).test();
725 }
726
727 #[test]
728 fn leaf_index_out_of_bounds_crashes_vm() {
729 let snippet = RootFromAuthenticationStruct;
730 let mut rng = StdRng::seed_from_u64(0);
731
732 // cover `num_leafs` fitting in a u32, and not
733 for tree_height in [0, 5, 31, 32, 40, 62] {
734 let num_leafs = 1_u64 << tree_height;
735 for bad_leaf_index in [num_leafs, num_leafs + 1, 2 * num_leafs, u64::MAX] {
736 let mut init_state = snippet.initial_state(&mut rng, tree_height, vec![0]);
737
738 // (u64, Digest) is encoded as digest first, followed by u64
739 let indexed_leafs_pointer = *init_state.stack.last().unwrap();
740 let leaf_index_pointer = indexed_leafs_pointer + bfe!(1 + Digest::LEN);
741 encode_to_memory(
742 &mut init_state.nondeterminism.ram,
743 leaf_index_pointer,
744 &bad_leaf_index,
745 );
746
747 test_assertion_failure(
748 &ShadowedProcedure::new(RootFromAuthenticationStruct),
749 init_state.into(),
750 &[RootFromAuthenticationStruct::LEAF_INDEX_GE_NUM_LEAFS_ERROR_ID],
751 );
752 }
753 }
754 }
755
756 impl Procedure for RootFromAuthenticationStruct {
757 fn rust_shadow(
758 &self,
759 stack: &mut Vec<BFieldElement>,
760 memory: &mut std::collections::HashMap<BFieldElement, BFieldElement>,
761 nondeterminism: &NonDeterminism,
762 _public_input: &[BFieldElement],
763 sponge: &mut Option<Tip5>,
764 ) -> Result<Vec<BFieldElement>, RustShadowError> {
765 fn digest_to_xfe(digest: Digest, challenge: XFieldElement) -> XFieldElement {
766 let [l0, l1, l2, l3, l4] = digest.0;
767 let leaf_xfe_lo = XFieldElement::new([BFieldElement::new(1), l0, l1]);
768 let leaf_xfe_hi = XFieldElement::new([l2, l3, l4]);
769
770 challenge * leaf_xfe_lo + leaf_xfe_hi
771 }
772
773 fn mimic_use_of_static_memory(
774 memory: &mut HashMap<BFieldElement, BFieldElement>,
775 alpha: XFieldElement,
776 beta: XFieldElement,
777 gamma: XFieldElement,
778 t: Digest,
779 right_and_left: Option<(Digest, Digest)>,
780 ) {
781 const ALPHA_POINTER_WRITE: BFieldElement = BFieldElement::new(BFieldElement::P - 4);
782 const BETA_POINTER_WRITE: BFieldElement = BFieldElement::new(BFieldElement::P - 7);
783 const GAMMA_POINTER_WRITE: BFieldElement =
784 BFieldElement::new(BFieldElement::P - 10);
785 const T_DIGEST_POINTER_WRITE: BFieldElement =
786 BFieldElement::new(BFieldElement::P - 15);
787 const RIGHT_DIGEST_POINTER_WRITE: BFieldElement =
788 BFieldElement::new(BFieldElement::P - 20);
789 const LEFT_DIGEST_POINTER_WRITE: BFieldElement =
790 BFieldElement::new(BFieldElement::P - 25);
791
792 encode_to_memory(memory, ALPHA_POINTER_WRITE, &alpha);
793 encode_to_memory(memory, BETA_POINTER_WRITE, &beta);
794 encode_to_memory(memory, GAMMA_POINTER_WRITE, &gamma);
795 encode_to_memory(memory, T_DIGEST_POINTER_WRITE, &t);
796
797 // only written in the ND-loop, which is skipped for trees of height 0
798 if let Some((right, left)) = right_and_left {
799 encode_to_memory(memory, RIGHT_DIGEST_POINTER_WRITE, &right);
800 encode_to_memory(memory, LEFT_DIGEST_POINTER_WRITE, &left);
801 }
802 }
803
804 fn accumulate_indexed_leafs(
805 indexed_leafs: &[(u64, Digest)],
806 alpha: XFieldElement,
807 beta: XFieldElement,
808 gamma: XFieldElement,
809 tree_num_leafs: u64,
810 ) -> Result<XFieldElement, RustShadowError> {
811 let mut p = XFieldElement::one();
812 for (leaf_idx, leaf) in indexed_leafs.iter().copied().rev() {
813 if leaf_idx >= tree_num_leafs {
814 let error_id =
815 RootFromAuthenticationStruct::LEAF_INDEX_GE_NUM_LEAFS_ERROR_ID;
816 return Err(RustShadowError::AssertionError(error_id));
817 }
818
819 let leaf_idx_as_bfe = bfe!(leaf_idx);
820 let node_idx_as_bfe = leaf_idx_as_bfe + bfe!(tree_num_leafs);
821 let leaf_as_xfe = digest_to_xfe(leaf, alpha);
822 let fact = leaf_as_xfe - beta + gamma * node_idx_as_bfe;
823 p *= fact;
824 }
825
826 Ok(p)
827 }
828
829 fn accumulate_auth_struct(
830 mut p: XFieldElement,
831 auth_struct: Vec<Digest>,
832 individual_tokens: &mut VecDeque<BFieldElement>,
833 alpha: XFieldElement,
834 beta: XFieldElement,
835 gamma: XFieldElement,
836 ) -> XFieldElement {
837 let mut prev = 0u64;
838
839 for auth_struct_elem in auth_struct.iter().copied().rev() {
840 let auth_struct_elem_node_index_hi: u32 =
841 individual_tokens.pop_front().unwrap().try_into().unwrap();
842 let auth_struct_elem_node_index_lo: u32 =
843 individual_tokens.pop_front().unwrap().try_into().unwrap();
844 let auth_struct_elem_node_index = ((auth_struct_elem_node_index_hi as u64)
845 << 32)
846 + auth_struct_elem_node_index_lo as u64;
847 assert!(auth_struct_elem_node_index > prev);
848 prev = auth_struct_elem_node_index;
849
850 let auth_struct_index_as_bfe = bfe!(auth_struct_elem_node_index);
851
852 let auth_struct_elem_xfe = digest_to_xfe(auth_struct_elem, alpha);
853 let fact = auth_struct_elem_xfe - beta + gamma * auth_struct_index_as_bfe;
854
855 p *= fact;
856 }
857
858 p
859 }
860
861 assert_eq!(
862 SIZE_OF_INDEXED_LEAFS_ELEMENT,
863 Self::indexed_leaf_element_type().stack_size()
864 );
865
866 // declare input-arguments
867 let indexed_leafs_pointer = stack.pop().unwrap();
868 let auth_struct_pointer = stack.pop().unwrap();
869 let tree_height: u32 = stack.pop().unwrap().try_into().unwrap();
870
871 let bfes_to_indexed_leaf =
872 |bfes: [BFieldElement; SIZE_OF_INDEXED_LEAFS_ELEMENT]| -> (u64, Digest) {
873 *<(u64, Digest)>::decode(&bfes).unwrap()
874 };
875 let bfes_to_digest = |bfes: [BFieldElement; Digest::LEN]| -> Digest {
876 *Digest::decode(&bfes[0..Digest::LEN]).unwrap()
877 };
878
879 let indexed_leafs: Vec<[BFieldElement; SIZE_OF_INDEXED_LEAFS_ELEMENT]> =
880 load_list_with_copy_elements(indexed_leafs_pointer, memory)?;
881 let indexed_leafs = indexed_leafs
882 .into_iter()
883 .map(bfes_to_indexed_leaf)
884 .collect_vec();
885 let auth_struct: Vec<[BFieldElement; Digest::LEN]> =
886 load_list_with_copy_elements(auth_struct_pointer, memory)?;
887 let auth_struct = auth_struct.into_iter().map(bfes_to_digest).collect_vec();
888
889 // Calculate challenges
890 let sponge = sponge.as_mut().expect("sponge must be initialized");
891 sponge.pad_and_absorb_all(&indexed_leafs.encode());
892 sponge.pad_and_absorb_all(&auth_struct.encode());
893
894 let sponge_output = sponge.squeeze();
895 let alpha = XFieldElement::new([sponge_output[1], sponge_output[2], sponge_output[3]]);
896 let beta = -XFieldElement::new([sponge_output[4], sponge_output[5], sponge_output[6]]);
897 let gamma = XFieldElement::new([sponge_output[7], sponge_output[8], sponge_output[9]]);
898
899 let tree_num_leafs = 1u64 << tree_height;
900
901 // Accumulate into `p` from public data
902 let mut p =
903 accumulate_indexed_leafs(&indexed_leafs, alpha, beta, gamma, tree_num_leafs)?;
904
905 let mut individual_tokens: VecDeque<BFieldElement> =
906 nondeterminism.individual_tokens.to_owned().into();
907 p = accumulate_auth_struct(p, auth_struct, &mut individual_tokens, alpha, beta, gamma);
908
909 // "Unaccumulate" into `p` from secret data, and calculate Merkle root
910 let mut t = indexed_leafs[0].1;
911 let mut t_xfe = digest_to_xfe(t, alpha);
912 let mut right_and_left = None;
913 if tree_num_leafs != 1 {
914 loop {
915 let left_index = individual_tokens.pop_front().unwrap();
916 let right_index = individual_tokens.pop_front().unwrap();
917 assert_eq!(left_index + bfe!(1), right_index);
918
919 let parent_index = left_index / bfe!(2);
920
921 let right = read_digest_from_input(&mut individual_tokens);
922 let left = read_digest_from_input(&mut individual_tokens);
923 right_and_left = Some((right, left));
924
925 t = Tip5::hash_pair(left, right);
926 t_xfe = digest_to_xfe(t, alpha);
927 let l_xfe = digest_to_xfe(left, alpha);
928 let r_xfe = digest_to_xfe(right, alpha);
929 let fact1 = l_xfe - beta + gamma * left_index;
930 let fact2 = r_xfe - beta + gamma * right_index;
931 let fact_parent = t_xfe - beta + gamma * parent_index;
932
933 p *= fact1.inverse() * fact2.inverse() * fact_parent;
934
935 if parent_index.is_one() {
936 break;
937 }
938 }
939 }
940
941 assert_eq!(t_xfe - beta + gamma, p);
942
943 // Return the Merkle root on the stack
944 for elem in t.encode().into_iter().rev() {
945 stack.push(elem);
946 }
947
948 mimic_use_of_static_memory(memory, alpha, -beta, gamma, t, right_and_left);
949
950 Ok(vec![])
951 }
952
953 fn pseudorandom_initial_state(
954 &self,
955 seed: [u8; 32],
956 bench_case: Option<BenchmarkCase>,
957 ) -> ProcedureInitialState {
958 let mut rng: StdRng = SeedableRng::from_seed(seed);
959
960 let num_chunks = 45;
961 let num_accessible_chunk_indices = 1 << 8;
962 let (tree_height, revealed_leaf_indices) = match bench_case {
963 None => {
964 let tree_height = rng.random_range(0..62);
965 let num_leafs_in_merkle_tree = 1 << tree_height;
966 let num_revealed_leafs =
967 rng.random_range(1..=min(num_leafs_in_merkle_tree, num_chunks));
968
969 let revealed_leaf_indices = (0..num_revealed_leafs)
970 .map(|_| {
971 rng.random_range(
972 0..min(num_accessible_chunk_indices, num_leafs_in_merkle_tree),
973 )
974 })
975 .unique()
976 .collect_vec();
977
978 (tree_height, revealed_leaf_indices)
979 }
980
981 // In both benchmarks, we leafs from the middle of the Merkle
982 // tree. Were we pick the indices is not so relevant for
983 // performance, as long as they're grouped together in a
984 // realistic way for the mutator set.
985 Some(BenchmarkCase::CommonCase) => {
986 let tree_height = 32;
987 let midpoint = 1 << (tree_height - 1);
988 let revealed_leaf_indices = (0..num_chunks)
989 .map(|_| {
990 rng.random_range(midpoint..num_accessible_chunk_indices + midpoint)
991 })
992 .unique()
993 .collect_vec();
994
995 (tree_height, revealed_leaf_indices)
996 }
997 Some(BenchmarkCase::WorstCase) => {
998 let tree_height = 62;
999 let midpoint = 1 << (tree_height - 1);
1000 let revealed_leaf_indices = (0..num_chunks)
1001 .map(|_| {
1002 rng.random_range(midpoint..num_accessible_chunk_indices + midpoint)
1003 })
1004 .unique()
1005 .collect_vec();
1006
1007 (tree_height, revealed_leaf_indices)
1008 }
1009 };
1010
1011 self.initial_state(&mut rng, tree_height, revealed_leaf_indices)
1012 }
1013
1014 fn corner_case_initial_states(&self) -> Vec<ProcedureInitialState> {
1015 let mut rng = StdRng::seed_from_u64(0);
1016 let tree_of_height_0 = self.initial_state(&mut rng, 0, vec![0]);
1017
1018 vec![tree_of_height_0]
1019 }
1020 }
1021
1022 impl RootFromAuthenticationStruct {
1023 fn initial_state(
1024 &self,
1025 rng: &mut StdRng,
1026 tree_height: u32,
1027 revealed_leaf_indices: Vec<u64>,
1028 ) -> ProcedureInitialState {
1029 let num_leafs_in_merkle_tree = 1 << tree_height;
1030
1031 // This picks leaf-indices with low values but I don't think that
1032 // matters for performance.
1033
1034 let num_revealed_leafs = revealed_leaf_indices.len();
1035 assert!(!num_revealed_leafs.is_zero());
1036
1037 let revealed_leafs: Vec<Digest> =
1038 (0..num_revealed_leafs).map(|_| rng.random()).collect_vec();
1039 let indexed_leafs = revealed_leaf_indices
1040 .into_iter()
1041 .zip_eq(revealed_leafs)
1042 .collect_vec();
1043
1044 let (mmra, mps) = mmra_with_mps(num_leafs_in_merkle_tree, indexed_leafs.clone());
1045 let indexed_mmr_mps = indexed_leafs
1046 .into_iter()
1047 .zip_eq(mps)
1048 .map(|((idx, leaf), mp)| (idx, leaf, mp))
1049 .collect_vec();
1050
1051 let mmr_authentication_struct =
1052 AuthStructIntegrityProof::new_from_mmr_membership_proofs(&mmra, indexed_mmr_mps);
1053 assert!(mmr_authentication_struct.len().is_one());
1054 let mmr_authentication_struct = &mmr_authentication_struct[&0];
1055
1056 let mut memory = HashMap::new();
1057 let authentication_structure_ptr = rng.random();
1058 let indexed_leafs_ptr = rng.random();
1059
1060 list_insert(
1061 authentication_structure_ptr,
1062 mmr_authentication_struct.auth_struct.clone(),
1063 &mut memory,
1064 );
1065 list_insert(
1066 indexed_leafs_ptr,
1067 mmr_authentication_struct.indexed_leafs.clone(),
1068 &mut memory,
1069 );
1070
1071 let stack = [
1072 self.init_stack_for_isolated_run(),
1073 vec![
1074 bfe!(tree_height),
1075 authentication_structure_ptr,
1076 indexed_leafs_ptr,
1077 ],
1078 ]
1079 .concat();
1080
1081 let nd_auth_struct_indices = mmr_authentication_struct
1082 .witness
1083 .nd_auth_struct_indices
1084 .iter()
1085 .rev()
1086 .flat_map(|node_index| node_index.encode().into_iter().rev().collect_vec())
1087 .collect_vec();
1088 let nd_loop_nd = mmr_authentication_struct
1089 .witness
1090 .nd_sibling_indices
1091 .iter()
1092 .copied()
1093 .zip_eq(
1094 mmr_authentication_struct
1095 .witness
1096 .nd_siblings
1097 .iter()
1098 .copied(),
1099 )
1100 .flat_map(|((left_index, right_index), (left_node, right_node))| {
1101 [
1102 vec![bfe!(left_index), bfe!(right_index)],
1103 right_node.encode().into_iter().rev().collect_vec(),
1104 left_node.encode().into_iter().rev().collect_vec(),
1105 ]
1106 .concat()
1107 })
1108 .collect_vec();
1109
1110 let individual_tokens = [nd_auth_struct_indices, nd_loop_nd].concat();
1111 let nondeterminism = NonDeterminism::new(individual_tokens).with_ram(memory);
1112 ProcedureInitialState {
1113 stack,
1114 nondeterminism,
1115 public_input: vec![],
1116 sponge: Some(Tip5::init()),
1117 }
1118 }
1119 }
1120}
1121
1122#[cfg(test)]
1123mod benches {
1124 use super::*;
1125 use crate::traits::procedure::ShadowedProcedure;
1126 use crate::traits::rust_shadow::RustShadow;
1127
1128 #[test]
1129 fn bench_root_from_auth_struct() {
1130 ShadowedProcedure::new(RootFromAuthenticationStruct).bench();
1131 }
1132}