IRISHUB-238: Add multiply store proof build and verification

This commit is contained in:
HaoyangLiu
2018-08-30 15:05:16 +08:00
parent fd8c1e5253
commit 703c643fc0
8 changed files with 401 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
package store
import (
"bytes"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"
"github.com/tendermint/iavl"
cmn "github.com/tendermint/tendermint/libs/common"
)
// commitID of substores, such as acc store, gov store
type SubstoreCommitID struct {
Name string `json:"name"`
Version int64 `json:"version"`
CommitHash cmn.HexBytes `json:"commit_hash"`
}
// proof of store which have multi substores
type MultiStoreProof struct {
CommitIDList []SubstoreCommitID `json:"commit_id_list"`
StoreName string `json:"store_name"`
RangeProof iavl.RangeProof `json:"range_proof"`
}
// build MultiStoreProof based on iavl proof and storeInfos
func BuildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []storeInfo) ([]byte, error) {
var rangeProof iavl.RangeProof
err := cdc.UnmarshalBinary(iavlProof, &rangeProof)
if err != nil {
return nil, err
}
var multiStoreProof MultiStoreProof
for _, storeInfo := range storeInfos {
commitID := SubstoreCommitID{
Name: storeInfo.Name,
Version: storeInfo.Core.CommitID.Version,
CommitHash: storeInfo.Core.CommitID.Hash,
}
multiStoreProof.CommitIDList = append(multiStoreProof.CommitIDList, commitID)
}
multiStoreProof.StoreName = storeName
multiStoreProof.RangeProof = rangeProof
proof, err := cdc.MarshalBinary(multiStoreProof)
if err != nil {
return nil, err
}
return proof, nil
}
// verify multiStoreCommitInfo against appHash
func VerifyMultiStoreCommitInfo(storeName string, multiStoreCommitInfo []SubstoreCommitID, appHash []byte) ([]byte, error) {
var substoreCommitHash []byte
var storeInfos []storeInfo
var height int64
for _, multiStoreCommitID := range multiStoreCommitInfo {
if multiStoreCommitID.Name == storeName {
substoreCommitHash = multiStoreCommitID.CommitHash
height = multiStoreCommitID.Version
}
storeInfo := storeInfo{
Name: multiStoreCommitID.Name,
Core: storeCore{
CommitID: sdk.CommitID{
Version: multiStoreCommitID.Version,
Hash: multiStoreCommitID.CommitHash,
},
},
}
storeInfos = append(storeInfos, storeInfo)
}
if len(substoreCommitHash) == 0 {
return nil, cmn.NewError("failed to get substore root commit hash by store name")
}
ci := commitInfo{
Version: height,
StoreInfos: storeInfos,
}
if !bytes.Equal(appHash, ci.Hash()) {
return nil, cmn.NewError("the merkle root of multiStoreCommitInfo doesn't equal to appHash")
}
return substoreCommitHash, nil
}
// verify iavl proof
func VerifyRangeProof(key, value []byte, substoreCommitHash []byte, rangeProof *iavl.RangeProof) error {
// Validate the proof to ensure data integrity.
err := rangeProof.Verify(substoreCommitHash)
if err != nil {
return errors.Wrap(err, "proof root hash doesn't equal to substore commit root hash")
}
if len(value) != 0 {
// Validate existence proof
err = rangeProof.VerifyItem(key, value)
if err != nil {
return errors.Wrap(err, "failed in existence verification")
}
} else {
// Validate absence proof
err = rangeProof.VerifyAbsence(key)
if err != nil {
return errors.Wrap(err, "failed in absence verification")
}
}
return nil
}
+96
View File
@@ -0,0 +1,96 @@
package store
import (
"encoding/hex"
"github.com/stretchr/testify/assert"
"github.com/tendermint/iavl"
cmn "github.com/tendermint/tendermint/libs/common"
"testing"
)
func TestVerifyMultiStoreCommitInfo(t *testing.T) {
appHash, _ := hex.DecodeString("ebf3c1fb724d3458023c8fefef7b33add2fc1e84")
substoreRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828")
storeName := "acc"
var multiStoreCommitInfo []SubstoreCommitID
gocRootHash, _ := hex.DecodeString("62c171bb022e47d1f745608ff749e676dbd25f78")
multiStoreCommitInfo = append(multiStoreCommitInfo, SubstoreCommitID{
Name: "gov",
Version: 689,
CommitHash: gocRootHash,
})
multiStoreCommitInfo = append(multiStoreCommitInfo, SubstoreCommitID{
Name: "main",
Version: 689,
CommitHash: nil,
})
accRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828")
multiStoreCommitInfo = append(multiStoreCommitInfo, SubstoreCommitID{
Name: "acc",
Version: 689,
CommitHash: accRootHash,
})
multiStoreCommitInfo = append(multiStoreCommitInfo, SubstoreCommitID{
Name: "ibc",
Version: 689,
CommitHash: nil,
})
stakeRootHash, _ := hex.DecodeString("987d1d27b8771d93aa3691262f661d2c85af7ca4")
multiStoreCommitInfo = append(multiStoreCommitInfo, SubstoreCommitID{
Name: "stake",
Version: 689,
CommitHash: stakeRootHash,
})
slashingRootHash, _ := hex.DecodeString("388ee6e5b11f367069beb1eefd553491afe9d73e")
multiStoreCommitInfo = append(multiStoreCommitInfo, SubstoreCommitID{
Name: "slashing",
Version: 689,
CommitHash: slashingRootHash,
})
commitHash, err := VerifyMultiStoreCommitInfo(storeName, multiStoreCommitInfo, appHash)
assert.Nil(t, err)
assert.Equal(t, commitHash, substoreRootHash)
appHash, _ = hex.DecodeString("29de216bf5e2531c688de36caaf024cd3bb09ee3")
_, err = VerifyMultiStoreCommitInfo(storeName, multiStoreCommitInfo, appHash)
assert.Error(t, err, "appHash doesn't match to the merkle root of multiStoreCommitInfo")
}
func TestVerifyRangeProof(t *testing.T) {
tree := iavl.NewTree(nil, 0)
rand := cmn.NewRand()
rand.Seed(0) // for determinism
for _, ikey := range []byte{0x11, 0x32, 0x50, 0x72, 0x99} {
key := []byte{ikey}
tree.Set(key, []byte(rand.Str(8)))
}
root := tree.Hash()
key := []byte{0x32}
val, proof, err := tree.GetWithProof(key)
assert.Nil(t, err)
assert.NotEmpty(t, val)
assert.NotEmpty(t, proof)
err = VerifyRangeProof(key, val, root, proof)
assert.Nil(t, err)
key = []byte{0x40}
val, proof, err = tree.GetWithProof(key)
assert.Nil(t, err)
assert.Empty(t, val)
assert.NotEmpty(t, proof)
err = VerifyRangeProof(key, val, root, proof)
assert.Nil(t, err)
}
+19
View File
@@ -291,6 +291,25 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery {
// trim the path and make the query
req.Path = subpath
res := queryable.Query(req)
// Currently, only when query subpath is "/store" or "/key", will proof be included in response.
// If there are some changes about proof building in iavlstore.go, we must change code here to keep consistency with iavlstore.go
if !req.Prove || subpath != "/store" && subpath != "/key" {
return res
}
//Load commit info from db
commitInfo, errMsg := getCommitInfo(rs.db,res.Height)
if errMsg != nil {
return sdk.ErrInternal(errMsg.Error()).QueryResult()
}
res.Proof, errMsg = BuildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos)
if errMsg != nil {
return sdk.ErrInternal(errMsg.Error()).QueryResult()
}
return res
}