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  }