Skip to main content

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}