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 }