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

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