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  }