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
+79
View File
@@ -13,6 +13,11 @@ import (
cmn "github.com/tendermint/tendermint/libs/common"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/wire"
"strings"
tendermintLiteProxy "github.com/tendermint/tendermint/lite/proxy"
abci "github.com/tendermint/tendermint/abci/types"
)
// GetNode returns an RPC client. If the context's client is not defined, an
@@ -304,12 +309,86 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log)
}
// Data from trusted node or subspace query doesn't need verification
if ctx.TrustNode || !isQueryStoreWithProof(path) {
return resp.Value, nil
}
err = ctx.verifyProof(path, resp)
if err != nil {
return nil, err
}
return resp.Value, nil
}
// verifyProof perform response proof verification
func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error {
// TODO: Later we consider to return error for missing valid certifier to verify data from untrusted node
if ctx.Certifier == nil {
if ctx.Logger != nil {
io.WriteString(ctx.Logger, fmt.Sprintf("Missing valid certifier to verify data from untrusted node\n"))
}
return nil
}
node, err := ctx.GetNode()
if err != nil {
return err
}
// TODO: need improvement
// If the the node http client connect to a full node which can't produce or receive new blocks,
// then here the code will wait for a while and return error if time is out.
// AppHash for height H is in header H+1
commit, err := tendermintLiteProxy.GetCertifiedCommit(resp.Height+1, node, ctx.Certifier)
if err != nil {
return err
}
var multiStoreProof store.MultiStoreProof
cdc := wire.NewCodec()
err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof)
if err != nil {
return errors.Wrap(err, "failed to unmarshalBinary rangeProof")
}
// Validate the substore commit hash against trusted appHash
substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(multiStoreProof.StoreName,
multiStoreProof.CommitIDList, commit.Header.AppHash)
if err != nil {
return errors.Wrap(err, "failed in verifying the proof against appHash")
}
err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof)
if err != nil {
return errors.Wrap(err, "failed in the range proof verification")
}
return nil
}
// queryStore performs a query from a Tendermint node with the provided a store
// name and path.
func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([]byte, error) {
path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
return ctx.query(path, key)
}
// isQueryStoreWithProof expects a format like /<queryType>/<storeName>/<subpath>
// queryType can be app or store
// if subpath equals to "/store" or "/key", then return true
func isQueryStoreWithProof(path string) (bool) {
if !strings.HasPrefix(path, "/") {
return false
}
paths := strings.SplitN(path[1:], "/", 3)
if len(paths) != 3 {
return false
}
// 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 paths[2] == "store" || paths[2] == "key" {
return true
}
return false
}