diff --git a/Gopkg.lock b/Gopkg.lock index fa6fafb99d..5e40c17e6c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -34,7 +34,7 @@ [[projects]] branch = "master" - digest = "1:6aabc1566d6351115d561d038da82a4c19b46c3b6e17f4a0a2fa60260663dc79" + digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8" name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" @@ -71,7 +71,7 @@ version = "v1.4.7" [[projects]] - digest = "1:fa30c0652956e159cdb97dcb2ef8b8db63ed668c02a5c3a40961c8f0641252fe" + digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11" name = "github.com/go-kit/kit" packages = [ "log", @@ -103,7 +103,7 @@ version = "v1.7.0" [[projects]] - digest = "1:212285efb97b9ec2e20550d81f0446cb7897e57cbdfd7301b1363ab113d8be45" + digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e" name = "github.com/gogo/protobuf" packages = [ "gogoproto", @@ -118,7 +118,7 @@ version = "v1.1.1" [[projects]] - digest = "1:cb22af0ed7c72d495d8be1106233ee553898950f15fd3f5404406d44c2e86888" + digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260" name = "github.com/golang/protobuf" packages = [ "proto", @@ -165,13 +165,12 @@ [[projects]] branch = "master" - digest = "1:ac64f01acc5eeea9dde40e326de6b6471e501392ec06524c3b51033aa50789bc" + digest = "1:12247a2e99a060cc692f6680e5272c8adf0b8f572e6bce0d7095e624c958a240" name = "github.com/hashicorp/hcl" packages = [ ".", "hcl/ast", "hcl/parser", - "hcl/printer", "hcl/scanner", "hcl/strconv", "hcl/token", @@ -263,7 +262,7 @@ version = "v1.0.0" [[projects]] - digest = "1:98225904b7abff96c052b669b25788f18225a36673fba022fb93514bb9a2a64e" + digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0" name = "github.com/prometheus/client_golang" packages = [ "prometheus", @@ -274,7 +273,7 @@ [[projects]] branch = "master" - digest = "1:0f37e09b3e92aaeda5991581311f8dbf38944b36a3edec61cc2d1991f527554a" + digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" name = "github.com/prometheus/client_model" packages = ["go"] pruneopts = "UT" @@ -282,7 +281,7 @@ [[projects]] branch = "master" - digest = "1:dad2e5a2153ee7a6c9ab8fc13673a16ee4fb64434a7da980965a3741b0c981a3" + digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5" name = "github.com/prometheus/common" packages = [ "expfmt", @@ -294,7 +293,7 @@ [[projects]] branch = "master" - digest = "1:a37c98f4b7a66bb5c539c0539f0915a74ef1c8e0b3b6f45735289d94cae92bfd" + digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290" name = "github.com/prometheus/procfs" packages = [ ".", @@ -313,7 +312,7 @@ revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] - digest = "1:37ace7f35375adec11634126944bdc45a673415e2fcc07382d03b75ec76ea94c" + digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84" name = "github.com/spf13/afero" packages = [ ".", @@ -332,7 +331,7 @@ version = "v1.2.0" [[projects]] - digest = "1:627ab2f549a6a55c44f46fa24a4307f4d0da81bfc7934ed0473bf38b24051d26" + digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" name = "github.com/spf13/cobra" packages = ["."] pruneopts = "UT" @@ -364,7 +363,7 @@ version = "v1.0.0" [[projects]] - digest = "1:73697231b93fb74a73ebd8384b68b9a60c57ea6b13c56d2425414566a72c8e6d" + digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6" name = "github.com/stretchr/testify" packages = [ "assert", @@ -376,7 +375,7 @@ [[projects]] branch = "master" - digest = "1:442d2ffa75ffae302ce8800bf4144696b92bef02917923ea132ce2d39efe7d65" + digest = "1:f2ffd421680b0a3f7887501b3c6974bcf19217ecd301d0e2c9b681940ec363d5" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -397,7 +396,7 @@ [[projects]] branch = "master" - digest = "1:203b409c21115233a576f99e8f13d8e07ad82b25500491f7e1cca12588fb3232" + digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722" name = "github.com/tendermint/ed25519" packages = [ ".", @@ -424,7 +423,7 @@ version = "v0.9.2" [[projects]] - digest = "1:963f6c04345ce36f900c1d6367200eebc3cc2db6ee632ff865ea8dcf64b748a0" + digest = "1:4f15e95fe3888cc75dd34f407d6394cbc7fd3ff24920851b92b295f6a8b556e6" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -491,7 +490,7 @@ version = "v0.23.1-rc0" [[projects]] - digest = "1:ad879bb8c71020a3f92f0c61f414d93eae1d5dc2f37023b6abaa3cc84b00165e" + digest = "1:bf6d9a827ea3cad964c2f863302e4f6823170d0b5ed16f72cf1184a7c615067e" name = "github.com/tendermint/tmlibs" packages = ["cli"] pruneopts = "UT" @@ -507,7 +506,7 @@ [[projects]] branch = "master" - digest = "1:2a3ce1f08dcae8bac666deb6e4c88b5d7170c510da38fd746231144cac351704" + digest = "1:27507554c6d4f060d8d700c31c624a43d3a92baa634e178ddc044bdf7d13b44a" name = "golang.org/x/crypto" packages = [ "blowfish", @@ -529,7 +528,7 @@ revision = "614d502a4dac94afa3a6ce146bd1736da82514c6" [[projects]] - digest = "1:04dda8391c3e2397daf254ac68003f30141c069b228d06baec8324a5f81dc1e9" + digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" name = "golang.org/x/net" packages = [ "context", @@ -546,7 +545,7 @@ [[projects]] branch = "master" - digest = "1:c8baf78f0ac6eb27c645e264fe5e8a74d5a50db188ab41a7ff3b275c112e0735" + digest = "1:86171d21d59449dcf7cee0b7d2da83dff989dab9b9b69bfe0a3d59c3c1ca6081" name = "golang.org/x/sys" packages = [ "cpu", @@ -556,7 +555,7 @@ revision = "11551d06cbcc94edc80a0facaccbda56473c19c1" [[projects]] - digest = "1:7509ba4347d1f8de6ae9be8818b0cd1abc3deeffe28aeaf4be6d4b6b5178d9ca" + digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" name = "golang.org/x/text" packages = [ "collate", @@ -587,7 +586,7 @@ revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4" [[projects]] - digest = "1:4515e3030c440845b046354fd5d57671238428b820deebce2e9dabb5cd3c51ac" + digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" name = "google.golang.org/grpc" packages = [ ".", @@ -664,6 +663,8 @@ "github.com/tendermint/tendermint/libs/common", "github.com/tendermint/tendermint/libs/db", "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/p2p", "github.com/tendermint/tendermint/privval", diff --git a/client/context/context.go b/client/context/context.go index 743c923552..4122cbab30 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -1,14 +1,18 @@ package context import ( - "io" - + "bytes" + "fmt" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" + "io" "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" ) @@ -32,6 +36,7 @@ type CLIContext struct { Async bool JSON bool PrintResponse bool + Certifier tmlite.Certifier } // NewCLIContext returns a new initialized CLIContext with parameters from the @@ -57,9 +62,40 @@ func NewCLIContext() CLIContext { Async: viper.GetBool(client.FlagAsync), JSON: viper.GetBool(client.FlagJson), 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. func (ctx CLIContext) WithCodec(cdc *wire.Codec) CLIContext { ctx.Codec = cdc @@ -117,3 +153,9 @@ func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext { ctx.UseLedger = useLedger 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 +} diff --git a/client/context/query.go b/client/context/query.go index e526c0abbc..4c1cad8777 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -10,9 +10,14 @@ import ( "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" + tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" + "strings" ) // 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) } + // 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 { + + 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 // 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 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 +} diff --git a/client/flags.go b/client/flags.go index 81e0670678..12bddb7d96 100644 --- a/client/flags.go +++ b/client/flags.go @@ -58,6 +58,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") 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(FlagTrustNode, true, "Don't verify proofs for query responses") } return cmds } diff --git a/client/lcd/root.go b/client/lcd/root.go index bfa62f1cf0..f18ee5dfd4 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -4,11 +4,11 @@ import ( "net/http" "os" - client "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" - keys "github.com/cosmos/cosmos-sdk/client/keys" - rpc "github.com/cosmos/cosmos-sdk/client/rpc" - tx "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/wire" auth "github.com/cosmos/cosmos-sdk/x/auth/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.FlagNode, "tcp://localhost:26657", "Address of the node to connect to") cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") + cmd.Flags().Bool(client.FlagTrustNode, false, "Whether trust connected full node") return cmd } diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 818eae1e86..7d9a46403c 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -190,6 +190,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress node, err := startTM(config, logger, genDoc, privVal, app) require.NoError(t, err) + tests.WaitForNextHeightTM(tests.ExtractPortFromAddress(config.RPC.ListenAddress)) lcd, err := startLCD(logger, listenAddr, cdc) require.NoError(t, err) diff --git a/server/export_test.go b/server/export_test.go index 358f72cf60..488c55bbf6 100644 --- a/server/export_test.go +++ b/server/export_test.go @@ -1,16 +1,16 @@ package server 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" + "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" - "github.com/cosmos/cosmos-sdk/server/mock" - ) + "os" + "testing" +) func TestEmptyState(t *testing.T) { defer setupViper(t)() diff --git a/server/mock/app.go b/server/mock/app.go index eb2dfc3cc3..3c6ad3ec27 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -129,7 +129,7 @@ func AppGenStateEmpty(_ *wire.Codec, _ []json.RawMessage) (appState json.RawMess // Return a validator, not much else 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{ PubKey: pk, diff --git a/store/multistoreproof.go b/store/multistoreproof.go new file mode 100644 index 0000000000..e25f1cc1f2 --- /dev/null +++ b/store/multistoreproof.go @@ -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 +} diff --git a/store/multistoreproof_test.go b/store/multistoreproof_test.go new file mode 100644 index 0000000000..b4e8a84b16 --- /dev/null +++ b/store/multistoreproof_test.go @@ -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) +} diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 04f8e44e69..d9cf8a29a2 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -291,6 +291,18 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { // trim the path and make the query req.Path = subpath 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 } diff --git a/tests/util.go b/tests/util.go index 1138bc95e9..48649eaf76 100644 --- a/tests/util.go +++ b/tests/util.go @@ -10,6 +10,7 @@ import ( tmclient "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpcclient "github.com/tendermint/tendermint/rpc/lib/client" + "strings" ) // 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() func init() {