cosmos-sdk/x/ibc/23-commitment/types/merkle.go

162 lines
4.5 KiB
Go

package types
import (
"net/url"
"github.com/cosmos/cosmos-sdk/store/rootmulti"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
"github.com/tendermint/tendermint/crypto/merkle"
)
// ICS 023 Merkle Types Implementation
//
// This file defines Merkle commitment types that implements ICS 023.
// Merkle proof implementation of the Proof interface
// Applied on SDK-based IBC implementation
var _ exported.Root = (*MerkleRoot)(nil)
// NewMerkleRoot constructs a new MerkleRoot
func NewMerkleRoot(hash []byte) MerkleRoot {
return MerkleRoot{
Hash: hash,
}
}
// GetHash implements RootI interface
func (mr MerkleRoot) GetHash() []byte {
return mr.Hash
}
// GetCommitmentType implements RootI interface
func (MerkleRoot) GetCommitmentType() exported.Type {
return exported.Merkle
}
// IsEmpty returns true if the root is empty
func (mr MerkleRoot) IsEmpty() bool {
return len(mr.GetHash()) == 0
}
var _ exported.Prefix = (*MerklePrefix)(nil)
// NewMerklePrefix constructs new MerklePrefix instance
func NewMerklePrefix(keyPrefix []byte) MerklePrefix {
return MerklePrefix{
KeyPrefix: keyPrefix,
}
}
// GetCommitmentType implements Prefix interface
func (MerklePrefix) GetCommitmentType() exported.Type {
return exported.Merkle
}
// Bytes returns the key prefix bytes
func (mp MerklePrefix) Bytes() []byte {
return mp.KeyPrefix
}
// IsEmpty returns true if the prefix is empty
func (mp MerklePrefix) IsEmpty() bool {
return len(mp.Bytes()) == 0
}
var _ exported.Path = (*MerklePath)(nil)
// NewMerklePath creates a new MerklePath instance
func NewMerklePath(keyPathStr []string) MerklePath {
merkleKeyPath := KeyPath{}
for _, keyStr := range keyPathStr {
merkleKeyPath = merkleKeyPath.AppendKey([]byte(keyStr), URL)
}
return MerklePath{
KeyPath: merkleKeyPath,
}
}
// GetCommitmentType implements PathI
func (MerklePath) GetCommitmentType() exported.Type {
return exported.Merkle
}
// String implements fmt.Stringer.
func (mp MerklePath) String() string {
return mp.KeyPath.String()
}
// Pretty returns the unescaped path of the URL string.
func (mp MerklePath) Pretty() string {
path, err := url.PathUnescape(mp.KeyPath.String())
if err != nil {
panic(err)
}
return path
}
// IsEmpty returns true if the path is empty
func (mp MerklePath) IsEmpty() bool {
return len(mp.KeyPath.Keys) == 0
}
// ApplyPrefix constructs a new commitment path from the arguments. It interprets
// the path argument in the context of the prefix argument.
//
// CONTRACT: provided path string MUST be a well formated path. See ICS24 for
// reference.
func ApplyPrefix(prefix exported.Prefix, path string) (MerklePath, error) {
err := host.PathValidator(path)
if err != nil {
return MerklePath{}, err
}
if prefix == nil || prefix.IsEmpty() {
return MerklePath{}, sdkerrors.Wrap(ErrInvalidPrefix, "prefix can't be empty")
}
return NewMerklePath([]string{string(prefix.Bytes()), path}), nil
}
var _ exported.Proof = (*MerkleProof)(nil)
// GetCommitmentType implements ProofI
func (MerkleProof) GetCommitmentType() exported.Type {
return exported.Merkle
}
// VerifyMembership verifies the membership pf a merkle proof against the given root, path, and value.
func (proof MerkleProof) VerifyMembership(root exported.Root, path exported.Path, value []byte) error {
if proof.IsEmpty() || root == nil || root.IsEmpty() || path == nil || path.IsEmpty() || len(value) == 0 {
return sdkerrors.Wrap(ErrInvalidMerkleProof, "empty params or proof")
}
runtime := rootmulti.DefaultProofRuntime()
return runtime.VerifyValue(proof.Proof, root.GetHash(), path.String(), value)
}
// VerifyNonMembership verifies the absence of a merkle proof against the given root and path.
func (proof MerkleProof) VerifyNonMembership(root exported.Root, path exported.Path) error {
if proof.IsEmpty() || root == nil || root.IsEmpty() || path == nil || path.IsEmpty() {
return sdkerrors.Wrap(ErrInvalidMerkleProof, "empty params or proof")
}
runtime := rootmulti.DefaultProofRuntime()
return runtime.VerifyAbsence(proof.Proof, root.GetHash(), path.String())
}
// IsEmpty returns true if the root is empty
func (proof MerkleProof) IsEmpty() bool {
return proof.Proof.Equal(nil) || proof.Equal(MerkleProof{}) || proof.Proof.Equal(nil) || proof.Proof.Equal(merkle.Proof{})
}
// ValidateBasic checks if the proof is empty.
func (proof MerkleProof) ValidateBasic() error {
if proof.IsEmpty() {
return ErrInvalidProof
}
return nil
}