github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/store/rootmulti/proof.go (about)

     1  package rootmulti
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/gnolang/gno/tm2/pkg/amino"
     8  	"github.com/gnolang/gno/tm2/pkg/crypto/merkle"
     9  	"github.com/gnolang/gno/tm2/pkg/errors"
    10  	"github.com/gnolang/gno/tm2/pkg/iavl"
    11  )
    12  
    13  // MultiStoreProof defines a collection of store proofs in a multi-store
    14  type MultiStoreProof struct {
    15  	StoreInfos []storeInfo
    16  }
    17  
    18  func NewMultiStoreProof(storeInfos []storeInfo) *MultiStoreProof {
    19  	return &MultiStoreProof{StoreInfos: storeInfos}
    20  }
    21  
    22  // ComputeRootHash returns the root hash for a given multi-store proof.
    23  func (proof *MultiStoreProof) ComputeRootHash() []byte {
    24  	ci := commitInfo{
    25  		Version:    -1, // TODO: Not needed; improve code.
    26  		StoreInfos: proof.StoreInfos,
    27  	}
    28  	return ci.Hash()
    29  }
    30  
    31  // RequireProof returns whether proof is required for the subpath.
    32  func RequireProof(subpath string) bool {
    33  	// XXX: create a better convention.
    34  	// Currently, only when query subpath is "/key", will proof be included in
    35  	// response. If there are some changes about proof building in iavlstore.go,
    36  	// we must change code here to keep consistency with iavlStore#Query.
    37  	return subpath == "/key"
    38  }
    39  
    40  //-----------------------------------------------------------------------------
    41  
    42  var _ merkle.ProofOperator = MultiStoreProofOp{}
    43  
    44  // the multi-store proof operation constant value
    45  const ProofOpMultiStore = "multistore"
    46  
    47  // TODO: document
    48  type MultiStoreProofOp struct {
    49  	// Encoded in ProofOp.Key
    50  	key []byte
    51  
    52  	// To encode in ProofOp.Data.
    53  	Proof *MultiStoreProof `json:"proof"`
    54  }
    55  
    56  func NewMultiStoreProofOp(key []byte, proof *MultiStoreProof) MultiStoreProofOp {
    57  	return MultiStoreProofOp{
    58  		key:   key,
    59  		Proof: proof,
    60  	}
    61  }
    62  
    63  // MultiStoreProofOpDecoder returns a multi-store merkle proof operator from a
    64  // given proof operation.
    65  func MultiStoreProofOpDecoder(pop merkle.ProofOp) (merkle.ProofOperator, error) {
    66  	if pop.Type != ProofOpMultiStore {
    67  		return nil, errors.New("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpMultiStore)
    68  	}
    69  
    70  	// XXX: a bit strange as we'll discard this, but it works
    71  	var op MultiStoreProofOp
    72  
    73  	err := amino.UnmarshalSized(pop.Data, &op)
    74  	if err != nil {
    75  		return nil, errors.Wrap(err, "decoding ProofOp.Data into MultiStoreProofOp")
    76  	}
    77  
    78  	return NewMultiStoreProofOp(pop.Key, op.Proof), nil
    79  }
    80  
    81  // ProofOp return a merkle proof operation from a given multi-store proof
    82  // operation.
    83  func (op MultiStoreProofOp) ProofOp() merkle.ProofOp {
    84  	bz := amino.MustMarshalSized(op)
    85  	return merkle.ProofOp{
    86  		Type: ProofOpMultiStore,
    87  		Key:  op.key,
    88  		Data: bz,
    89  	}
    90  }
    91  
    92  // String implements the Stringer interface for a mult-store proof operation.
    93  func (op MultiStoreProofOp) String() string {
    94  	return fmt.Sprintf("MultiStoreProofOp{%v}", op.GetKey())
    95  }
    96  
    97  // GetKey returns the key for a multi-store proof operation.
    98  func (op MultiStoreProofOp) GetKey() []byte {
    99  	return op.key
   100  }
   101  
   102  // Run executes a multi-store proof operation for a given value. It returns
   103  // the root hash if the value matches all the store's commitID's hash or an
   104  // error otherwise.
   105  func (op MultiStoreProofOp) Run(args [][]byte) ([][]byte, error) {
   106  	if len(args) != 1 {
   107  		return nil, errors.New("Value size is not 1")
   108  	}
   109  
   110  	value := args[0]
   111  	root := op.Proof.ComputeRootHash()
   112  
   113  	for _, si := range op.Proof.StoreInfos {
   114  		if si.Name == string(op.key) {
   115  			if bytes.Equal(value, si.Core.CommitID.Hash) {
   116  				return [][]byte{root}, nil
   117  			}
   118  
   119  			return nil, errors.New("hash mismatch for substore %v: %X vs %X", si.Name, si.Core.CommitID.Hash, value)
   120  		}
   121  	}
   122  
   123  	return nil, errors.New("key %v not found in multistore proof", op.key)
   124  }
   125  
   126  //----------------------------------------
   127  
   128  // XXX: This should be managed by the rootMultiStore which may want to register
   129  // more proof ops?
   130  func DefaultProofRuntime() (prt *merkle.ProofRuntime) {
   131  	prt = merkle.NewProofRuntime()
   132  	prt.RegisterOpDecoder(merkle.ProofOpSimpleValue, merkle.SimpleValueOpDecoder)
   133  	prt.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.IAVLValueOpDecoder)
   134  	prt.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.IAVLAbsenceOpDecoder)
   135  	prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder)
   136  	return
   137  }