Merge PR #6323: Use simple merkle proof for commit info
This commit is contained in:
parent
d17f5771c7
commit
cd272d525a
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
|
|
@ -516,7 +516,7 @@ func hashStores(stores map[types.StoreKey]types.CommitKVStore) []byte {
|
|||
CommitID: store.LastCommitID(),
|
||||
// StoreType: store.GetStoreType(),
|
||||
},
|
||||
}.Hash()
|
||||
}.GetHash()
|
||||
}
|
||||
return SimpleHashFromMap(m)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue