github.com/Finschia/finschia-sdk@v0.49.1/store/types/proof.go (about)

     1  package types
     2  
     3  import (
     4  	"github.com/Finschia/ostracon/crypto/merkle"
     5  	ics23 "github.com/confio/ics23/go"
     6  	tmmerkle "github.com/tendermint/tendermint/proto/tendermint/crypto"
     7  
     8  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
     9  )
    10  
    11  const (
    12  	ProofOpIAVLCommitment         = "ics23:iavl"
    13  	ProofOpSimpleMerkleCommitment = "ics23:simple"
    14  )
    15  
    16  // CommitmentOp implements merkle.ProofOperator by wrapping an ics23 CommitmentProof
    17  // It also contains a Key field to determine which key the proof is proving.
    18  // NOTE: CommitmentProof currently can either be ExistenceProof or NonexistenceProof
    19  //
    20  // Type and Spec are classified by the kind of merkle proof it represents allowing
    21  // the code to be reused by more types. Spec is never on the wire, but mapped from type in the code.
    22  type CommitmentOp struct {
    23  	Type  string
    24  	Spec  *ics23.ProofSpec
    25  	Key   []byte
    26  	Proof *ics23.CommitmentProof
    27  }
    28  
    29  var _ merkle.ProofOperator = CommitmentOp{}
    30  
    31  func NewIavlCommitmentOp(key []byte, proof *ics23.CommitmentProof) CommitmentOp {
    32  	return CommitmentOp{
    33  		Type:  ProofOpIAVLCommitment,
    34  		Spec:  ics23.IavlSpec,
    35  		Key:   key,
    36  		Proof: proof,
    37  	}
    38  }
    39  
    40  func NewSimpleMerkleCommitmentOp(key []byte, proof *ics23.CommitmentProof) CommitmentOp {
    41  	return CommitmentOp{
    42  		Type:  ProofOpSimpleMerkleCommitment,
    43  		Spec:  ics23.TendermintSpec,
    44  		Key:   key,
    45  		Proof: proof,
    46  	}
    47  }
    48  
    49  // CommitmentOpDecoder takes a merkle.ProofOp and attempts to decode it into a CommitmentOp ProofOperator
    50  // The proofOp.Data is just a marshaled CommitmentProof. The Key of the CommitmentOp is extracted
    51  // from the unmarshalled proof.
    52  func CommitmentOpDecoder(pop tmmerkle.ProofOp) (merkle.ProofOperator, error) {
    53  	var spec *ics23.ProofSpec
    54  	switch pop.Type {
    55  	case ProofOpIAVLCommitment:
    56  		spec = ics23.IavlSpec
    57  	case ProofOpSimpleMerkleCommitment:
    58  		spec = ics23.TendermintSpec
    59  	default:
    60  		return nil, sdkerrors.Wrapf(ErrInvalidProof, "unexpected ProofOp.Type; got %s, want supported ics23 subtypes 'ProofOpIAVLCommitment' or 'ProofOpSimpleMerkleCommitment'", pop.Type)
    61  	}
    62  
    63  	proof := &ics23.CommitmentProof{}
    64  	err := proof.Unmarshal(pop.Data)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	op := CommitmentOp{
    70  		Type:  pop.Type,
    71  		Key:   pop.Key,
    72  		Spec:  spec,
    73  		Proof: proof,
    74  	}
    75  	return op, nil
    76  }
    77  
    78  func (op CommitmentOp) GetKey() []byte {
    79  	return op.Key
    80  }
    81  
    82  // Run takes in a list of arguments and attempts to run the proof op against these arguments.
    83  // Returns the root wrapped in [][]byte if the proof op succeeds with given args. If not,
    84  // it will return an error.
    85  //
    86  // CommitmentOp will accept args of length 1 or length 0
    87  // If length 1 args is passed in, then CommitmentOp will attempt to prove the existence of the key
    88  // with the value provided by args[0] using the embedded CommitmentProof and return the CommitmentRoot of the proof.
    89  // If length 0 args is passed in, then CommitmentOp will attempt to prove the absence of the key
    90  // in the CommitmentOp and return the CommitmentRoot of the proof.
    91  func (op CommitmentOp) Run(args [][]byte) ([][]byte, error) {
    92  	// calculate root from proof
    93  	root, err := op.Proof.Calculate()
    94  	if err != nil {
    95  		return nil, sdkerrors.Wrapf(ErrInvalidProof, "could not calculate root for proof: %v", err)
    96  	}
    97  	// Only support an existence proof or nonexistence proof (batch proofs currently unsupported)
    98  	switch len(args) {
    99  	case 0:
   100  		// Args are nil, so we verify the absence of the key.
   101  		absent := ics23.VerifyNonMembership(op.Spec, root, op.Proof, op.Key)
   102  		if !absent {
   103  			return nil, sdkerrors.Wrapf(ErrInvalidProof, "proof did not verify absence of key: %s", string(op.Key))
   104  		}
   105  
   106  	case 1:
   107  		// Args is length 1, verify existence of key with value args[0]
   108  		if !ics23.VerifyMembership(op.Spec, root, op.Proof, op.Key, args[0]) {
   109  			return nil, sdkerrors.Wrapf(ErrInvalidProof, "proof did not verify existence of key %s with given value %x", op.Key, args[0])
   110  		}
   111  	default:
   112  		return nil, sdkerrors.Wrapf(ErrInvalidProof, "args must be length 0 or 1, got: %d", len(args))
   113  	}
   114  
   115  	return [][]byte{root}, nil
   116  }
   117  
   118  // ProofOp implements ProofOperator interface and converts a CommitmentOp
   119  // into a merkle.ProofOp format that can later be decoded by CommitmentOpDecoder
   120  // back into a CommitmentOp for proof verification
   121  func (op CommitmentOp) ProofOp() tmmerkle.ProofOp {
   122  	bz, err := op.Proof.Marshal()
   123  	if err != nil {
   124  		panic(err.Error())
   125  	}
   126  	return tmmerkle.ProofOp{
   127  		Type: op.Type,
   128  		Key:  op.Key,
   129  		Data: bz,
   130  	}
   131  }