1
+ use std:: collections:: HashMap ;
2
+
1
3
use triton_vm:: prelude:: * ;
2
4
3
5
use crate :: prelude:: * ;
6
+ use crate :: traits:: basic_snippet:: Reviewer ;
7
+ use crate :: traits:: basic_snippet:: SignOffFingerprint ;
4
8
5
- pub const MERKLE_AUTHENTICATION_ROOT_MISMATCH_ERROR : i128 = 2 ;
6
-
7
- /// Verify membership in a Merkle tree.
9
+ /// Verify membership in a [Merkle tree](twenty_first::prelude::MerkleTree).
10
+ ///
11
+ /// Verify that a leaf lives in a Merkle tree, given the tree's root, its
12
+ /// height, the leaf's index, and the leaf itself. The authentication path is
13
+ /// non-deterministically divined. This algorithm asserts that the leaf is a
14
+ /// member of the tree; phrased differently, if membership could not be
15
+ /// established, it crashes the VM.
16
+ ///
17
+ /// ### Behavior
18
+ ///
19
+ /// ```text
20
+ /// BEFORE: _ [root: Digest] tree_height leaf_index [leaf: Digest]
21
+ /// AFTER: _
22
+ /// ```
23
+ ///
24
+ /// ### Preconditions
25
+ ///
26
+ /// - all input arguments are properly [`BFieldCodec`] encoded
27
+ ///
28
+ /// ### Postconditions
8
29
///
9
- /// MerkleVerify -- verify that a leaf lives in a Merkle tree,
10
- /// given the root, leaf index, and leaf. The authentication path
11
- /// is non-deterministically divined. This algorithm asserts
12
- /// that the path is valid; phrased differently, it crashes the
13
- /// VM if it is not.
30
+ /// None.
14
31
#[ derive( Clone , Debug ) ]
15
32
pub struct MerkleVerify ;
16
33
34
+ impl MerkleVerify {
35
+ pub const TREE_TOO_HIGH_ERROR_ID : i128 = 0 ;
36
+ pub const OUT_OF_BOUNDS_LEAF_ERROR_ID : i128 = 1 ;
37
+ pub const ROOT_MISMATCH_ERROR_ID : i128 = 2 ;
38
+ }
39
+
17
40
impl BasicSnippet for MerkleVerify {
18
41
fn inputs ( & self ) -> Vec < ( DataType , String ) > {
19
42
vec ! [
@@ -32,7 +55,7 @@ impl BasicSnippet for MerkleVerify {
32
55
"tasmlib_hashing_merkle_verify" . to_string ( )
33
56
}
34
57
35
- fn code ( & self , _library : & mut Library ) -> Vec < LabelledInstruction > {
58
+ fn code ( & self , _ : & mut Library ) -> Vec < LabelledInstruction > {
36
59
let entrypoint = self . entrypoint ( ) ;
37
60
let traverse_tree = format ! ( "{entrypoint}_traverse_tree" ) ;
38
61
let tree_height_is_not_zero = format ! ( "{entrypoint}_tree_height_is_not_zero" ) ;
@@ -42,15 +65,19 @@ impl BasicSnippet for MerkleVerify {
42
65
{ entrypoint} :
43
66
/* Assert reasonable tree height
44
67
*
45
- * Don't rely on `pow`'s `is_u32` and `assert leaf_index < num_leaves` only:
46
- * Since bfe!(2)^3784253760 == 1 ∧ 3784253760 < 4294967295 == u32::MAX, weird
47
- * things are possible. It is not immediately obvious how this translates into
48
- * an attack, but there's no point in leaving a potential attack vector open.
68
+ * Don't rely only on
69
+ * 1. `pow`'s implicit check that the exponent is a u32,
70
+ * 2. `assert leaf_index < num_leaves`.
71
+ * Since bfe!(2)^192 == 1 and 192 < u32::MAX, weird things are possible. For
72
+ * example, the number of leafs for a tree of height 193 would incorrectly be
73
+ * computed as 2.
74
+ * Any attack would probably still require a hash collision to work, but there's
75
+ * no point in leaving a potential attack vector open.
49
76
*/
50
77
push 32
51
78
dup 7
52
79
lt
53
- assert error_id 0
80
+ assert error_id { Self :: TREE_TOO_HIGH_ERROR_ID }
54
81
55
82
/* Calculate node index from tree height and leaf index */
56
83
dup 6
@@ -61,7 +88,7 @@ impl BasicSnippet for MerkleVerify {
61
88
dup 0 dup 7 lt
62
89
// _ [root; 5] tree_height leaf_index [leaf; 5] num_leaves (leaf_index < num_leaves)
63
90
64
- assert error_id 1
91
+ assert error_id { Self :: OUT_OF_BOUNDS_LEAF_ERROR_ID }
65
92
// _ [root; 5] tree_height leaf_index [leaf; 5] num_leaves
66
93
67
94
pick 6
@@ -71,43 +98,44 @@ impl BasicSnippet for MerkleVerify {
71
98
place 5
72
99
// _ [root; 5] tree_height node_index [leaf; 5]
73
100
74
- dup 6
101
+ pick 6
75
102
skiz
76
103
call { tree_height_is_not_zero}
77
-
78
- // _ [root; 5] [0|1] [0|1] [calculated_root; 5]
104
+ // _ [root; 5] [0|1] [calculated_root; 5]
79
105
80
106
/* compare calculated and provided root */
81
-
82
107
pick 5
83
- pick 6
84
- pop 2
85
- // _ [root; 5] [calculated_root; 5]
86
-
87
- assert_vector error_id { MERKLE_AUTHENTICATION_ROOT_MISMATCH_ERROR }
108
+ pop 1
109
+ assert_vector error_id { Self :: ROOT_MISMATCH_ERROR_ID }
88
110
pop 5
89
111
90
112
return
91
113
114
+ // BEFORE: _ node_index [leaf; 5]
92
115
{ tree_height_is_not_zero} :
93
- // _ [root; 5] tree_height node_index [leaf; 5]
94
-
95
116
push 1
96
- swap 7
97
- pop 1
98
- // _ [root; 5] 1 node_index [leaf; 5]
117
+ place 6
118
+ // _ 1 node_index [leaf; 5]
99
119
100
120
call { traverse_tree}
101
- // _ [root; 5] 1 1 [calculated_root; 5]
121
+ // _ 1 1 [calculated_root; 5]
122
+
123
+ pick 6
124
+ pop 1
102
125
103
126
return
104
127
105
128
{ traverse_tree} :
106
129
merkle_step
107
130
recurse_or_return
108
-
109
131
)
110
132
}
133
+
134
+ fn sign_offs ( & self ) -> HashMap < Reviewer , SignOffFingerprint > {
135
+ let mut sign_offs = HashMap :: new ( ) ;
136
+ sign_offs. insert ( Reviewer ( "ferdinand" ) , 0x54be0725136e609e . into ( ) ) ;
137
+ sign_offs
138
+ }
111
139
}
112
140
113
141
#[ cfg( test) ]
@@ -173,17 +201,18 @@ mod tests {
173
201
let leaf = rng. gen ( ) ;
174
202
175
203
// walk up tree to calculate root
176
- let mut node_digest = leaf;
204
+ let mut current_node = leaf;
177
205
let mut node_index = leaf_index + num_leaves;
178
206
for & sibling in & path {
179
207
let node_is_left_sibling = node_index % 2 == 0 ;
180
- node_digest = match node_is_left_sibling {
181
- true => Tip5 :: hash_pair ( node_digest, sibling) ,
182
- false => Tip5 :: hash_pair ( sibling, node_digest) ,
208
+ current_node = if node_is_left_sibling {
209
+ Tip5 :: hash_pair ( current_node, sibling)
210
+ } else {
211
+ Tip5 :: hash_pair ( sibling, current_node)
183
212
} ;
184
213
node_index /= 2 ;
185
214
}
186
- let root = node_digest ;
215
+ let root = current_node ;
187
216
188
217
let mut stack = Self . init_stack_for_isolated_run ( ) ;
189
218
push_encodable ( & mut stack, & root) ;
@@ -286,10 +315,14 @@ mod tests {
286
315
. digests
287
316
. extend ( additional_bogus_tree_nodes) ;
288
317
318
+ let expected_errors = [
319
+ MerkleVerify :: OUT_OF_BOUNDS_LEAF_ERROR_ID ,
320
+ MerkleVerify :: ROOT_MISMATCH_ERROR_ID ,
321
+ ] ;
289
322
test_assertion_failure (
290
323
& ShadowedReadOnlyAlgorithm :: new ( MerkleVerify ) ,
291
324
initial_state. into ( ) ,
292
- & [ 1 , 2 ] ,
325
+ & expected_errors ,
293
326
) ;
294
327
}
295
328
@@ -306,7 +339,7 @@ mod tests {
306
339
test_assertion_failure (
307
340
& ShadowedReadOnlyAlgorithm :: new ( MerkleVerify ) ,
308
341
initial_state. into ( ) ,
309
- & [ 0 ] ,
342
+ & [ MerkleVerify :: TREE_TOO_HIGH_ERROR_ID ] ,
310
343
) ;
311
344
}
312
345
@@ -342,7 +375,7 @@ mod tests {
342
375
test_assertion_failure (
343
376
& ShadowedReadOnlyAlgorithm :: new ( MerkleVerify ) ,
344
377
initial_state. into ( ) ,
345
- & [ 2 ] ,
378
+ & [ MerkleVerify :: ROOT_MISMATCH_ERROR_ID ] ,
346
379
) ;
347
380
}
348
381
}
0 commit comments