package types_test import ( "fmt" "testing" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" abci "github.com/tendermint/tendermint/abci/types" ) func (suite *MerkleTestSuite) TestVerifyMembership() { suite.iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) cid := suite.store.Commit() res := suite.store.Query(abci.RequestQuery{ Path: fmt.Sprintf("/%s/key", suite.storeKey.Name()), // required path to get key/value+proof Data: []byte("MYKEY"), Prove: true, }) require.NotNil(suite.T(), res.Proof) proof := types.MerkleProof{ Proof: res.Proof, } suite.Require().NoError(proof.ValidateBasic()) suite.Require().Error(types.MerkleProof{}.ValidateBasic()) cases := []struct { name string root []byte pathArr []string value []byte shouldPass bool }{ {"valid proof", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, []byte("MYVALUE"), true}, // valid proof {"wrong value", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, []byte("WRONGVALUE"), false}, // invalid proof with wrong value {"nil value", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, []byte(nil), false}, // invalid proof with nil value {"wrong key", cid.Hash, []string{suite.storeKey.Name(), "NOTMYKEY"}, []byte("MYVALUE"), false}, // invalid proof with wrong key {"wrong path 1", cid.Hash, []string{suite.storeKey.Name(), "MYKEY", "MYKEY"}, []byte("MYVALUE"), false}, // invalid proof with wrong path {"wrong path 2", cid.Hash, []string{suite.storeKey.Name()}, []byte("MYVALUE"), false}, // invalid proof with wrong path {"wrong path 3", cid.Hash, []string{"MYKEY"}, []byte("MYVALUE"), false}, // invalid proof with wrong path {"wrong storekey", cid.Hash, []string{"otherStoreKey", "MYKEY"}, []byte("MYVALUE"), false}, // invalid proof with wrong store prefix {"wrong root", []byte("WRONGROOT"), []string{suite.storeKey.Name(), "MYKEY"}, []byte("MYVALUE"), false}, // invalid proof with wrong root {"nil root", []byte(nil), []string{suite.storeKey.Name(), "MYKEY"}, []byte("MYVALUE"), false}, // invalid proof with nil root } for i, tc := range cases { tc := tc suite.Run(tc.name, func() { root := types.NewMerkleRoot(tc.root) path := types.NewMerklePath(tc.pathArr) err := proof.VerifyMembership(&root, path, tc.value) if tc.shouldPass { // nolint: scopelint suite.Require().NoError(err, "test case %d should have passed", i) } else { // nolint: scopelint suite.Require().Error(err, "test case %d should have failed", i) } }) } } func (suite *MerkleTestSuite) TestVerifyNonMembership() { suite.iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) cid := suite.store.Commit() // Get Proof res := suite.store.Query(abci.RequestQuery{ Path: fmt.Sprintf("/%s/key", suite.storeKey.Name()), // required path to get key/value+proof Data: []byte("MYABSENTKEY"), Prove: true, }) require.NotNil(suite.T(), res.Proof) proof := types.MerkleProof{ Proof: res.Proof, } suite.Require().NoError(proof.ValidateBasic()) cases := []struct { name string root []byte pathArr []string shouldPass bool }{ {"valid proof", cid.Hash, []string{suite.storeKey.Name(), "MYABSENTKEY"}, true}, // valid proof {"wrong key", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, false}, // invalid proof with existent key {"wrong path 1", cid.Hash, []string{suite.storeKey.Name(), "MYKEY", "MYABSENTKEY"}, false}, // invalid proof with wrong path {"wrong path 2", cid.Hash, []string{suite.storeKey.Name(), "MYABSENTKEY", "MYKEY"}, false}, // invalid proof with wrong path {"wrong path 3", cid.Hash, []string{suite.storeKey.Name()}, false}, // invalid proof with wrong path {"wrong path 4", cid.Hash, []string{"MYABSENTKEY"}, false}, // invalid proof with wrong path {"wrong storeKey", cid.Hash, []string{"otherStoreKey", "MYABSENTKEY"}, false}, // invalid proof with wrong store prefix {"wrong root", []byte("WRONGROOT"), []string{suite.storeKey.Name(), "MYABSENTKEY"}, false}, // invalid proof with wrong root {"nil root", []byte(nil), []string{suite.storeKey.Name(), "MYABSENTKEY"}, false}, // invalid proof with nil root } for i, tc := range cases { tc := tc suite.Run(tc.name, func() { root := types.NewMerkleRoot(tc.root) path := types.NewMerklePath(tc.pathArr) err := proof.VerifyNonMembership(&root, path) if tc.shouldPass { // nolint: scopelint suite.Require().NoError(err, "test case %d should have passed", i) } else { // nolint: scopelint suite.Require().Error(err, "test case %d should have failed", i) } }) } } func TestApplyPrefix(t *testing.T) { prefix := types.NewMerklePrefix([]byte("storePrefixKey")) pathStr := "path1/path2/path3/key" prefixedPath, err := types.ApplyPrefix(prefix, pathStr) require.Nil(t, err, "valid prefix returns error") require.Equal(t, "/storePrefixKey/path1/path2/path3/key", prefixedPath.Pretty(), "Prefixed path incorrect") require.Equal(t, "/storePrefixKey/path1%2Fpath2%2Fpath3%2Fkey", prefixedPath.String(), "Prefixed scaped path incorrect") // invalid prefix contains non-alphanumeric character invalidPathStr := "invalid-path/doesitfail?/hopefully" invalidPath, err := types.ApplyPrefix(prefix, invalidPathStr) require.NotNil(t, err, "invalid prefix does not returns error") require.Equal(t, types.MerklePath{}, invalidPath, "invalid prefix returns valid Path on ApplyPrefix") }