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.4 KiB

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