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