Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imp: cleanup verifcation arg code for 23-commitment #7493

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
15 changes: 0 additions & 15 deletions modules/core/23-commitment/types/bench_test.go

This file was deleted.

65 changes: 20 additions & 45 deletions modules/core/23-commitment/types/merkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ package types
import (
"bytes"

"github.com/cosmos/gogoproto/proto"
ics23 "github.com/cosmos/ics23/go"

errorsmod "cosmossdk.io/errors"

cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto"

"github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types/v2"
"github.com/cosmos/ibc-go/v9/modules/core/exported"
)
Expand Down Expand Up @@ -86,19 +83,16 @@ func ApplyPrefix(prefix exported.Prefix, path v2.MerklePath) (v2.MerklePath, err
// VerifyMembership verifies the membership of a merkle proof against the given root, path, and value.
// Note that the path is expected as []string{<store key of module>, <key corresponding to requested value>}.
func (proof MerkleProof) VerifyMembership(specs []*ics23.ProofSpec, root exported.Root, path exported.Path, value []byte) error {
if err := proof.validateVerificationArgs(specs, root); err != nil {
return err
}

// VerifyMembership specific argument validation
mpath, ok := path.(v2.MerklePath)
if !ok {
return errorsmod.Wrapf(ErrInvalidProof, "path %v is not of type MerklePath", path)
}
if len(mpath.KeyPath) != len(specs) {
return errorsmod.Wrapf(ErrInvalidProof, "path length %d not same as proof %d",
len(mpath.KeyPath), len(specs))

if err := validateVerificationArgs(proof, mpath, specs, root); err != nil {
return err
}

// VerifyMembership specific argument validation
if len(value) == 0 {
return errorsmod.Wrap(ErrInvalidProof, "empty value in membership proof")
}
Expand All @@ -112,17 +106,13 @@ func (proof MerkleProof) VerifyMembership(specs []*ics23.ProofSpec, root exporte
// VerifyNonMembership verifies a chained proof where the absence of a given path is proven
// at the lowest subtree and then each subtree's inclusion is proved up to the final root.
func (proof MerkleProof) VerifyNonMembership(specs []*ics23.ProofSpec, root exported.Root, path exported.Path) error {
if err := proof.validateVerificationArgs(specs, root); err != nil {
return err
}

// VerifyNonMembership specific argument validation
mpath, ok := path.(v2.MerklePath)
if !ok {
return errorsmod.Wrapf(ErrInvalidProof, "path %v is not of type MerkleProof", path)
}
if len(mpath.KeyPath) != len(specs) {
return errorsmod.Wrapf(ErrInvalidProof, "path length %d not same as proof %d", len(mpath.KeyPath), len(specs))

if err := validateVerificationArgs(proof, mpath, specs, root); err != nil {
return err
}

// VerifyNonMembership will verify the absence of key in lowest subtree, and then chain inclusion proofs
Expand Down Expand Up @@ -198,40 +188,25 @@ func verifyChainedMembershipProof(root []byte, specs []*ics23.ProofSpec, proofs
return nil
}

// blankMerkleProof and blankProofOps will be used to compare against their zero values,
// and are declared as globals to avoid having to unnecessarily re-allocate on every comparison.
var (
blankMerkleProof = &MerkleProof{}
blankProofOps = &cmtcrypto.ProofOps{}
)

// Empty returns true if the root is empty
func (proof *MerkleProof) Empty() bool {
return proof == nil || proto.Equal(proof, blankMerkleProof) || proto.Equal(proof, blankProofOps)
}

// ValidateBasic checks if the proof is empty.
func (proof MerkleProof) ValidateBasic() error {
if proof.Empty() {
return ErrInvalidProof
}
return nil
}

// validateVerificationArgs verifies the proof arguments are valid
func (proof MerkleProof) validateVerificationArgs(specs []*ics23.ProofSpec, root exported.Root) error {
if proof.Empty() {
return errorsmod.Wrap(ErrInvalidMerkleProof, "proof cannot be empty")
// validateVerificationArgs verifies the proof arguments are valid.
// The merkle path and merkle proof contain a list of keys and their proofs
// which correspond to individual trees. The length of these keys and their proofs
// must equal the length of the given specs. All arguments must be non-empty.
func validateVerificationArgs(proof MerkleProof, path v2.MerklePath, specs []*ics23.ProofSpec, root exported.Root) error {
if proof.GetProofs() == nil {
return errorsmod.Wrap(ErrInvalidMerkleProof, "proof must not be empty")
}

if root == nil || root.Empty() {
return errorsmod.Wrap(ErrInvalidMerkleProof, "root cannot be empty")
}

if len(specs) != len(proof.Proofs) {
return errorsmod.Wrapf(ErrInvalidMerkleProof,
"length of specs: %d not equal to length of proof: %d",
len(specs), len(proof.Proofs))
return errorsmod.Wrapf(ErrInvalidMerkleProof, "length of specs: %d not equal to length of proof: %d", len(specs), len(proof.Proofs))
}

if len(path.KeyPath) != len(specs) {
return errorsmod.Wrapf(ErrInvalidProof, "path length %d not same as proof %d", len(path.KeyPath), len(specs))
Comment on lines 204 to +209
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could indicate in godoc why this is. e.g. key path of length > 1 is expected to be a chained proof.

Every path element inside path.KeyPath is expected to have a corresponding proofSpec, right? In the normal case first we calculate subroots for inclusion/non-inclusion and then inclusion of the subroot in commitment root.

Only in the case of path.KeyPath == 1, would we be able to do a single inclusion or non-inclusion proof.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. This code is expecting to receive a list of keys which corresponds to different trees (which is why it must be a list of specs, 1 per tree)

}

for i, spec := range specs {
Expand Down
5 changes: 0 additions & 5 deletions modules/core/23-commitment/types/merkle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ func (suite *MerkleTestSuite) TestVerifyMembership() {
proof, err := types.ConvertProofs(res.ProofOps)
require.NoError(suite.T(), err)

suite.Require().NoError(proof.ValidateBasic())
DimitrisJim marked this conversation as resolved.
Show resolved Hide resolved
suite.Require().Error(types.MerkleProof{}.ValidateBasic())

cases := []struct {
name string
root []byte
Expand Down Expand Up @@ -93,8 +90,6 @@ func (suite *MerkleTestSuite) TestVerifyNonMembership() {
proof, err := types.ConvertProofs(res.ProofOps)
require.NoError(suite.T(), err)

suite.Require().NoError(proof.ValidateBasic())

cases := []struct {
name string
root []byte
Expand Down
Loading