github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/crypto/merkle/proof_op.go (about) 1 package merkle 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 8 cmtcrypto "github.com/badrootd/celestia-core/proto/tendermint/crypto" 9 ) 10 11 //---------------------------------------- 12 // ProofOp gets converted to an instance of ProofOperator: 13 14 // ProofOperator is a layer for calculating intermediate Merkle roots 15 // when a series of Merkle trees are chained together. 16 // Run() takes leaf values from a tree and returns the Merkle 17 // root for the corresponding tree. It takes and returns a list of bytes 18 // to allow multiple leaves to be part of a single proof, for instance in a range proof. 19 // ProofOp() encodes the ProofOperator in a generic way so it can later be 20 // decoded with OpDecoder. 21 type ProofOperator interface { 22 Run([][]byte) ([][]byte, error) 23 GetKey() []byte 24 ProofOp() cmtcrypto.ProofOp 25 } 26 27 //---------------------------------------- 28 // Operations on a list of ProofOperators 29 30 // ProofOperators is a slice of ProofOperator(s). 31 // Each operator will be applied to the input value sequentially 32 // and the last Merkle root will be verified with already known data 33 type ProofOperators []ProofOperator 34 35 func (poz ProofOperators) VerifyValue(root []byte, keypath string, value []byte) (err error) { 36 return poz.Verify(root, keypath, [][]byte{value}) 37 } 38 39 func (poz ProofOperators) Verify(root []byte, keypath string, args [][]byte) (err error) { 40 keys, err := KeyPathToKeys(keypath) 41 if err != nil { 42 return 43 } 44 45 for i, op := range poz { 46 key := op.GetKey() 47 if len(key) != 0 { 48 if len(keys) == 0 { 49 return fmt.Errorf("key path has insufficient # of parts: expected no more keys but got %+v", string(key)) 50 } 51 lastKey := keys[len(keys)-1] 52 if !bytes.Equal(lastKey, key) { 53 return fmt.Errorf("key mismatch on operation #%d: expected %+v but got %+v", i, string(lastKey), string(key)) 54 } 55 keys = keys[:len(keys)-1] 56 } 57 args, err = op.Run(args) 58 if err != nil { 59 return 60 } 61 } 62 if !bytes.Equal(root, args[0]) { 63 return fmt.Errorf("calculated root hash is invalid: expected %X but got %X", root, args[0]) 64 } 65 if len(keys) != 0 { 66 return errors.New("keypath not consumed all") 67 } 68 return nil 69 } 70 71 // VerifyFromKeys performs the same verification logic as the normal Verify 72 // method, except it does not perform any processing on the keypath. This is 73 // useful when using keys that have split or escape points as a part of the key. 74 func (poz ProofOperators) VerifyFromKeys(root []byte, keys [][]byte, args [][]byte) (err error) { 75 for i, op := range poz { 76 key := op.GetKey() 77 if len(key) != 0 { 78 if len(keys) == 0 { 79 return fmt.Errorf("key path has insufficient # of parts: expected no more keys but got %+v", string(key)) 80 } 81 lastKey := keys[len(keys)-1] 82 if !bytes.Equal(lastKey, key) { 83 return fmt.Errorf("key mismatch on operation #%d: expected %+v but got %+v", i, string(lastKey), string(key)) 84 } 85 keys = keys[:len(keys)-1] 86 } 87 args, err = op.Run(args) 88 if err != nil { 89 return 90 } 91 } 92 if !bytes.Equal(root, args[0]) { 93 return fmt.Errorf("calculated root hash is invalid: expected %X but got %X", root, args[0]) 94 } 95 if len(keys) != 0 { 96 return fmt.Errorf("keypath not consumed all: %s", string(bytes.Join(keys, []byte("/")))) 97 } 98 return nil 99 } 100 101 //---------------------------------------- 102 // ProofRuntime - main entrypoint 103 104 type OpDecoder func(cmtcrypto.ProofOp) (ProofOperator, error) 105 106 type ProofRuntime struct { 107 decoders map[string]OpDecoder 108 } 109 110 func NewProofRuntime() *ProofRuntime { 111 return &ProofRuntime{ 112 decoders: make(map[string]OpDecoder), 113 } 114 } 115 116 func (prt *ProofRuntime) RegisterOpDecoder(typ string, dec OpDecoder) { 117 _, ok := prt.decoders[typ] 118 if ok { 119 panic("already registered for type " + typ) 120 } 121 prt.decoders[typ] = dec 122 } 123 124 func (prt *ProofRuntime) Decode(pop cmtcrypto.ProofOp) (ProofOperator, error) { 125 decoder := prt.decoders[pop.Type] 126 if decoder == nil { 127 return nil, fmt.Errorf("unrecognized proof type %v", pop.Type) 128 } 129 return decoder(pop) 130 } 131 132 func (prt *ProofRuntime) DecodeProof(proof *cmtcrypto.ProofOps) (ProofOperators, error) { 133 poz := make(ProofOperators, 0, len(proof.Ops)) 134 for _, pop := range proof.Ops { 135 operator, err := prt.Decode(pop) 136 if err != nil { 137 return nil, fmt.Errorf("decoding a proof operator: %w", err) 138 } 139 poz = append(poz, operator) 140 } 141 return poz, nil 142 } 143 144 func (prt *ProofRuntime) VerifyValue(proof *cmtcrypto.ProofOps, root []byte, keypath string, value []byte) (err error) { 145 return prt.Verify(proof, root, keypath, [][]byte{value}) 146 } 147 148 func (prt *ProofRuntime) VerifyValueFromKeys(proof *cmtcrypto.ProofOps, root []byte, keys [][]byte, value []byte) (err error) { 149 return prt.VerifyFromKeys(proof, root, keys, [][]byte{value}) 150 } 151 152 // TODO In the long run we'll need a method of classification of ops, 153 // whether existence or absence or perhaps a third? 154 func (prt *ProofRuntime) VerifyAbsence(proof *cmtcrypto.ProofOps, root []byte, keypath string) (err error) { 155 return prt.Verify(proof, root, keypath, nil) 156 } 157 158 func (prt *ProofRuntime) Verify(proof *cmtcrypto.ProofOps, root []byte, keypath string, args [][]byte) (err error) { 159 poz, err := prt.DecodeProof(proof) 160 if err != nil { 161 return fmt.Errorf("decoding proof: %w", err) 162 } 163 return poz.Verify(root, keypath, args) 164 } 165 166 // VerifyFromKeys performs the same verification logic as the normal Verify 167 // method, except it does not perform any processing on the keypath. This is 168 // useful when using keys that have split or escape points as a part of the key. 169 func (prt *ProofRuntime) VerifyFromKeys(proof *cmtcrypto.ProofOps, root []byte, keys [][]byte, args [][]byte) (err error) { 170 poz, err := prt.DecodeProof(proof) 171 if err != nil { 172 return fmt.Errorf("decoding proof: %w", err) 173 } 174 return poz.VerifyFromKeys(root, keys, args) 175 } 176 177 // DefaultProofRuntime only knows about value proofs. 178 // To use e.g. IAVL proofs, register op-decoders as 179 // defined in the IAVL package. 180 func DefaultProofRuntime() (prt *ProofRuntime) { 181 prt = NewProofRuntime() 182 prt.RegisterOpDecoder(ProofOpValue, ValueOpDecoder) 183 return 184 }