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  }