Merge PR #2192: Split LCD implementation PR, part one

This commit is contained in:
Christopher Goes 2018-08-31 18:41:24 +02:00 committed by GitHub
commit 03f79ef744
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 388 additions and 37 deletions

45
Gopkg.lock generated
View File

@ -34,7 +34,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:6aabc1566d6351115d561d038da82a4c19b46c3b6e17f4a0a2fa60260663dc79" digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8"
name = "github.com/btcsuite/btcd" name = "github.com/btcsuite/btcd"
packages = ["btcec"] packages = ["btcec"]
pruneopts = "UT" pruneopts = "UT"
@ -71,7 +71,7 @@
version = "v1.4.7" version = "v1.4.7"
[[projects]] [[projects]]
digest = "1:fa30c0652956e159cdb97dcb2ef8b8db63ed668c02a5c3a40961c8f0641252fe" digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11"
name = "github.com/go-kit/kit" name = "github.com/go-kit/kit"
packages = [ packages = [
"log", "log",
@ -103,7 +103,7 @@
version = "v1.7.0" version = "v1.7.0"
[[projects]] [[projects]]
digest = "1:212285efb97b9ec2e20550d81f0446cb7897e57cbdfd7301b1363ab113d8be45" digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e"
name = "github.com/gogo/protobuf" name = "github.com/gogo/protobuf"
packages = [ packages = [
"gogoproto", "gogoproto",
@ -118,7 +118,7 @@
version = "v1.1.1" version = "v1.1.1"
[[projects]] [[projects]]
digest = "1:cb22af0ed7c72d495d8be1106233ee553898950f15fd3f5404406d44c2e86888" digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260"
name = "github.com/golang/protobuf" name = "github.com/golang/protobuf"
packages = [ packages = [
"proto", "proto",
@ -165,13 +165,12 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:ac64f01acc5eeea9dde40e326de6b6471e501392ec06524c3b51033aa50789bc" digest = "1:12247a2e99a060cc692f6680e5272c8adf0b8f572e6bce0d7095e624c958a240"
name = "github.com/hashicorp/hcl" name = "github.com/hashicorp/hcl"
packages = [ packages = [
".", ".",
"hcl/ast", "hcl/ast",
"hcl/parser", "hcl/parser",
"hcl/printer",
"hcl/scanner", "hcl/scanner",
"hcl/strconv", "hcl/strconv",
"hcl/token", "hcl/token",
@ -263,7 +262,7 @@
version = "v1.0.0" version = "v1.0.0"
[[projects]] [[projects]]
digest = "1:98225904b7abff96c052b669b25788f18225a36673fba022fb93514bb9a2a64e" digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0"
name = "github.com/prometheus/client_golang" name = "github.com/prometheus/client_golang"
packages = [ packages = [
"prometheus", "prometheus",
@ -274,7 +273,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:0f37e09b3e92aaeda5991581311f8dbf38944b36a3edec61cc2d1991f527554a" digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
name = "github.com/prometheus/client_model" name = "github.com/prometheus/client_model"
packages = ["go"] packages = ["go"]
pruneopts = "UT" pruneopts = "UT"
@ -282,7 +281,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:dad2e5a2153ee7a6c9ab8fc13673a16ee4fb64434a7da980965a3741b0c981a3" digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5"
name = "github.com/prometheus/common" name = "github.com/prometheus/common"
packages = [ packages = [
"expfmt", "expfmt",
@ -294,7 +293,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:a37c98f4b7a66bb5c539c0539f0915a74ef1c8e0b3b6f45735289d94cae92bfd" digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290"
name = "github.com/prometheus/procfs" name = "github.com/prometheus/procfs"
packages = [ packages = [
".", ".",
@ -313,7 +312,7 @@
revision = "e2704e165165ec55d062f5919b4b29494e9fa790" revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
[[projects]] [[projects]]
digest = "1:37ace7f35375adec11634126944bdc45a673415e2fcc07382d03b75ec76ea94c" digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84"
name = "github.com/spf13/afero" name = "github.com/spf13/afero"
packages = [ packages = [
".", ".",
@ -332,7 +331,7 @@
version = "v1.2.0" version = "v1.2.0"
[[projects]] [[projects]]
digest = "1:627ab2f549a6a55c44f46fa24a4307f4d0da81bfc7934ed0473bf38b24051d26" digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e"
name = "github.com/spf13/cobra" name = "github.com/spf13/cobra"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
@ -364,7 +363,7 @@
version = "v1.0.0" version = "v1.0.0"
[[projects]] [[projects]]
digest = "1:73697231b93fb74a73ebd8384b68b9a60c57ea6b13c56d2425414566a72c8e6d" digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6"
name = "github.com/stretchr/testify" name = "github.com/stretchr/testify"
packages = [ packages = [
"assert", "assert",
@ -376,7 +375,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:442d2ffa75ffae302ce8800bf4144696b92bef02917923ea132ce2d39efe7d65" digest = "1:f2ffd421680b0a3f7887501b3c6974bcf19217ecd301d0e2c9b681940ec363d5"
name = "github.com/syndtr/goleveldb" name = "github.com/syndtr/goleveldb"
packages = [ packages = [
"leveldb", "leveldb",
@ -397,7 +396,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:203b409c21115233a576f99e8f13d8e07ad82b25500491f7e1cca12588fb3232" digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722"
name = "github.com/tendermint/ed25519" name = "github.com/tendermint/ed25519"
packages = [ packages = [
".", ".",
@ -424,7 +423,7 @@
version = "v0.9.2" version = "v0.9.2"
[[projects]] [[projects]]
digest = "1:963f6c04345ce36f900c1d6367200eebc3cc2db6ee632ff865ea8dcf64b748a0" digest = "1:4f15e95fe3888cc75dd34f407d6394cbc7fd3ff24920851b92b295f6a8b556e6"
name = "github.com/tendermint/tendermint" name = "github.com/tendermint/tendermint"
packages = [ packages = [
"abci/client", "abci/client",
@ -491,7 +490,7 @@
version = "v0.23.1-rc0" version = "v0.23.1-rc0"
[[projects]] [[projects]]
digest = "1:ad879bb8c71020a3f92f0c61f414d93eae1d5dc2f37023b6abaa3cc84b00165e" digest = "1:bf6d9a827ea3cad964c2f863302e4f6823170d0b5ed16f72cf1184a7c615067e"
name = "github.com/tendermint/tmlibs" name = "github.com/tendermint/tmlibs"
packages = ["cli"] packages = ["cli"]
pruneopts = "UT" pruneopts = "UT"
@ -507,7 +506,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:2a3ce1f08dcae8bac666deb6e4c88b5d7170c510da38fd746231144cac351704" digest = "1:27507554c6d4f060d8d700c31c624a43d3a92baa634e178ddc044bdf7d13b44a"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = [ packages = [
"blowfish", "blowfish",
@ -529,7 +528,7 @@
revision = "614d502a4dac94afa3a6ce146bd1736da82514c6" revision = "614d502a4dac94afa3a6ce146bd1736da82514c6"
[[projects]] [[projects]]
digest = "1:04dda8391c3e2397daf254ac68003f30141c069b228d06baec8324a5f81dc1e9" digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
name = "golang.org/x/net" name = "golang.org/x/net"
packages = [ packages = [
"context", "context",
@ -546,7 +545,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:c8baf78f0ac6eb27c645e264fe5e8a74d5a50db188ab41a7ff3b275c112e0735" digest = "1:86171d21d59449dcf7cee0b7d2da83dff989dab9b9b69bfe0a3d59c3c1ca6081"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = [ packages = [
"cpu", "cpu",
@ -556,7 +555,7 @@
revision = "11551d06cbcc94edc80a0facaccbda56473c19c1" revision = "11551d06cbcc94edc80a0facaccbda56473c19c1"
[[projects]] [[projects]]
digest = "1:7509ba4347d1f8de6ae9be8818b0cd1abc3deeffe28aeaf4be6d4b6b5178d9ca" digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
name = "golang.org/x/text" name = "golang.org/x/text"
packages = [ packages = [
"collate", "collate",
@ -587,7 +586,7 @@
revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4" revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4"
[[projects]] [[projects]]
digest = "1:4515e3030c440845b046354fd5d57671238428b820deebce2e9dabb5cd3c51ac" digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
name = "google.golang.org/grpc" name = "google.golang.org/grpc"
packages = [ packages = [
".", ".",
@ -664,6 +663,8 @@
"github.com/tendermint/tendermint/libs/common", "github.com/tendermint/tendermint/libs/common",
"github.com/tendermint/tendermint/libs/db", "github.com/tendermint/tendermint/libs/db",
"github.com/tendermint/tendermint/libs/log", "github.com/tendermint/tendermint/libs/log",
"github.com/tendermint/tendermint/lite",
"github.com/tendermint/tendermint/lite/proxy",
"github.com/tendermint/tendermint/node", "github.com/tendermint/tendermint/node",
"github.com/tendermint/tendermint/p2p", "github.com/tendermint/tendermint/p2p",
"github.com/tendermint/tendermint/privval", "github.com/tendermint/tendermint/privval",

View File

@ -1,14 +1,18 @@
package context package context
import ( import (
"io" "bytes"
"fmt"
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"io"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
tmlite "github.com/tendermint/tendermint/lite"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
rpcclient "github.com/tendermint/tendermint/rpc/client" rpcclient "github.com/tendermint/tendermint/rpc/client"
) )
@ -32,6 +36,7 @@ type CLIContext struct {
Async bool Async bool
JSON bool JSON bool
PrintResponse bool PrintResponse bool
Certifier tmlite.Certifier
} }
// NewCLIContext returns a new initialized CLIContext with parameters from the // NewCLIContext returns a new initialized CLIContext with parameters from the
@ -57,9 +62,40 @@ func NewCLIContext() CLIContext {
Async: viper.GetBool(client.FlagAsync), Async: viper.GetBool(client.FlagAsync),
JSON: viper.GetBool(client.FlagJson), JSON: viper.GetBool(client.FlagJson),
PrintResponse: viper.GetBool(client.FlagPrintResponse), PrintResponse: viper.GetBool(client.FlagPrintResponse),
Certifier: createCertifier(),
} }
} }
func createCertifier() tmlite.Certifier {
trustNode := viper.GetBool(client.FlagTrustNode)
if trustNode {
return nil
}
chainID := viper.GetString(client.FlagChainID)
home := viper.GetString(cli.HomeFlag)
nodeURI := viper.GetString(client.FlagNode)
var errMsg bytes.Buffer
if chainID == "" {
errMsg.WriteString("chain-id ")
}
if home == "" {
errMsg.WriteString("home ")
}
if nodeURI == "" {
errMsg.WriteString("node ")
}
// errMsg is not empty
if errMsg.Len() != 0 {
panic(fmt.Errorf("can't create certifier for distrust mode, empty values from these options: %s", errMsg.String()))
}
certifier, err := tmliteProxy.GetCertifier(chainID, home, nodeURI)
if err != nil {
panic(err)
}
return certifier
}
// WithCodec returns a copy of the context with an updated codec. // WithCodec returns a copy of the context with an updated codec.
func (ctx CLIContext) WithCodec(cdc *wire.Codec) CLIContext { func (ctx CLIContext) WithCodec(cdc *wire.Codec) CLIContext {
ctx.Codec = cdc ctx.Codec = cdc
@ -117,3 +153,9 @@ func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext {
ctx.UseLedger = useLedger ctx.UseLedger = useLedger
return ctx return ctx
} }
// WithCertifier - return a copy of the context with an updated Certifier
func (ctx CLIContext) WithCertifier(certifier tmlite.Certifier) CLIContext {
ctx.Certifier = certifier
return ctx
}

View File

@ -10,9 +10,14 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/wire"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
rpcclient "github.com/tendermint/tendermint/rpc/client" rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
"strings"
) )
// GetNode returns an RPC client. If the context's client is not defined, an // GetNode returns an RPC client. If the context's client is not defined, an
@ -304,12 +309,77 @@ 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) 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 return resp.Value, nil
} }
// verifyProof perform response proof verification
func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error {
if ctx.Certifier == nil {
return fmt.Errorf("missing valid certifier to verify data from untrusted node")
}
node, err := ctx.GetNode()
if err != nil {
return err
}
// AppHash for height H is in header H+1
commit, err := tmliteProxy.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")
}
// Verify the substore commit hash against trusted appHash
substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(multiStoreProof.StoreName,
multiStoreProof.StoreInfos, 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 // queryStore performs a query from a Tendermint node with the provided a store
// name and path. // name and path.
func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([]byte, error) { func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([]byte, error) {
path := fmt.Sprintf("/store/%s/%s", storeName, endPath) path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
return ctx.query(path, key) return ctx.query(path, key)
} }
// isQueryStoreWithProof expects a format like /<queryType>/<storeName>/<subpath>
// queryType can be app or store
func isQueryStoreWithProof(path string) bool {
if !strings.HasPrefix(path, "/") {
return false
}
paths := strings.SplitN(path[1:], "/", 3)
if len(paths) != 3 {
return false
}
if store.RequireProof("/" + paths[2]) {
return true
}
return false
}

View File

@ -58,6 +58,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously")
c.Flags().Bool(FlagJson, false, "return output in json format") c.Flags().Bool(FlagJson, false, "return output in json format")
c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)") c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)")
c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for query responses")
} }
return cmds return cmds
} }

View File

@ -4,11 +4,11 @@ import (
"net/http" "net/http"
"os" "os"
client "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
keys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/keys"
rpc "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/rpc"
tx "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
@ -66,6 +66,7 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command {
cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to") cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to")
cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to") cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to")
cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections")
cmd.Flags().Bool(client.FlagTrustNode, false, "Whether trust connected full node")
return cmd return cmd
} }

View File

@ -190,6 +190,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
node, err := startTM(config, logger, genDoc, privVal, app) node, err := startTM(config, logger, genDoc, privVal, app)
require.NoError(t, err) require.NoError(t, err)
tests.WaitForNextHeightTM(tests.ExtractPortFromAddress(config.RPC.ListenAddress))
lcd, err := startLCD(logger, listenAddr, cdc) lcd, err := startLCD(logger, listenAddr, cdc)
require.NoError(t, err) require.NoError(t, err)

View File

@ -1,16 +1,16 @@
package server package server
import ( import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/tendermint/tendermint/libs/log"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"os"
"bytes" "bytes"
"github.com/cosmos/cosmos-sdk/server/mock"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/stretchr/testify/require"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tendermint/libs/log"
"io" "io"
"github.com/cosmos/cosmos-sdk/server/mock" "os"
) "testing"
)
func TestEmptyState(t *testing.T) { func TestEmptyState(t *testing.T) {
defer setupViper(t)() defer setupViper(t)()

View File

@ -129,7 +129,7 @@ func AppGenStateEmpty(_ *wire.Codec, _ []json.RawMessage) (appState json.RawMess
// Return a validator, not much else // Return a validator, not much else
func AppGenTx(_ *wire.Codec, pk crypto.PubKey, genTxConfig gc.GenTx) ( func AppGenTx(_ *wire.Codec, pk crypto.PubKey, genTxConfig gc.GenTx) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
validator = tmtypes.GenesisValidator{ validator = tmtypes.GenesisValidator{
PubKey: pk, PubKey: pk,

91
store/multistoreproof.go Normal file
View File

@ -0,0 +1,91 @@
package store
import (
"bytes"
"github.com/pkg/errors"
"github.com/tendermint/iavl"
cmn "github.com/tendermint/tendermint/libs/common"
)
// MultiStoreProof defines a collection of store proofs in a multi-store
type MultiStoreProof struct {
StoreInfos []storeInfo
StoreName string
RangeProof iavl.RangeProof
}
// buildMultiStoreProof build MultiStoreProof based on iavl proof and storeInfos
func buildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []storeInfo) []byte {
var rangeProof iavl.RangeProof
cdc.MustUnmarshalBinary(iavlProof, &rangeProof)
msp := MultiStoreProof{
StoreInfos: storeInfos,
StoreName: storeName,
RangeProof: rangeProof,
}
proof := cdc.MustMarshalBinary(msp)
return proof
}
// VerifyMultiStoreCommitInfo verify multiStoreCommitInfo against appHash
func VerifyMultiStoreCommitInfo(storeName string, storeInfos []storeInfo, appHash []byte) ([]byte, error) {
var substoreCommitHash []byte
var height int64
for _, storeInfo := range storeInfos {
if storeInfo.Name == storeName {
substoreCommitHash = storeInfo.Core.CommitID.Hash
height = storeInfo.Core.CommitID.Version
}
}
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
}
// VerifyRangeProof verify iavl RangeProof
func VerifyRangeProof(key, value []byte, substoreCommitHash []byte, rangeProof *iavl.RangeProof) error {
// verify 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 {
// verify existence proof
err = rangeProof.VerifyItem(key, value)
if err != nil {
return errors.Wrap(err, "failed in existence verification")
}
} else {
// verify absence proof
err = rangeProof.VerifyAbsence(key)
if err != nil {
return errors.Wrap(err, "failed in absence verification")
}
}
return nil
}
// RequireProof return whether proof is require for the subpath
func RequireProof(subpath string) bool {
// 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:212
if subpath == "/store" || subpath == "/key" {
return true
}
return false
}

View File

@ -0,0 +1,120 @@
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 storeInfos []storeInfo
gocRootHash, _ := hex.DecodeString("62c171bb022e47d1f745608ff749e676dbd25f78")
storeInfos = append(storeInfos, storeInfo{
Name: "gov",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: gocRootHash,
},
},
})
storeInfos = append(storeInfos, storeInfo{
Name: "main",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: nil,
},
},
})
accRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828")
storeInfos = append(storeInfos, storeInfo{
Name: "acc",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: accRootHash,
},
},
})
storeInfos = append(storeInfos, storeInfo{
Name: "ibc",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: nil,
},
},
})
stakeRootHash, _ := hex.DecodeString("987d1d27b8771d93aa3691262f661d2c85af7ca4")
storeInfos = append(storeInfos, storeInfo{
Name: "stake",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: stakeRootHash,
},
},
})
slashingRootHash, _ := hex.DecodeString("388ee6e5b11f367069beb1eefd553491afe9d73e")
storeInfos = append(storeInfos, storeInfo{
Name: "slashing",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: slashingRootHash,
},
},
})
commitHash, err := VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash)
assert.Nil(t, err)
assert.Equal(t, commitHash, substoreRootHash)
appHash, _ = hex.DecodeString("29de216bf5e2531c688de36caaf024cd3bb09ee3")
_, err = VerifyMultiStoreCommitInfo(storeName, storeInfos, 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)
}

View File

@ -291,6 +291,18 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery {
// trim the path and make the query // trim the path and make the query
req.Path = subpath req.Path = subpath
res := queryable.Query(req) res := queryable.Query(req)
if !req.Prove || !RequireProof(subpath) {
return res
}
commitInfo, errMsg := getCommitInfo(rs.db, res.Height)
if errMsg != nil {
return sdk.ErrInternal(errMsg.Error()).QueryResult()
}
res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos)
return res return res
} }

View File

@ -10,6 +10,7 @@ import (
tmclient "github.com/tendermint/tendermint/rpc/client" tmclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpcclient "github.com/tendermint/tendermint/rpc/lib/client" rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
"strings"
) )
// Wait for the next tendermint block from the Tendermint RPC // Wait for the next tendermint block from the Tendermint RPC
@ -185,6 +186,17 @@ func WaitForRPC(laddr string) {
} }
} }
// ExtractPortFromAddress extract port from listenAddress
// The listenAddress must be some strings like tcp://0.0.0.0:12345
func ExtractPortFromAddress(listenAddress string) string {
stringList := strings.Split(listenAddress, ":")
length := len(stringList)
if length != 3 {
panic(fmt.Errorf("expected listen address: tcp://0.0.0.0:12345, got %s", listenAddress))
}
return stringList[2]
}
var cdc = amino.NewCodec() var cdc = amino.NewCodec()
func init() { func init() {