1
1
use std:: collections:: HashMap ;
2
2
3
3
use triton_vm:: prelude:: * ;
4
- use twenty_first:: util_types:: shared :: bag_peaks ;
4
+ use triton_vm :: twenty_first:: util_types:: mmr :: mmr_accumulator :: MmrAccumulator ;
5
5
6
- use crate :: mmr :: MAX_MMR_HEIGHT ;
6
+ use crate :: arithmetic ;
7
7
use crate :: prelude:: * ;
8
8
use crate :: traits:: basic_snippet:: Reviewer ;
9
9
use crate :: traits:: basic_snippet:: SignOffFingerprint ;
10
10
11
- /// [Bag peaks](bag_peaks) into a single [`Digest`].
11
+ /// [Bag peaks](bag_peaks) of an MMR into a single [`Digest`].
12
12
///
13
13
/// # Behavior
14
14
///
15
15
/// ```text
16
- /// BEFORE: _ *peaks
16
+ /// BEFORE: _ *mmr_accumulator
17
17
/// AFTER: _ [bagged_peaks: Digest]
18
18
/// ```
19
19
///
20
20
/// # Preconditions
21
21
///
22
22
/// - the input argument points to a properly [`BFieldCodec`]-encoded list of
23
23
/// [`Digest`]s in memory
24
- /// - the pointed-to list contains fewer than [`MAX_MMR_HEIGHT`] elements
24
+ /// - the pointed-to MMR accumulator is consistent, *i.e.*, the number of peaks
25
+ /// matches with the number of set bits in the leaf count.
25
26
///
26
27
/// # Postconditions
27
28
///
28
29
/// - the output is a single [`Digest`] computed like in [`bag_peaks`]
29
30
/// - the output is properly [`BFieldCodec`] encoded
30
31
///
32
+ /// # Crashes
33
+ ///
34
+ /// - if the MMR accumulator is inconsistent, *i.e.*, if the number of peaks
35
+ /// does not match the number of set bits in the leaf count
36
+ ///
31
37
#[ derive( Debug , Copy , Clone , Eq , PartialEq , Hash ) ]
32
38
pub struct BagPeaks ;
33
39
40
+ impl BagPeaks {
41
+ const INCONSISTENT_PEAKS_NUM : usize = 11 ;
42
+ }
43
+
34
44
impl BasicSnippet for BagPeaks {
35
45
fn inputs ( & self ) -> Vec < ( DataType , String ) > {
36
- let list_of_digests = DataType :: List ( Box :: new ( DataType :: Digest ) ) ;
46
+ let mmr_accumulator = DataType :: List ( Box :: new ( DataType :: Digest ) ) ;
37
47
38
- vec ! [ ( list_of_digests , "*peaks " . to_string( ) ) ]
48
+ vec ! [ ( mmr_accumulator , "*mmra " . to_string( ) ) ]
39
49
}
40
50
41
51
fn outputs ( & self ) -> Vec < ( DataType , String ) > {
@@ -46,107 +56,77 @@ impl BasicSnippet for BagPeaks {
46
56
"tasmlib_mmr_bag_peaks" . to_string ( )
47
57
}
48
58
49
- fn code ( & self , _ : & mut Library ) -> Vec < LabelledInstruction > {
59
+ fn code ( & self , library : & mut Library ) -> Vec < LabelledInstruction > {
50
60
let entrypoint = self . entrypoint ( ) ;
51
-
52
- let length_is_zero = format ! ( "{entrypoint}_length_is_zero" ) ;
53
- let length_is_not_zero = format ! ( "{entrypoint}_length_is_not_zero" ) ;
54
- let length_is_one = format ! ( "{entrypoint}_length_is_one" ) ;
55
- let length_is_gt_one = format ! ( "{entrypoint}_length_is_gt_one" ) ;
56
61
let bagging_loop = format ! ( "{entrypoint}_loop" ) ;
57
62
58
- let Digest ( zero_digest) = bag_peaks ( & [ ] ) ;
63
+ let destructure_mmra = MmrAccumulator :: destructure ( ) ;
64
+ let pop_count = library. import ( Box :: new ( arithmetic:: u64:: popcount:: PopCount ) ) ;
59
65
60
66
triton_asm ! (
61
- // BEFORE: _ *peaks
67
+ // BEFORE: _ *mmra
62
68
// AFTER: _ [bagged_peaks: Digest]
63
69
{ entrypoint} :
64
- dup 0 read_mem 1 pop 1
65
- // _ *peaks length
70
+ { & destructure_mmra }
71
+ // _ *peaks *leaf_count
66
72
67
- push { MAX_MMR_HEIGHT }
68
- dup 1
69
- lt
70
- assert
73
+ addi 1 read_mem 2 pop 1
74
+ hint leaf_count : u64 = stack[ 0 ..2 ]
75
+ // _ *peaks [leaf_count]
71
76
72
- /* special case: length is 0 */
73
- push 1
74
- dup 1 push 0 eq
75
- // _ *peaks length 1 (length==0)
77
+ dup 1 dup 1
78
+ call { pop_count}
79
+ // _ *peaks [leaf_count] popcount
76
80
77
- skiz call { length_is_zero }
78
- skiz call { length_is_not_zero }
81
+ dup 3 read_mem 1 pop 1
82
+ // _ *peaks [leaf_count] popcount num_peaks
79
83
80
- // _ [bagged_peaks: Digest]
81
- return
84
+ dup 1 eq
85
+ // _ *peaks [leaf_count] pop_count (num_peaks==pop_count)
82
86
83
- // BEFORE: _ *peaks length 1
84
- // AFTER: _ [bagged_peaks: Digest] 0
85
- { length_is_zero} :
86
- pop 3
87
+ assert error_id { Self :: INCONSISTENT_PEAKS_NUM }
88
+ hint num_peaks: u32 = stack[ 0 ]
89
+ // _ *peaks [leaf_count] num_peaks
87
90
88
- push { zero_digest[ 4 ] }
89
- push { zero_digest[ 3 ] }
90
- push { zero_digest[ 2 ] }
91
- push { zero_digest[ 1 ] }
92
- push { zero_digest[ 0 ] }
93
- hint bag_of_no_peaks: Digest = stack[ 0 ..5 ]
91
+ place 2
92
+ // _ *peaks num_peaks [leaf_count]
93
+ // _ *peaks len [leaf_count] <-- rename
94
94
95
95
push 0
96
- return
97
-
98
- // BEFORE: _ *peaks length
99
- // AFTER: _ [bagged_peaks: Digest]
100
- { length_is_not_zero} :
101
- /* special case: length is 1 */
102
- push 1
103
- dup 1 push 1 eq
104
- // _ *peaks length 1 (length==1)
105
-
106
- skiz call { length_is_one}
107
- skiz call { length_is_gt_one}
108
- return
109
-
110
- // BEFORE: _ *peaks length 1
111
- // AFTER: _ [bagged_peaks: Digest] 0
112
- { length_is_one} :
113
- pop 2
114
-
115
- push { Digest :: LEN } add
116
- // _ *peaks[0]_lw
117
-
118
- read_mem { Digest :: LEN }
119
- pop 1
120
-
121
96
push 0
122
- return
97
+ push 0
98
+ push 0
99
+ push 0
100
+ push 0
101
+ push 0
102
+ push 0
103
+ pick 9
104
+ pick 9
105
+ // _ *peaks len 0 0 0 0 0 0 0 0 [leaf_count; 2]
123
106
124
- // BEFORE: _ *peaks length
125
- // AFTER: _ [bagged_peaks: Digest ]
126
- { length_is_gt_one } :
127
- /* Get pointer to last word of peaks list */
107
+ hash
108
+ // _ *peaks len [hash_of_leaf_count ]
109
+
110
+ pick 5
128
111
push { Digest :: LEN }
129
112
mul
130
- // _ *peaks offset
113
+ // _ *peaks [hash_of_leaf_count] size_of_peaks_list
131
114
132
- dup 1
115
+ dup 6
133
116
add
134
- // _ *peaks *peaks[last]_lw
135
-
136
- read_mem { Digest :: LEN }
137
- // _ *peaks [peaks[last]: Digest] *peaks[last-1]_lw
117
+ // _ *peaks [hash_of_leaf_count] *peaks[last]_lw
138
118
139
119
place 5
140
- // _ *peaks *peaks[last-1]_lw [peaks[last]: Digest]
141
- // _ *peaks *peaks[last-1]_lw [acc: Digest]
120
+ // _ *peaks *peaks[last]_lw [hash_of_leaf_count]
121
+
122
+ dup 6 dup 6 eq push 0 eq
123
+ // _ *peaks *peaks[last]_lw [hash_of_leaf_count] (num_peaks == 0)
142
124
143
- call { bagging_loop}
144
- // *peaks *peaks [acc: Digest]
145
- // *peaks *peaks [bagged_peaks: Digest]
125
+ skiz call { bagging_loop}
126
+ // _ *peaks *peaks [bag_hash]
146
127
147
- pick 6
148
- pick 6
149
- pop 2
128
+ pick 6 pick 6 pop 2
129
+ // _ [bagged_peaks: Digest]
150
130
151
131
return
152
132
@@ -165,29 +145,31 @@ impl BasicSnippet for BagPeaks {
165
145
}
166
146
167
147
fn sign_offs ( & self ) -> HashMap < Reviewer , SignOffFingerprint > {
168
- let mut sign_offs = HashMap :: new ( ) ;
169
- sign_offs. insert ( Reviewer ( "ferdinand" ) , 0xaf79abb21cb46bf . into ( ) ) ;
170
- sign_offs
148
+ [ ] . into ( )
171
149
}
172
150
}
173
151
174
152
#[ cfg( test) ]
175
153
mod tests {
176
154
use std:: collections:: HashMap ;
177
155
156
+ use triton_vm:: twenty_first:: prelude:: Mmr ;
178
157
use twenty_first:: math:: other:: random_elements;
179
158
180
159
use super :: * ;
181
160
use crate :: test_prelude:: * ;
182
161
183
162
impl BagPeaks {
184
- fn set_up_initial_state ( & self , num_peaks : usize ) -> FunctionInitialState {
185
- let address = random ( ) ;
163
+ fn set_up_initial_state ( & self , leaf_count : u64 ) -> FunctionInitialState {
164
+ let address = rand :: random ( ) ;
186
165
let mut stack = self . init_stack_for_isolated_run ( ) ;
187
166
push_encodable ( & mut stack, & address) ;
188
167
189
168
let mut memory = HashMap :: new ( ) ;
190
- encode_to_memory ( & mut memory, address, & random_elements :: < Digest > ( num_peaks) ) ;
169
+ let num_peaks = leaf_count. count_ones ( ) ;
170
+ let mmra =
171
+ MmrAccumulator :: init ( random_elements :: < Digest > ( num_peaks as usize ) , leaf_count) ;
172
+ encode_to_memory ( & mut memory, address, & mmra) ;
191
173
192
174
FunctionInitialState { stack, memory }
193
175
}
@@ -200,28 +182,45 @@ mod tests {
200
182
memory : & mut HashMap < BFieldElement , BFieldElement > ,
201
183
) {
202
184
let address = pop_encodable ( stack) ;
203
- let peaks = * Vec :: < Digest > :: decode_from_memory ( memory, address) . unwrap ( ) ;
204
- push_encodable ( stack, & bag_peaks ( & peaks) ) ;
185
+ let mmra = * MmrAccumulator :: decode_from_memory ( memory, address) . unwrap ( ) ;
186
+
187
+ fn bag_peaks ( peaks : & [ Digest ] , leaf_count : u64 ) -> Digest {
188
+ // use `hash_10` over `hash` or `hash_varlen` to simplify hashing in Triton VM
189
+ let [ lo_limb, hi_limb] = leaf_count. encode ( ) [ ..] else {
190
+ panic ! ( "internal error: unknown encoding of type `u64`" )
191
+ } ;
192
+ let padded_leaf_count = bfe_array ! [ lo_limb, hi_limb, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ;
193
+ let hashed_leaf_count = Digest :: new ( Tip5 :: hash_10 ( & padded_leaf_count) ) ;
194
+
195
+ peaks
196
+ . iter ( )
197
+ . rev ( )
198
+ . fold ( hashed_leaf_count, |acc, & peak| Tip5 :: hash_pair ( peak, acc) )
199
+ }
200
+
201
+ let bag = bag_peaks ( & mmra. peaks ( ) , mmra. num_leafs ( ) ) ;
202
+ println ! ( "bag: {bag}" ) ;
203
+ push_encodable ( stack, & bag) ;
205
204
}
206
205
207
206
fn pseudorandom_initial_state (
208
207
& self ,
209
208
seed : [ u8 ; 32 ] ,
210
209
bench_case : Option < BenchmarkCase > ,
211
210
) -> FunctionInitialState {
212
- let num_peaks = match bench_case {
213
- Some ( BenchmarkCase :: CommonCase ) => 30 ,
214
- Some ( BenchmarkCase :: WorstCase ) => 60 ,
215
- None => StdRng :: from_seed ( seed) . gen_range ( 0 ..= 63 ) ,
211
+ let num_leafs = match bench_case {
212
+ Some ( BenchmarkCase :: CommonCase ) => 348753 ,
213
+ Some ( BenchmarkCase :: WorstCase ) => 843759843768 ,
214
+ None => StdRng :: from_seed ( seed) . random_range ( 0u64 .. ( u64 :: MAX >> 1 ) ) ,
216
215
} ;
217
216
218
- self . set_up_initial_state ( num_peaks )
217
+ self . set_up_initial_state ( num_leafs )
219
218
}
220
219
221
220
fn corner_case_initial_states ( & self ) -> Vec < FunctionInitialState > {
222
221
( 0 ..=5 )
223
222
. chain ( [ 63 ] )
224
- . map ( |num_peaks| self . set_up_initial_state ( num_peaks) )
223
+ . map ( |num_peaks| self . set_up_initial_state ( ( 1 << num_peaks) - 1 ) )
225
224
. collect ( )
226
225
}
227
226
}
0 commit comments