diff --git a/store/rootmulti/proof.go b/store/rootmulti/proof.go index af0f919c36..fa078db62b 100644 --- a/store/rootmulti/proof.go +++ b/store/rootmulti/proof.go @@ -1,31 +1,10 @@ package rootmulti import ( - "bytes" - "errors" - "fmt" - "github.com/tendermint/iavl" "github.com/tendermint/tendermint/crypto/merkle" ) -// MultiStoreProof defines a collection of store proofs in a multi-store -type MultiStoreProof struct { - StoreInfos []storeInfo -} - -func NewMultiStoreProof(storeInfos []storeInfo) *MultiStoreProof { - return &MultiStoreProof{StoreInfos: storeInfos} -} - -// ComputeRootHash returns the root hash for a given multi-store proof. -func (proof *MultiStoreProof) ComputeRootHash() []byte { - ci := commitInfo{ - StoreInfos: proof.StoreInfos, - } - return ci.Hash() -} - // RequireProof returns whether proof is required for the subpath. func RequireProof(subpath string) bool { // XXX: create a better convention. @@ -37,92 +16,6 @@ func RequireProof(subpath string) bool { //----------------------------------------------------------------------------- -var _ merkle.ProofOperator = MultiStoreProofOp{} - -// the multi-store proof operation constant value -const ProofOpMultiStore = "multistore" - -// TODO: document -type MultiStoreProofOp struct { - // Encoded in ProofOp.Key - key []byte - - // To encode in ProofOp.Data. - Proof *MultiStoreProof `json:"proof"` -} - -func NewMultiStoreProofOp(key []byte, proof *MultiStoreProof) MultiStoreProofOp { - return MultiStoreProofOp{ - key: key, - Proof: proof, - } -} - -// MultiStoreProofOpDecoder returns a multi-store merkle proof operator from a -// given proof operation. -func MultiStoreProofOpDecoder(pop merkle.ProofOp) (merkle.ProofOperator, error) { - if pop.Type != ProofOpMultiStore { - return nil, fmt.Errorf("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpMultiStore) - } - - // XXX: a bit strange as we'll discard this, but it works - var op MultiStoreProofOp - - err := cdc.UnmarshalBinaryBare(pop.Data, &op) - if err != nil { - return nil, fmt.Errorf("decoding ProofOp.Data into MultiStoreProofOp: %w", err) - } - - return NewMultiStoreProofOp(pop.Key, op.Proof), nil -} - -// ProofOp return a merkle proof operation from a given multi-store proof -// operation. -func (op MultiStoreProofOp) ProofOp() merkle.ProofOp { - bz := cdc.MustMarshalBinaryBare(op) - return merkle.ProofOp{ - Type: ProofOpMultiStore, - Key: op.key, - Data: bz, - } -} - -// String implements the Stringer interface for a mult-store proof operation. -func (op MultiStoreProofOp) String() string { - return fmt.Sprintf("MultiStoreProofOp{%v}", op.GetKey()) -} - -// GetKey returns the key for a multi-store proof operation. -func (op MultiStoreProofOp) GetKey() []byte { - return op.key -} - -// Run executes a multi-store proof operation for a given value. It returns -// the root hash if the value matches all the store's commitID's hash or an -// error otherwise. -func (op MultiStoreProofOp) Run(args [][]byte) ([][]byte, error) { - if len(args) != 1 { - return nil, errors.New("value size is not 1") - } - - value := args[0] - root := op.Proof.ComputeRootHash() - - for _, si := range op.Proof.StoreInfos { - if si.Name == string(op.key) { - if bytes.Equal(value, si.Core.CommitID.Hash) { - return [][]byte{root}, nil - } - - return nil, fmt.Errorf("hash mismatch for substore %v: %X vs %X", si.Name, si.Core.CommitID.Hash, value) - } - } - - return nil, fmt.Errorf("key %v not found in multistore proof", op.key) -} - -//----------------------------------------------------------------------------- - // XXX: This should be managed by the rootMultiStore which may want to register // more proof ops? func DefaultProofRuntime() (prt *merkle.ProofRuntime) { @@ -130,6 +23,5 @@ func DefaultProofRuntime() (prt *merkle.ProofRuntime) { prt.RegisterOpDecoder(merkle.ProofOpSimpleValue, merkle.SimpleValueOpDecoder) prt.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.ValueOpDecoder) prt.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.AbsenceOpDecoder) - prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder) return } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index bc59f62316..c73b50df3e 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -7,7 +7,7 @@ import ( "github.com/pkg/errors" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/tmhash" + "github.com/tendermint/tendermint/crypto/merkle" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec" @@ -457,13 +457,8 @@ func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery { } // Restore origin path and append proof op. - res.Proof.Ops = append(res.Proof.Ops, NewMultiStoreProofOp( - []byte(storeName), - NewMultiStoreProof(commitInfo.StoreInfos), - ).ProofOp()) + res.Proof.Ops = append(res.Proof.Ops, commitInfo.ProofOp(storeName)) - // TODO: handle in another TM v0.26 update PR - // res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos) return res } @@ -561,15 +556,31 @@ type commitInfo struct { StoreInfos []storeInfo } -// Hash returns the simple merkle root hash of the stores sorted by name. -func (ci commitInfo) Hash() []byte { - // TODO: cache to ci.hash []byte +func (ci commitInfo) toMap() map[string][]byte { m := make(map[string][]byte, len(ci.StoreInfos)) for _, storeInfo := range ci.StoreInfos { - m[storeInfo.Name] = storeInfo.Hash() + m[storeInfo.Name] = storeInfo.GetHash() } + return m +} - return SimpleHashFromMap(m) +// Hash returns the simple merkle root hash of the stores sorted by name. +func (ci commitInfo) Hash() []byte { + // we need a special case for empty set, as SimpleProofsFromMap requires at least one entry + if len(ci.StoreInfos) == 0 { + return nil + } + rootHash, _, _ := merkle.SimpleProofsFromMap(ci.toMap()) + return rootHash +} + +func (ci commitInfo) ProofOp(storeName string) merkle.ProofOp { + _, proofs, _ := merkle.SimpleProofsFromMap(ci.toMap()) + proof := proofs[storeName] + if proof == nil { + panic(fmt.Sprintf("ProofOp for %s but not registered store name", storeName)) + } + return merkle.NewSimpleValueOp([]byte(storeName), proof).ProofOp() } func (ci commitInfo) CommitID() types.CommitID { @@ -596,20 +607,15 @@ type storeCore struct { // ... maybe add more state } -// Implements merkle.Hasher. -func (si storeInfo) Hash() []byte { - // Doesn't write Name, since SimpleHashFromMap() will - // include them via the keys. - bz := si.Core.CommitID.Hash - hasher := tmhash.New() - - _, err := hasher.Write(bz) - if err != nil { - // TODO: Handle with #870 - panic(err) - } - - return hasher.Sum(nil) +// GetHash returns the GetHash from the CommitID. +// This is used in CommitInfo.Hash() +// +// When we commit to this in a merkle proof, we create a map of storeInfo.Name -> storeInfo.GetHash() +// and build a merkle proof from that. +// This is then chained with the substore proof, so we prove the root hash from the substore before this +// and need to pass that (unmodified) as the leaf value of the multistore proof. +func (si storeInfo) GetHash() []byte { + return si.Core.CommitID.Hash } //---------------------------------------- diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index a6f40dde38..4a325b819c 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -516,7 +516,7 @@ func hashStores(stores map[types.StoreKey]types.CommitKVStore) []byte { CommitID: store.LastCommitID(), // StoreType: store.GetStoreType(), }, - }.Hash() + }.GetHash() } return SimpleHashFromMap(m) } diff --git a/x/evidence/types/params.go b/x/evidence/types/params.go index 4a195933ef..07c23c9854 100644 --- a/x/evidence/types/params.go +++ b/x/evidence/types/params.go @@ -6,6 +6,6 @@ import ( // DONTCOVER -// The Double Sign Jail period ends at Max Time supported by Amino +// DoubleSignJailEndTime period ends at Max Time supported by Amino // (Dec 31, 9999 - 23:59:59 GMT). var DoubleSignJailEndTime = time.Unix(253402300799, 0)