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 }