From c4ead8b8a373202034260fa78c958656b74540a4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 18 Oct 2017 18:20:22 +0200 Subject: [PATCH] Expose height flag in all queries --- app/store.go | 11 ++++++----- client/commands/query/get.go | 18 +++++++++++------- client/commands/query/state.go | 2 +- client/query.go | 11 +++++++++-- client/query_test.go | 12 ++++++++++-- .../counter/cmd/countercli/commands/query.go | 2 +- modules/coin/commands/query.go | 2 +- modules/coin/rest/handlers.go | 17 ++++++++++++++--- modules/eyes/commands/query.go | 2 +- modules/ibc/commands/query.go | 12 ++++++------ modules/nonce/commands/query.go | 2 +- modules/nonce/rest/handlers.go | 14 +++++++++++++- modules/roles/commands/query.go | 2 +- 13 files changed, 75 insertions(+), 32 deletions(-) diff --git a/app/store.go b/app/store.go index a19aa968c..24b465ee9 100644 --- a/app/store.go +++ b/app/store.go @@ -146,11 +146,12 @@ func (app *StoreApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQu // we must retrun most recent, even if apphash // is not yet in the blockchain - // if tree.Tree.VersionExists(app.height - 1) { - // height = app.height - 1 - // } else { - height = app.CommittedHeight() - // } + withProof := app.CommittedHeight() - 1 + if tree.Tree.VersionExists(withProof) { + height = withProof + } else { + height = app.CommittedHeight() + } } resQuery.Height = height diff --git a/client/commands/query/get.go b/client/commands/query/get.go index 08bbc45ed..8e1284c48 100644 --- a/client/commands/query/get.go +++ b/client/commands/query/get.go @@ -25,8 +25,8 @@ import ( // It will try to get the proof for the given key. If it is successful, // it will return the height and also unserialize proof.Data into the data // argument (so pass in a pointer to the appropriate struct) -func GetParsed(key []byte, data interface{}, prove bool) (uint64, error) { - bs, h, err := Get(key, prove) +func GetParsed(key []byte, data interface{}, height int, prove bool) (uint64, error) { + bs, h, err := Get(key, height, prove) if err != nil { return 0, err } @@ -45,27 +45,31 @@ func GetParsed(key []byte, data interface{}, prove bool) (uint64, error) { // we just repeat whatever any (potentially malicious) node gives us. // Only use that if you are running the full node yourself, // and it is localhost or you have a secure connection (not HTTP) -func Get(key []byte, prove bool) (data.Bytes, uint64, error) { +func Get(key []byte, height int, prove bool) (data.Bytes, uint64, error) { + if height < 0 { + return nil, 0, fmt.Errorf("Height cannot be negative") + } + if !prove { node := commands.GetNode() resp, err := node.ABCIQueryWithOptions("/key", key, - rpcclient.ABCIQueryOptions{Trusted: true}) + rpcclient.ABCIQueryOptions{Trusted: true, Height: uint64(height)}) return data.Bytes(resp.Value), resp.Height, err } - val, h, _, err := GetWithProof(key) + val, h, _, err := GetWithProof(key, height) return val, h, err } // GetWithProof returns the values stored under a given key at the named // height as in Get. Additionally, it will return a validated merkle // proof for the key-value pair if it exists, and all checks pass. -func GetWithProof(key []byte) (data.Bytes, uint64, iavl.KeyProof, error) { +func GetWithProof(key []byte, height int) (data.Bytes, uint64, iavl.KeyProof, error) { node := commands.GetNode() cert, err := commands.GetCertifier() if err != nil { return nil, 0, nil, err } - return client.GetWithProof(key, node, cert) + return client.GetWithProof(key, height, node, cert) } // ParseHexKey parses the key flag as hex and converts to bytes or returns error diff --git a/client/commands/query/state.go b/client/commands/query/state.go index 13dae0ea8..4412d3e10 100644 --- a/client/commands/query/state.go +++ b/client/commands/query/state.go @@ -28,7 +28,7 @@ func keyQueryCmd(cmd *cobra.Command, args []string) error { } prove := !viper.GetBool(commands.FlagTrustNode) - val, h, err := Get(key, prove) + val, h, err := Get(key, GetHeight(), prove) if err != nil { return err } diff --git a/client/query.go b/client/query.go index 70184bea6..c6c058943 100644 --- a/client/query.go +++ b/client/query.go @@ -17,10 +17,17 @@ import ( // If there is any error in checking, returns an error. // If val is non-empty, proof should be KeyExistsProof // If val is empty, proof should be KeyMissingProof -func GetWithProof(key []byte, node client.Client, cert certifiers.Certifier) ( +func GetWithProof(key []byte, reqHeight int, node client.Client, + cert certifiers.Certifier) ( val data.Bytes, height uint64, proof iavl.KeyProof, err error) { - resp, err := node.ABCIQuery("/key", key) + if reqHeight < 0 { + err = errors.Errorf("Height cannot be negative") + return + } + + resp, err := node.ABCIQueryWithOptions("/key", key, + client.ABCIQueryOptions{Height: uint64(reqHeight)}) if err != nil { return } diff --git a/client/query_test.go b/client/query_test.go index f8031ee83..8faff5599 100644 --- a/client/query_test.go +++ b/client/query_test.go @@ -56,6 +56,7 @@ func TestAppProofs(t *testing.T) { require.NoError(err, "%+v", err) require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx) require.EqualValues(0, br.DeliverTx.Code) + brh := br.Height // This sets up our trust on the node based on some past point. source := certclient.New(cl) @@ -71,7 +72,14 @@ func TestAppProofs(t *testing.T) { // Test existing key. var data eyes.Data - bs, height, proof, err := GetWithProof(k, cl, cert) + // verify a query before the tx block has no data (and valid non-exist proof) + bs, height, proof, err := GetWithProof(k, brh-1, cl, cert) + require.NotNil(err) + require.True(lc.IsNoDataErr(err)) + require.Nil(bs) + + // but given that block it is good + bs, height, proof, err = GetWithProof(k, brh, cl, cert) require.NoError(err, "%+v", err) require.NotNil(proof) require.True(height >= uint64(latest.Header.Height)) @@ -89,7 +97,7 @@ func TestAppProofs(t *testing.T) { // Test non-existing key. missing := []byte("my-missing-key") - bs, _, proof, err = GetWithProof(missing, cl, cert) + bs, _, proof, err = GetWithProof(missing, 0, cl, cert) require.True(lc.IsNoDataErr(err)) require.Nil(bs) require.NotNil(proof) diff --git a/examples/counter/cmd/countercli/commands/query.go b/examples/counter/cmd/countercli/commands/query.go index d55f6db48..40a42df72 100644 --- a/examples/counter/cmd/countercli/commands/query.go +++ b/examples/counter/cmd/countercli/commands/query.go @@ -23,7 +23,7 @@ func counterQueryCmd(cmd *cobra.Command, args []string) error { prove := !viper.GetBool(commands.FlagTrustNode) key := stack.PrefixedKey(counter.NameCounter, counter.StateKey()) - h, err := query.GetParsed(key, &cp, prove) + h, err := query.GetParsed(key, &cp, query.GetHeight(), prove) if err != nil { return err } diff --git a/modules/coin/commands/query.go b/modules/coin/commands/query.go index ca0cced01..c1e0d9f55 100644 --- a/modules/coin/commands/query.go +++ b/modules/coin/commands/query.go @@ -34,7 +34,7 @@ func accountQueryCmd(cmd *cobra.Command, args []string) error { acc := coin.Account{} prove := !viper.GetBool(commands.FlagTrustNode) - height, err := query.GetParsed(key, &acc, prove) + height, err := query.GetParsed(key, &acc, query.GetHeight(), prove) if lc.IsNoDataErr(err) { return errors.Errorf("Account bytes are empty for address %s ", addr) } else if err != nil { diff --git a/modules/coin/rest/handlers.go b/modules/coin/rest/handlers.go index 49250d5e2..443cc9408 100644 --- a/modules/coin/rest/handlers.go +++ b/modules/coin/rest/handlers.go @@ -3,6 +3,7 @@ package rest import ( "fmt" "net/http" + "strconv" "strings" "github.com/gorilla/mux" @@ -32,7 +33,7 @@ type SendInput struct { To *sdk.Actor `json:"to"` From *sdk.Actor `json:"from"` - Amount coin.Coins `json:"amount"` + Amount coin.Coins `json:"amount"` } // doQueryAccount is the HTTP handlerfunc to query an account @@ -45,11 +46,22 @@ func doQueryAccount(w http.ResponseWriter, r *http.Request) { common.WriteError(w, err) return } + + var h int + qHeight := args["height"] + if qHeight != "" { + h, err = strconv.Atoi(qHeight) + if err != nil { + common.WriteError(w, err) + return + } + } + actor = coin.ChainAddr(actor) key := stack.PrefixedKey(coin.NameCoin, actor.Bytes()) account := new(coin.Account) prove := !viper.GetBool(commands.FlagTrustNode) - height, err := query.GetParsed(key, account, prove) + height, err := query.GetParsed(key, account, h, prove) if lightclient.IsNoDataErr(err) { err := fmt.Errorf("account bytes are empty for address: %q", signature) common.WriteError(w, err) @@ -152,4 +164,3 @@ func RegisterAll(r *mux.Router) error { } // End of mux.Router registrars - diff --git a/modules/eyes/commands/query.go b/modules/eyes/commands/query.go index 6e71e88c6..aa6865d92 100644 --- a/modules/eyes/commands/query.go +++ b/modules/eyes/commands/query.go @@ -35,7 +35,7 @@ func eyesQueryCmd(cmd *cobra.Command, args []string) error { key = stack.PrefixedKey(eyes.Name, key) prove := !viper.GetBool(commands.FlagTrustNode) - height, err := query.GetParsed(key, &res, prove) + height, err := query.GetParsed(key, &res, query.GetHeight(), prove) if err != nil { return err } diff --git a/modules/ibc/commands/query.go b/modules/ibc/commands/query.go index e4fec6953..fb1da6506 100644 --- a/modules/ibc/commands/query.go +++ b/modules/ibc/commands/query.go @@ -87,7 +87,7 @@ func ibcQueryCmd(cmd *cobra.Command, args []string) error { var res ibc.HandlerInfo key := stack.PrefixedKey(ibc.NameIBC, ibc.HandlerKey()) prove := !viper.GetBool(commands.FlagTrustNode) - h, err := query.GetParsed(key, &res, prove) + h, err := query.GetParsed(key, &res, query.GetHeight(), prove) if err != nil { return err } @@ -98,7 +98,7 @@ func chainsQueryCmd(cmd *cobra.Command, args []string) error { list := [][]byte{} key := stack.PrefixedKey(ibc.NameIBC, ibc.ChainsKey()) prove := !viper.GetBool(commands.FlagTrustNode) - h, err := query.GetParsed(key, &list, prove) + h, err := query.GetParsed(key, &list, query.GetHeight(), prove) if err != nil { return err } @@ -121,7 +121,7 @@ func chainQueryCmd(cmd *cobra.Command, args []string) error { var res ibc.ChainInfo key := stack.PrefixedKey(ibc.NameIBC, ibc.ChainKey(arg)) prove := !viper.GetBool(commands.FlagTrustNode) - h, err := query.GetParsed(key, &res, prove) + h, err := query.GetParsed(key, &res, query.GetHeight(), prove) if err != nil { return err } @@ -158,7 +158,7 @@ func packetsQueryCmd(cmd *cobra.Command, args []string) error { var res uint64 prove := !viper.GetBool(commands.FlagTrustNode) - h, err := query.GetParsed(key, &res, prove) + h, err := query.GetParsed(key, &res, query.GetHeight(), prove) if err != nil { return err } @@ -190,7 +190,7 @@ func packetQueryCmd(cmd *cobra.Command, args []string) error { // Input queue just display the results var packet ibc.Packet if from != "" { - h, err := query.GetParsed(key, &packet, prove) + h, err := query.GetParsed(key, &packet, query.GetHeight(), prove) if err != nil { return err } @@ -198,7 +198,7 @@ func packetQueryCmd(cmd *cobra.Command, args []string) error { } // output queue, create a post packet - bs, height, proof, err := query.GetWithProof(key) + bs, height, proof, err := query.GetWithProof(key, query.GetHeight()) if err != nil { return err } diff --git a/modules/nonce/commands/query.go b/modules/nonce/commands/query.go index e4652638e..860f7b3aa 100644 --- a/modules/nonce/commands/query.go +++ b/modules/nonce/commands/query.go @@ -45,7 +45,7 @@ func nonceQueryCmd(cmd *cobra.Command, args []string) error { func doNonceQuery(signers []sdk.Actor) (sequence uint32, height uint64, err error) { key := stack.PrefixedKey(nonce.NameNonce, nonce.GetSeqKey(signers)) prove := !viper.GetBool(commands.FlagTrustNode) - height, err = query.GetParsed(key, &sequence, prove) + height, err = query.GetParsed(key, &sequence, query.GetHeight(), prove) if lc.IsNoDataErr(err) { // no data, return sequence 0 return 0, 0, nil diff --git a/modules/nonce/rest/handlers.go b/modules/nonce/rest/handlers.go index b27e7512f..3a3077616 100644 --- a/modules/nonce/rest/handlers.go +++ b/modules/nonce/rest/handlers.go @@ -3,6 +3,7 @@ package rest import ( "fmt" "net/http" + "strconv" "github.com/gorilla/mux" "github.com/spf13/viper" @@ -28,6 +29,17 @@ func doQueryNonce(w http.ResponseWriter, r *http.Request) { common.WriteError(w, err) return } + + var h int + qHeight := args["height"] + if qHeight != "" { + h, err = strconv.Atoi(qHeight) + if err != nil { + common.WriteError(w, err) + return + } + } + actor = coin.ChainAddr(actor) key := nonce.GetSeqKey([]sdk.Actor{actor}) key = stack.PrefixedKey(nonce.NameNonce, key) @@ -35,7 +47,7 @@ func doQueryNonce(w http.ResponseWriter, r *http.Request) { prove := !viper.GetBool(commands.FlagTrustNode) // query sequence number - data, height, err := query.Get(key, prove) + data, height, err := query.Get(key, h, prove) if lightclient.IsNoDataErr(err) { err = fmt.Errorf("nonce empty for address: %q", signature) common.WriteError(w, err) diff --git a/modules/roles/commands/query.go b/modules/roles/commands/query.go index fb2b077cf..5fbc22405 100644 --- a/modules/roles/commands/query.go +++ b/modules/roles/commands/query.go @@ -30,7 +30,7 @@ func roleQueryCmd(cmd *cobra.Command, args []string) error { var res roles.Role key := stack.PrefixedKey(roles.NameRole, role) prove := !viper.GetBool(commands.FlagTrustNode) - height, err := query.GetParsed(key, &res, prove) + height, err := query.GetParsed(key, &res, query.GetHeight(), prove) if err != nil { return err }