github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/iavl/proof_test.go (about) 1 package iavl 2 3 import ( 4 "bytes" 5 "errors" 6 "testing" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 11 "github.com/gnolang/gno/tm2/pkg/amino" 12 "github.com/gnolang/gno/tm2/pkg/db/memdb" 13 "github.com/gnolang/gno/tm2/pkg/random" 14 "github.com/gnolang/gno/tm2/pkg/testutils" 15 ) 16 17 func TestTreeGetWithProof(t *testing.T) { 18 t.Parallel() 19 20 tree := NewMutableTree(memdb.NewMemDB(), 0) 21 require := require.New(t) 22 for _, ikey := range []byte{0x11, 0x32, 0x50, 0x72, 0x99} { 23 key := []byte{ikey} 24 tree.Set(key, []byte(random.RandStr(8))) 25 } 26 root := tree.WorkingHash() 27 28 key := []byte{0x32} 29 val, proof, err := tree.GetWithProof(key) 30 require.NoError(err) 31 require.NotEmpty(val) 32 require.NotNil(proof) 33 err = proof.VerifyItem(key, val) 34 require.Error(err, "%+v", err) // Verifying item before calling Verify(root) 35 err = proof.Verify(root) 36 require.NoError(err, "%+v", err) 37 err = proof.VerifyItem(key, val) 38 require.NoError(err, "%+v", err) 39 40 key = []byte{0x1} 41 val, proof, err = tree.GetWithProof(key) 42 require.NoError(err) 43 require.Empty(val) 44 require.NotNil(proof) 45 err = proof.VerifyAbsence(key) 46 require.Error(err, "%+v", err) // Verifying absence before calling Verify(root) 47 err = proof.Verify(root) 48 require.NoError(err, "%+v", err) 49 err = proof.VerifyAbsence(key) 50 require.NoError(err, "%+v", err) 51 } 52 53 func TestTreeKeyExistsProof(t *testing.T) { 54 t.Parallel() 55 56 tree := NewMutableTree(memdb.NewMemDB(), 0) 57 root := tree.WorkingHash() 58 59 // should get false for proof with nil root 60 proof, keys, values, err := tree.getRangeProof([]byte("foo"), nil, 1) 61 assert.Nil(t, proof) 62 assert.Error(t, proof.Verify(root)) 63 assert.Nil(t, keys) 64 assert.Nil(t, values) 65 assert.NoError(t, err) 66 67 // insert lots of info and store the bytes 68 allkeys := make([][]byte, 200) 69 for i := 0; i < 200; i++ { 70 key := random.RandStr(20) 71 value := "value_for_" + key 72 tree.Set([]byte(key), []byte(value)) 73 allkeys[i] = []byte(key) 74 } 75 sortByteSlices(allkeys) // Sort all keys 76 root = tree.WorkingHash() 77 78 // query random key fails 79 proof, _, _, err = tree.getRangeProof([]byte("foo"), nil, 2) 80 assert.Nil(t, err) 81 assert.Nil(t, proof.Verify(root)) 82 assert.Nil(t, proof.VerifyAbsence([]byte("foo")), proof.String()) 83 84 // query min key fails 85 proof, _, _, err = tree.getRangeProof([]byte{0x00}, []byte{0x01}, 2) 86 assert.Nil(t, err) 87 assert.Nil(t, proof.Verify(root)) 88 assert.Nil(t, proof.VerifyAbsence([]byte{0x00})) 89 90 // valid proof for real keys 91 for i, key := range allkeys { 92 var keys, values [][]byte 93 proof, keys, values, err = tree.getRangeProof(key, nil, 2) 94 require.Nil(t, err) 95 96 require.Equal(t, 97 append([]byte("value_for_"), key...), 98 values[0], 99 ) 100 require.Equal(t, key, keys[0]) 101 require.Nil(t, proof.Verify(root)) 102 require.Nil(t, proof.VerifyAbsence(cpIncr(key))) 103 require.Equal(t, 1, len(keys), proof.String()) 104 require.Equal(t, 1, len(values), proof.String()) 105 if i < len(allkeys)-1 { 106 if i < len(allkeys)-2 { 107 // No last item... not a proof of absence of large key. 108 require.NotNil(t, proof.VerifyAbsence(bytes.Repeat([]byte{0xFF}, 20)), proof.String()) 109 } else { 110 // Last item is included. 111 require.Nil(t, proof.VerifyAbsence(bytes.Repeat([]byte{0xFF}, 20))) 112 } 113 } else { 114 // last item of tree... valid proof of absence of large key. 115 require.Nil(t, proof.VerifyAbsence(bytes.Repeat([]byte{0xFF}, 20))) 116 } 117 } 118 // TODO: Test with single value in tree. 119 } 120 121 func TestTreeKeyInRangeProofs(t *testing.T) { 122 t.Parallel() 123 124 tree := NewMutableTree(memdb.NewMemDB(), 0) 125 require := require.New(t) 126 keys := []byte{0x0a, 0x11, 0x2e, 0x32, 0x50, 0x72, 0x99, 0xa1, 0xe4, 0xf7} // 10 total. 127 for _, ikey := range keys { 128 key := []byte{ikey} 129 tree.Set(key, key) 130 } 131 root := tree.WorkingHash() 132 133 // For spacing: 134 T := 10 135 // disable: don't use underscores in Go names; var nil______ should be nil (golint) 136 nil______ := []byte(nil) 137 138 cases := []struct { 139 start byte 140 end byte 141 pkeys []byte // proof keys, one byte per key. 142 vals []byte // keys and values, one byte per key. 143 lidx int64 // proof left index (index of first proof key). 144 pnc bool // does panic 145 }{ 146 {start: 0x0a, end: 0xf7, pkeys: keys[0:T], vals: keys[0:9], lidx: 0}, // #0 147 {start: 0x0a, end: 0xf8, pkeys: keys[0:T], vals: keys[0:T], lidx: 0}, // #1 148 {start: 0x00, end: 0xff, pkeys: keys[0:T], vals: keys[0:T], lidx: 0}, // #2 149 {start: 0x14, end: 0xe4, pkeys: keys[1:9], vals: keys[2:8], lidx: 1}, // #3 150 {start: 0x14, end: 0xe5, pkeys: keys[1:9], vals: keys[2:9], lidx: 1}, // #4 151 {start: 0x14, end: 0xe6, pkeys: keys[1:T], vals: keys[2:9], lidx: 1}, // #5 152 {start: 0x14, end: 0xf1, pkeys: keys[1:T], vals: keys[2:9], lidx: 1}, // #6 153 {start: 0x14, end: 0xf7, pkeys: keys[1:T], vals: keys[2:9], lidx: 1}, // #7 154 {start: 0x14, end: 0xff, pkeys: keys[1:T], vals: keys[2:T], lidx: 1}, // #8 155 {start: 0x2e, end: 0x31, pkeys: keys[2:4], vals: keys[2:3], lidx: 2}, // #9 156 {start: 0x2e, end: 0x32, pkeys: keys[2:4], vals: keys[2:3], lidx: 2}, // #10 157 {start: 0x2f, end: 0x32, pkeys: keys[2:4], vals: nil______, lidx: 2}, // #11 158 {start: 0x2e, end: 0x31, pkeys: keys[2:4], vals: keys[2:3], lidx: 2}, // #12 159 {start: 0x2e, end: 0x2f, pkeys: keys[2:3], vals: keys[2:3], lidx: 2}, // #13 160 {start: 0x12, end: 0x31, pkeys: keys[1:4], vals: keys[2:3], lidx: 1}, // #14 161 {start: 0xf8, end: 0xff, pkeys: keys[9:T], vals: nil______, lidx: 9}, // #15 162 {start: 0x12, end: 0x20, pkeys: keys[1:3], vals: nil______, lidx: 1}, // #16 163 {start: 0x00, end: 0x09, pkeys: keys[0:1], vals: nil______, lidx: 0}, // #17 164 {start: 0xf7, end: 0x00, pnc: true}, // #18 165 {start: 0xf8, end: 0x00, pnc: true}, // #19 166 {start: 0x10, end: 0x10, pnc: true}, // #20 167 {start: 0x12, end: 0x12, pnc: true}, // #21 168 {start: 0xff, end: 0xf7, pnc: true}, // #22 169 } 170 171 // fmt.Println("PRINT TREE") 172 // printNode(tree.ndb, tree.root, 0) 173 // fmt.Println("PRINT TREE END") 174 175 for i, c := range cases { 176 t.Logf("case %v", i) 177 start := []byte{c.start} 178 end := []byte{c.end} 179 180 if c.pnc { 181 require.Panics(func() { tree.GetRangeWithProof(start, end, 0) }) 182 continue 183 } 184 185 // Compute range proof. 186 keys, values, proof, err := tree.GetRangeWithProof(start, end, 0) 187 require.NoError(err, "%+v", err) 188 require.Equal(c.pkeys, flatten(proof.Keys())) 189 require.Equal(c.vals, flatten(keys)) 190 require.Equal(c.vals, flatten(values)) 191 require.Equal(c.lidx, proof.LeftIndex()) 192 193 // Verify that proof is valid. 194 err = proof.Verify(root) 195 require.NoError(err, "%+v", err) 196 verifyProof(t, proof, root) 197 198 // Verify each value of pkeys. 199 for _, key := range c.pkeys { 200 err := proof.VerifyItem([]byte{key}, []byte{key}) 201 require.NoError(err) 202 } 203 204 // Verify each value of vals. 205 for _, key := range c.vals { 206 err := proof.VerifyItem([]byte{key}, []byte{key}) 207 require.NoError(err) 208 } 209 } 210 } 211 212 func verifyProof(t *testing.T, proof *RangeProof, root []byte) { 213 t.Helper() 214 215 // Proof must verify. 216 require.NoError(t, proof.Verify(root)) 217 218 // Write/Read then verify. 219 cdc := amino.NewCodec() 220 proofBytes := cdc.MustMarshalSized(proof) 221 proof2 := new(RangeProof) 222 err := cdc.UnmarshalSized(proofBytes, proof2) 223 require.Nil(t, err, "Failed to read KeyExistsProof from bytes: %v", err) 224 225 // Random mutations must not verify 226 for i := 0; i < 1e4; i++ { 227 badProofBytes := testutils.MutateByteSlice(proofBytes) 228 badProof := new(RangeProof) 229 err := cdc.UnmarshalSized(badProofBytes, badProof) 230 if err != nil { 231 continue // couldn't even decode. 232 } 233 // re-encode to make sure it's actually different. 234 badProofBytes2 := cdc.MustMarshalSized(badProof) 235 if bytes.Equal(proofBytes, badProofBytes2) { 236 continue // didn't mutate successfully. 237 } 238 // may be invalid... errors are okay 239 if err == nil { 240 assert.Errorf(t, rangeProofVerify(badProof, root), 241 "Proof was still valid after a random mutation:\n%X\n%X", 242 proofBytes, badProofBytes) 243 } 244 } 245 } 246 247 // ---------------------------------------- 248 249 func rangeProofVerify(rangeProof *RangeProof, root []byte) (namedError error) { 250 defer func() { 251 if e := recover(); e != nil { 252 namedError = errors.New(e.(string)) 253 } 254 }() 255 namedError = rangeProof.Verify(root) 256 return 257 } 258 259 func flatten(bzz [][]byte) (res []byte) { 260 for _, bz := range bzz { 261 res = append(res, bz...) 262 } 263 return res 264 }