You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

103 lines
2.5 KiB

package merkle
import (
"bytes"
"fmt"
"github.com/tendermint/tendermint/crypto/tmhash"
tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto"
)
const ProofOpValue = "simple:v"
// ValueOp takes a key and a single value as argument and
// produces the root hash. The corresponding tree structure is
// the SimpleMap tree. SimpleMap takes a Hasher, and currently
// Tendermint uses tmhash. SimpleValueOp should support
// the hash function as used in tmhash. TODO support
// additional hash functions here as options/args to this
// operator.
//
// If the produced root hash matches the expected hash, the
// proof is good.
type ValueOp struct {
// Encoded in ProofOp.Key.
key []byte
// To encode in ProofOp.Data
Proof *Proof `json:"proof"`
}
var _ ProofOperator = ValueOp{}
func NewValueOp(key []byte, proof *Proof) ValueOp {
return ValueOp{
key: key,
Proof: proof,
}
}
func ValueOpDecoder(pop tmcrypto.ProofOp) (ProofOperator, error) {
if pop.Type != ProofOpValue {
return nil, fmt.Errorf("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpValue)
}
var pbop tmcrypto.ValueOp // a bit strange as we'll discard this, but it works.
err := pbop.Unmarshal(pop.Data)
if err != nil {
return nil, fmt.Errorf("decoding ProofOp.Data into ValueOp: %w", err)
}
sp, err := ProofFromProto(pbop.Proof)
if err != nil {
return nil, err
}
return NewValueOp(pop.Key, sp), nil
}
func (op ValueOp) ProofOp() tmcrypto.ProofOp {
pbval := tmcrypto.ValueOp{
Key: op.key,
Proof: op.Proof.ToProto(),
}
bz, err := pbval.Marshal()
if err != nil {
panic(err)
}
return tmcrypto.ProofOp{
Type: ProofOpValue,
Key: op.key,
Data: bz,
}
}
func (op ValueOp) String() string {
return fmt.Sprintf("ValueOp{%v}", op.GetKey())
}
func (op ValueOp) Run(args [][]byte) ([][]byte, error) {
if len(args) != 1 {
return nil, fmt.Errorf("expected 1 arg, got %v", len(args))
}
value := args[0]
hasher := tmhash.New()
hasher.Write(value) //nolint: errcheck // does not error
vhash := hasher.Sum(nil)
bz := new(bytes.Buffer)
// Wrap <op.Key, vhash> to hash the KVPair.
encodeByteSlice(bz, op.key) //nolint: errcheck // does not error
encodeByteSlice(bz, vhash) //nolint: errcheck // does not error
kvhash := leafHash(bz.Bytes())
if !bytes.Equal(kvhash, op.Proof.LeafHash) {
return nil, fmt.Errorf("leaf hash mismatch: want %X got %X", op.Proof.LeafHash, kvhash)
}
return [][]byte{
op.Proof.ComputeRootHash(),
}, nil
}
func (op ValueOp) GetKey() []byte {
return op.key
}