github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/crypto/merkle/proof_value.go (about) 1 package merkle 2 3 import ( 4 "bytes" 5 "fmt" 6 7 tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" 8 9 "github.com/line/ostracon/crypto/tmhash" 10 ) 11 12 const ProofOpValue = "simple:v" 13 14 // ValueOp takes a key and a single value as argument and 15 // produces the root hash. The corresponding tree structure is 16 // the SimpleMap tree. SimpleMap takes a Hasher, and currently 17 // Ostracon uses tmhash. SimpleValueOp should support 18 // the hash function as used in tmhash. TODO support 19 // additional hash functions here as options/args to this 20 // operator. 21 // 22 // If the produced root hash matches the expected hash, the 23 // proof is good. 24 type ValueOp struct { 25 // Encoded in ProofOp.Key. 26 key []byte 27 28 // To encode in ProofOp.Data 29 Proof *Proof `json:"proof"` 30 } 31 32 var _ ProofOperator = ValueOp{} 33 34 func NewValueOp(key []byte, proof *Proof) ValueOp { 35 return ValueOp{ 36 key: key, 37 Proof: proof, 38 } 39 } 40 41 func ValueOpDecoder(pop tmcrypto.ProofOp) (ProofOperator, error) { 42 if pop.Type != ProofOpValue { 43 return nil, fmt.Errorf("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpValue) 44 } 45 var pbop tmcrypto.ValueOp // a bit strange as we'll discard this, but it works. 46 err := pbop.Unmarshal(pop.Data) 47 if err != nil { 48 return nil, fmt.Errorf("decoding ProofOp.Data into ValueOp: %w", err) 49 } 50 51 sp, err := ProofFromProto(pbop.Proof) 52 if err != nil { 53 return nil, err 54 } 55 return NewValueOp(pop.Key, sp), nil 56 } 57 58 func (op ValueOp) ProofOp() tmcrypto.ProofOp { 59 pbval := tmcrypto.ValueOp{ 60 Key: op.key, 61 Proof: op.Proof.ToProto(), 62 } 63 bz, err := pbval.Marshal() 64 if err != nil { 65 panic(err) 66 } 67 return tmcrypto.ProofOp{ 68 Type: ProofOpValue, 69 Key: op.key, 70 Data: bz, 71 } 72 } 73 74 func (op ValueOp) String() string { 75 return fmt.Sprintf("ValueOp{%v}", op.GetKey()) 76 } 77 78 func (op ValueOp) Run(args [][]byte) ([][]byte, error) { 79 if len(args) != 1 { 80 return nil, fmt.Errorf("expected 1 arg, got %v", len(args)) 81 } 82 value := args[0] 83 hasher := tmhash.New() 84 hasher.Write(value) 85 vhash := hasher.Sum(nil) 86 87 bz := new(bytes.Buffer) 88 // Wrap <op.Key, vhash> to hash the KVPair. 89 encodeByteSlice(bz, op.key) //nolint: errcheck // does not error 90 encodeByteSlice(bz, vhash) //nolint: errcheck // does not error 91 kvhash := leafHash(bz.Bytes()) 92 93 if !bytes.Equal(kvhash, op.Proof.LeafHash) { 94 return nil, fmt.Errorf("leaf hash mismatch: want %X got %X", op.Proof.LeafHash, kvhash) 95 } 96 97 return [][]byte{ 98 op.Proof.ComputeRootHash(), 99 }, nil 100 } 101 102 func (op ValueOp) GetKey() []byte { 103 return op.key 104 }