diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index b00c97d23..b4faafdcb 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -69,10 +69,14 @@ func (c *HTTP) ABCIInfo() (*ctypes.ResultABCIInfo, error) { return result, nil } -func (c *HTTP) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { +func (c *HTTP) ABCIQuery(path string, data data.Bytes) (*ctypes.ResultABCIQuery, error) { + return c.ABCIQueryWithOptions(path, data, DefaultABCIQueryOptions) +} + +func (c *HTTP) ABCIQueryWithOptions(path string, data data.Bytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { result := new(ctypes.ResultABCIQuery) _, err := c.rpc.Call("abci_query", - map[string]interface{}{"path": path, "data": data, "prove": prove}, + map[string]interface{}{"path": path, "data": data, "height": opts.Height, "trusted": opts.Trusted}, result) if err != nil { return nil, errors.Wrap(err, "ABCIQuery") diff --git a/rpc/client/interface.go b/rpc/client/interface.go index ed7ccabaf..10689a561 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -31,7 +31,8 @@ import ( type ABCIClient interface { // reading from abci app ABCIInfo() (*ctypes.ResultABCIInfo, error) - ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) + ABCIQuery(path string, data data.Bytes) (*ctypes.ResultABCIQuery, error) + ABCIQueryWithOptions(path string, data data.Bytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) // writing to abci app BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) diff --git a/rpc/client/localclient.go b/rpc/client/localclient.go index 134f935ca..9a14c8684 100644 --- a/rpc/client/localclient.go +++ b/rpc/client/localclient.go @@ -57,8 +57,12 @@ func (c Local) ABCIInfo() (*ctypes.ResultABCIInfo, error) { return core.ABCIInfo() } -func (c Local) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { - return core.ABCIQuery(path, data, prove) +func (c Local) ABCIQuery(path string, data data.Bytes) (*ctypes.ResultABCIQuery, error) { + return c.ABCIQueryWithOptions(path, data, DefaultABCIQueryOptions) +} + +func (c Local) ABCIQueryWithOptions(path string, data data.Bytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { + return core.ABCIQuery(path, data, opts.Height, opts.Trusted) } func (c Local) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { diff --git a/rpc/client/mock/abci.go b/rpc/client/mock/abci.go index db3fa4f1d..a05ce0b8d 100644 --- a/rpc/client/mock/abci.go +++ b/rpc/client/mock/abci.go @@ -24,8 +24,12 @@ func (a ABCIApp) ABCIInfo() (*ctypes.ResultABCIInfo, error) { return &ctypes.ResultABCIInfo{a.App.Info(abci.RequestInfo{version.Version})}, nil } -func (a ABCIApp) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { - q := a.App.Query(abci.RequestQuery{data, path, 0, prove}) +func (a ABCIApp) ABCIQuery(path string, data data.Bytes) (*ctypes.ResultABCIQuery, error) { + return a.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions) +} + +func (a ABCIApp) ABCIQueryWithOptions(path string, data data.Bytes, opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { + q := a.App.Query(abci.RequestQuery{data, path, opts.Height, opts.Trusted}) return &ctypes.ResultABCIQuery{q.Result()}, nil } @@ -79,8 +83,12 @@ func (m ABCIMock) ABCIInfo() (*ctypes.ResultABCIInfo, error) { return &ctypes.ResultABCIInfo{res.(abci.ResponseInfo)}, nil } -func (m ABCIMock) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { - res, err := m.Query.GetResponse(QueryArgs{path, data, prove}) +func (m ABCIMock) ABCIQuery(path string, data data.Bytes) (*ctypes.ResultABCIQuery, error) { + return m.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions) +} + +func (m ABCIMock) ABCIQueryWithOptions(path string, data data.Bytes, opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { + res, err := m.Query.GetResponse(QueryArgs{path, data, opts.Height, opts.Trusted}) if err != nil { return nil, err } @@ -131,9 +139,10 @@ func (r *ABCIRecorder) _assertABCIClient() client.ABCIClient { } type QueryArgs struct { - Path string - Data data.Bytes - Prove bool + Path string + Data data.Bytes + Height uint64 + Trusted bool } func (r *ABCIRecorder) addCall(call Call) { @@ -150,11 +159,15 @@ func (r *ABCIRecorder) ABCIInfo() (*ctypes.ResultABCIInfo, error) { return res, err } -func (r *ABCIRecorder) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { - res, err := r.Client.ABCIQuery(path, data, prove) +func (r *ABCIRecorder) ABCIQuery(path string, data data.Bytes) (*ctypes.ResultABCIQuery, error) { + return r.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions) +} + +func (r *ABCIRecorder) ABCIQueryWithOptions(path string, data data.Bytes, opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { + res, err := r.Client.ABCIQueryWithOptions(path, data, opts) r.addCall(Call{ Name: "abci_query", - Args: QueryArgs{path, data, prove}, + Args: QueryArgs{path, data, opts.Height, opts.Trusted}, Response: res, Error: err, }) diff --git a/rpc/client/mock/abci_test.go b/rpc/client/mock/abci_test.go index 245db6c68..a7afa0894 100644 --- a/rpc/client/mock/abci_test.go +++ b/rpc/client/mock/abci_test.go @@ -12,6 +12,7 @@ import ( "github.com/tendermint/abci/example/dummy" abci "github.com/tendermint/abci/types" data "github.com/tendermint/go-wire/data" + "github.com/tendermint/tendermint/rpc/client" "github.com/tendermint/tendermint/rpc/client/mock" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" @@ -50,7 +51,7 @@ func TestABCIMock(t *testing.T) { assert.Equal("foobar", err.Error()) // query always returns the response - query, err := m.ABCIQuery("/", nil, false) + query, err := m.ABCIQueryWithOptions("/", nil, client.ABCIQueryOptions{Trusted: true}) require.Nil(err) require.NotNil(query) assert.EqualValues(key, query.Key) @@ -92,7 +93,7 @@ func TestABCIRecorder(t *testing.T) { require.Equal(0, len(r.Calls)) r.ABCIInfo() - r.ABCIQuery("path", data.Bytes("data"), true) + r.ABCIQueryWithOptions("path", data.Bytes("data"), client.ABCIQueryOptions{Trusted: false}) require.Equal(2, len(r.Calls)) info := r.Calls[0] @@ -115,7 +116,7 @@ func TestABCIRecorder(t *testing.T) { require.True(ok) assert.Equal("path", qa.Path) assert.EqualValues("data", qa.Data) - assert.True(qa.Prove) + assert.False(qa.Trusted) // now add some broadcasts txs := []types.Tx{{1}, {2}, {3}} @@ -164,7 +165,7 @@ func TestABCIApp(t *testing.T) { assert.True(res.DeliverTx.Code.IsOK()) // check the key - qres, err := m.ABCIQuery("/key", data.Bytes(key), false) + qres, err := m.ABCIQueryWithOptions("/key", data.Bytes(key), client.ABCIQueryOptions{Trusted: true}) require.Nil(err) assert.EqualValues(value, qres.Value) } diff --git a/rpc/client/mock/client.go b/rpc/client/mock/client.go index f32694edd..08d19cea5 100644 --- a/rpc/client/mock/client.go +++ b/rpc/client/mock/client.go @@ -84,8 +84,12 @@ func (c Client) ABCIInfo() (*ctypes.ResultABCIInfo, error) { return core.ABCIInfo() } -func (c Client) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { - return core.ABCIQuery(path, data, prove) +func (c Client) ABCIQuery(path string, data data.Bytes) (*ctypes.ResultABCIQuery, error) { + return c.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions) +} + +func (c Client) ABCIQueryWithOptions(path string, data data.Bytes, opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { + return core.ABCIQuery(path, data, opts.Height, opts.Trusted) } func (c Client) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 9bcd3de45..792f0f271 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -98,6 +98,23 @@ func TestGenesisAndValidators(t *testing.T) { } } +func TestABCIQuery(t *testing.T) { + for i, c := range GetClients() { + // write something + k, v, tx := MakeTxKV() + bres, err := c.BroadcastTxCommit(tx) + require.Nil(t, err, "%d: %+v", i, err) + apph := bres.Height + 1 // this is where the tx will be applied to the state + + // wait before querying + client.WaitForHeight(c, apph, nil) + qres, err := c.ABCIQuery("/key", k) + if assert.Nil(t, err) && assert.True(t, qres.Code.IsOK()) { + assert.EqualValues(t, v, qres.Value) + } + } +} + // Make some app checks func TestAppCalls(t *testing.T) { assert, require := assert.New(t), require.New(t) @@ -124,7 +141,7 @@ func TestAppCalls(t *testing.T) { // wait before querying client.WaitForHeight(c, apph, nil) - qres, err := c.ABCIQuery("/key", k, false) + qres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Trusted: true}) if assert.Nil(err) && assert.True(qres.Code.IsOK()) { // assert.Equal(k, data.GetKey()) // only returned for proofs assert.EqualValues(v, qres.Value) @@ -172,7 +189,7 @@ func TestAppCalls(t *testing.T) { assert.Equal(block.Block.LastCommit, commit2.Commit) // and we got a proof that works! - pres, err := c.ABCIQuery("/key", k, true) + pres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Trusted: false}) if assert.Nil(err) && assert.True(pres.Code.IsOK()) { proof, err := iavl.ReadProof(pres.Proof) if assert.Nil(err) { diff --git a/rpc/client/types.go b/rpc/client/types.go new file mode 100644 index 000000000..dc573edd6 --- /dev/null +++ b/rpc/client/types.go @@ -0,0 +1,12 @@ +package client + +// ABCIQueryOptions can be used to provide options for ABCIQuery call other +// than the DefaultABCIQueryOptions. +type ABCIQueryOptions struct { + Height uint64 + Trusted bool +} + +// DefaultABCIQueryOptions are latest height (0) and trusted equal to false +// (which will result in a proof being returned). +var DefaultABCIQueryOptions = ABCIQueryOptions{Height: 0, Trusted: false} diff --git a/rpc/core/abci.go b/rpc/core/abci.go index 06275a9e3..564c0bc63 100644 --- a/rpc/core/abci.go +++ b/rpc/core/abci.go @@ -41,16 +41,18 @@ import ( // // ### Query Parameters // -// | Parameter | Type | Default | Required | Description | -// |-----------+--------+---------+----------+---------------------------------------| -// | path | string | false | false | Path to the data ("/a/b/c") | -// | data | []byte | false | true | Data | -// | prove | bool | false | false | Include a proof of the data inclusion | -func ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { +// | Parameter | Type | Default | Required | Description | +// |-----------+--------+---------+----------+------------------------------------------------| +// | path | string | false | false | Path to the data ("/a/b/c") | +// | data | []byte | false | true | Data | +// | height | uint64 | 0 | false | Height (0 means latest) | +// | trusted | bool | false | false | Does not include a proof of the data inclusion | +func ABCIQuery(path string, data data.Bytes, height uint64, trusted bool) (*ctypes.ResultABCIQuery, error) { resQuery, err := proxyAppQuery.QuerySync(abci.RequestQuery{ - Path: path, - Data: data, - Prove: prove, + Path: path, + Data: data, + Height: height, + Prove: !trusted, }) if err != nil { return nil, err diff --git a/rpc/core/routes.go b/rpc/core/routes.go index 485f7a00f..b1dbd3785 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -29,7 +29,7 @@ var Routes = map[string]*rpc.RPCFunc{ "broadcast_tx_async": rpc.NewRPCFunc(BroadcastTxAsync, "tx"), // abci API - "abci_query": rpc.NewRPCFunc(ABCIQuery, "path,data,prove"), + "abci_query": rpc.NewRPCFunc(ABCIQuery, "path,data,height,prove"), "abci_info": rpc.NewRPCFunc(ABCIInfo, ""), }