Skip to content

Commit

Permalink
Merge PR #6323: Use simple merkle proof for commit info
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanfrey authored Jun 3, 2020
1 parent d17f577 commit cd272d5
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 136 deletions.
108 changes: 0 additions & 108 deletions store/rootmulti/proof.go
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -37,99 +16,12 @@ 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) {
prt = merkle.NewProofRuntime()
prt.RegisterOpDecoder(merkle.ProofOpSimpleValue, merkle.SimpleValueOpDecoder)
prt.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.ValueOpDecoder)
prt.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.AbsenceOpDecoder)
prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder)
return
}
58 changes: 32 additions & 26 deletions store/rootmulti/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}

// 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
}

return SimpleHashFromMap(m)
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 {
Expand All @@ -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
}

//----------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion store/rootmulti/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ func hashStores(stores map[types.StoreKey]types.CommitKVStore) []byte {
CommitID: store.LastCommitID(),
// StoreType: store.GetStoreType(),
},
}.Hash()
}.GetHash()
}
return SimpleHashFromMap(m)
}
2 changes: 1 addition & 1 deletion x/evidence/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit cd272d5

Please sign in to comment.