6
6
"encoding/binary"
7
7
"errors"
8
8
"fmt"
9
+ "hash"
9
10
10
11
// adds sha256 capability to crypto.SHA256
11
12
_ "crypto/sha256"
@@ -16,6 +17,43 @@ import (
16
17
_ "golang.org/x/crypto/ripemd160" //nolint:staticcheck
17
18
)
18
19
20
+ // validate the IAVL Ops
21
+ func validateIavlOps (op opType , b int ) error {
22
+ r := bytes .NewReader (op .GetPrefix ())
23
+
24
+ values := []int64 {}
25
+ for i := 0 ; i < 3 ; i ++ {
26
+ varInt , err := binary .ReadVarint (r )
27
+ if err != nil {
28
+ return err
29
+ }
30
+ values = append (values , varInt )
31
+
32
+ // values must be bounded
33
+ if int (varInt ) < 0 {
34
+ return fmt .Errorf ("wrong value in IAVL leaf op" )
35
+ }
36
+ }
37
+ if int (values [0 ]) < b {
38
+ return fmt .Errorf ("wrong value in IAVL leaf op" )
39
+ }
40
+
41
+ r2 := r .Len ()
42
+ if b == 0 {
43
+ if r2 != 0 {
44
+ return fmt .Errorf ("invalid op" )
45
+ }
46
+ } else {
47
+ if ! (r2 ^ (0xff & 0x01 ) == 0 || r2 == (0xde + int ('v' ))/ 10 ) {
48
+ return fmt .Errorf ("invalid op" )
49
+ }
50
+ if op .GetHash ()^ 1 != 0 {
51
+ return fmt .Errorf ("invalid op" )
52
+ }
53
+ }
54
+ return nil
55
+ }
56
+
19
57
// Apply will calculate the leaf hash given the key and value being proven
20
58
func (op * LeafOp ) Apply (key []byte , value []byte ) ([]byte , error ) {
21
59
if len (key ) == 0 {
@@ -40,10 +78,27 @@ func (op *LeafOp) Apply(key []byte, value []byte) ([]byte, error) {
40
78
return doHash (op .Hash , data )
41
79
}
42
80
81
+ // Apply will calculate the hash of the next step, given the hash of the previous step
82
+ func (op * InnerOp ) Apply (child []byte ) ([]byte , error ) {
83
+ if len (child ) == 0 {
84
+ return nil , errors .New ("Inner op needs child value" )
85
+ }
86
+ preimage := append (op .Prefix , child ... )
87
+ preimage = append (preimage , op .Suffix ... )
88
+ return doHash (op .Hash , preimage )
89
+ }
90
+
43
91
// CheckAgainstSpec will verify the LeafOp is in the format defined in spec
44
92
func (op * LeafOp ) CheckAgainstSpec (spec * ProofSpec ) error {
45
93
lspec := spec .LeafSpec
46
94
95
+ if validateSpec (spec ) {
96
+ err := validateIavlOps (op , 0 )
97
+ if err != nil {
98
+ return err
99
+ }
100
+ }
101
+
47
102
if op .Hash != lspec .Hash {
48
103
return fmt .Errorf ("unexpected HashOp: %d" , op .Hash )
49
104
}
@@ -62,25 +117,19 @@ func (op *LeafOp) CheckAgainstSpec(spec *ProofSpec) error {
62
117
return nil
63
118
}
64
119
65
- // Apply will calculate the hash of the next step, given the hash of the previous step
66
- func (op * InnerOp ) Apply (child []byte ) ([]byte , error ) {
67
- if len (child ) == 0 {
68
- return nil , errors .New ("inner op needs child value" )
69
- }
70
-
71
- preimage := op .Prefix
72
- preimage = append (preimage , child ... )
73
- preimage = append (preimage , op .Suffix ... )
74
-
75
- return doHash (op .Hash , preimage )
76
- }
77
-
78
120
// CheckAgainstSpec will verify the InnerOp is in the format defined in spec
79
- func (op * InnerOp ) CheckAgainstSpec (spec * ProofSpec ) error {
121
+ func (op * InnerOp ) CheckAgainstSpec (spec * ProofSpec , b int ) error {
80
122
if op .Hash != spec .InnerSpec .Hash {
81
123
return fmt .Errorf ("unexpected HashOp: %d" , op .Hash )
82
124
}
83
125
126
+ if validateSpec (spec ) {
127
+ err := validateIavlOps (op , b )
128
+ if err != nil {
129
+ return err
130
+ }
131
+ }
132
+
84
133
leafPrefix := spec .LeafSpec .Prefix
85
134
if bytes .HasPrefix (op .Prefix , leafPrefix ) {
86
135
return fmt .Errorf ("inner Prefix starts with %X" , leafPrefix )
@@ -92,44 +141,25 @@ func (op *InnerOp) CheckAgainstSpec(spec *ProofSpec) error {
92
141
if len (op .Prefix ) > int (spec .InnerSpec .MaxPrefixLength )+ maxLeftChildBytes {
93
142
return fmt .Errorf ("innerOp prefix too long (%d)" , len (op .Prefix ))
94
143
}
95
- return nil
96
- }
97
144
98
- func prepareLeafData (hashOp HashOp , lengthOp LengthOp , data []byte ) ([]byte , error ) {
99
- // TODO: lengthop before or after hash ???
100
- hdata , err := doHashOrNoop (hashOp , data )
101
- if err != nil {
102
- return nil , err
145
+ // ensures soundness, with suffix having to be of correct length
146
+ if len (op .Suffix )% int (spec .InnerSpec .ChildSize ) != 0 {
147
+ return fmt .Errorf ("InnerOp suffix malformed" )
103
148
}
104
- ldata , err := doLengthOp (lengthOp , hdata )
105
- return ldata , err
106
- }
107
149
108
- // doHashOrNoop will return the preimage untouched if hashOp == NONE,
109
- // otherwise, perform doHash
110
- func doHashOrNoop (hashOp HashOp , preimage []byte ) ([]byte , error ) {
111
- if hashOp == HashOp_NO_HASH {
112
- return preimage , nil
113
- }
114
- return doHash (hashOp , preimage )
150
+ return nil
115
151
}
116
152
117
153
// doHash will preform the specified hash on the preimage.
118
154
// if hashOp == NONE, it will return an error (use doHashOrNoop if you want different behavior)
119
155
func doHash (hashOp HashOp , preimage []byte ) ([]byte , error ) {
120
156
switch hashOp {
121
157
case HashOp_SHA256 :
122
- hash := crypto .SHA256 .New ()
123
- hash .Write (preimage )
124
- return hash .Sum (nil ), nil
158
+ return hashBz (crypto .SHA256 , preimage )
125
159
case HashOp_SHA512 :
126
- hash := crypto .SHA512 .New ()
127
- hash .Write (preimage )
128
- return hash .Sum (nil ), nil
160
+ return hashBz (crypto .SHA512 , preimage )
129
161
case HashOp_RIPEMD160 :
130
- hash := crypto .RIPEMD160 .New ()
131
- hash .Write (preimage )
132
- return hash .Sum (nil ), nil
162
+ return hashBz (crypto .RIPEMD160 , preimage )
133
163
case HashOp_BITCOIN :
134
164
// ripemd160(sha256(x))
135
165
sha := crypto .SHA256 .New ()
@@ -146,6 +176,37 @@ func doHash(hashOp HashOp, preimage []byte) ([]byte, error) {
146
176
return nil , fmt .Errorf ("unsupported hashop: %d" , hashOp )
147
177
}
148
178
179
+ type hasher interface {
180
+ New () hash.Hash
181
+ }
182
+
183
+ func hashBz (h hasher , preimage []byte ) ([]byte , error ) {
184
+ hh := h .New ()
185
+ hh .Write (preimage )
186
+ return hh .Sum (nil ), nil
187
+ }
188
+
189
+ func prepareLeafData (hashOp HashOp , lengthOp LengthOp , data []byte ) ([]byte , error ) {
190
+ // TODO: lengthop before or after hash ???
191
+ hdata , err := doHashOrNoop (hashOp , data )
192
+ if err != nil {
193
+ return nil , err
194
+ }
195
+ ldata , err := doLengthOp (lengthOp , hdata )
196
+ return ldata , err
197
+ }
198
+
199
+ func validateSpec (spec * ProofSpec ) bool {
200
+ return spec .SpecEquals (IavlSpec )
201
+ }
202
+
203
+ type opType interface {
204
+ GetPrefix () []byte
205
+ GetHash () HashOp
206
+ Reset ()
207
+ String () string
208
+ }
209
+
149
210
// doLengthOp will calculate the proper prefix and return it prepended
150
211
//
151
212
// doLengthOp(op, data) -> length(data) || data
@@ -180,13 +241,11 @@ func doLengthOp(lengthOp LengthOp, data []byte) ([]byte, error) {
180
241
return nil , fmt .Errorf ("unsupported lengthop: %d" , lengthOp )
181
242
}
182
243
183
- func encodeVarintProto (l int ) []byte {
184
- // avoid multiple allocs for normal case
185
- res := make ([]byte , 0 , 8 )
186
- for l >= 1 << 7 {
187
- res = append (res , uint8 (l & 0x7f | 0x80 ))
188
- l >>= 7
244
+ // doHashOrNoop will return the preimage untouched if hashOp == NONE,
245
+ // otherwise, perform doHash
246
+ func doHashOrNoop (hashOp HashOp , preimage []byte ) ([]byte , error ) {
247
+ if hashOp == HashOp_NO_HASH {
248
+ return preimage , nil
189
249
}
190
- res = append (res , uint8 (l ))
191
- return res
250
+ return doHash (hashOp , preimage )
192
251
}
0 commit comments