Browse Source

limit /tx_search output

Refs #909
pull/1567/head
Anton Kaliaev 7 years ago
parent
commit
a6b74b82d1
No known key found for this signature in database GPG Key ID: 7B6881D965918214
9 changed files with 94 additions and 51 deletions
  1. +8
    -6
      rpc/client/httpclient.go
  2. +1
    -1
      rpc/client/interface.go
  3. +2
    -2
      rpc/client/localclient.go
  4. +7
    -7
      rpc/client/rpc_test.go
  5. +6
    -0
      rpc/core/README.md
  6. +20
    -0
      rpc/core/pipe.go
  7. +1
    -1
      rpc/core/routes.go
  8. +43
    -34
      rpc/core/tx.go
  9. +6
    -0
      rpc/core/types/responses.go

+ 8
- 6
rpc/client/httpclient.go View File

@ -204,17 +204,19 @@ func (c *HTTP) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
return result, nil return result, nil
} }
func (c *HTTP) TxSearch(query string, prove bool) ([]*ctypes.ResultTx, error) {
results := new([]*ctypes.ResultTx)
func (c *HTTP) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
result := new(ctypes.ResultTxSearch)
params := map[string]interface{}{ params := map[string]interface{}{
"query": query,
"prove": prove,
"query": query,
"prove": prove,
"page": page,
"per_page": perPage,
} }
_, err := c.rpc.Call("tx_search", params, results)
_, err := c.rpc.Call("tx_search", params, result)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "TxSearch") return nil, errors.Wrap(err, "TxSearch")
} }
return *results, nil
return result, nil
} }
func (c *HTTP) Validators(height *int64) (*ctypes.ResultValidators, error) { func (c *HTTP) Validators(height *int64) (*ctypes.ResultValidators, error) {


+ 1
- 1
rpc/client/interface.go View File

@ -50,7 +50,7 @@ type SignClient interface {
Commit(height *int64) (*ctypes.ResultCommit, error) Commit(height *int64) (*ctypes.ResultCommit, error)
Validators(height *int64) (*ctypes.ResultValidators, error) Validators(height *int64) (*ctypes.ResultValidators, error)
Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error)
TxSearch(query string, prove bool) ([]*ctypes.ResultTx, error)
TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error)
} }
// HistoryClient shows us data from genesis to now in large chunks. // HistoryClient shows us data from genesis to now in large chunks.


+ 2
- 2
rpc/client/localclient.go View File

@ -128,8 +128,8 @@ func (Local) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
return core.Tx(hash, prove) return core.Tx(hash, prove)
} }
func (Local) TxSearch(query string, prove bool) ([]*ctypes.ResultTx, error) {
return core.TxSearch(query, prove)
func (Local) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
return core.TxSearch(query, prove, page, perPage)
} }
func (c *Local) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, out chan<- interface{}) error { func (c *Local) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, out chan<- interface{}) error {


+ 7
- 7
rpc/client/rpc_test.go View File

@ -334,11 +334,11 @@ func TestTxSearch(t *testing.T) {
// now we query for the tx. // now we query for the tx.
// since there's only one tx, we know index=0. // since there's only one tx, we know index=0.
results, err := c.TxSearch(fmt.Sprintf("tx.hash='%v'", txHash), true)
result, err := c.TxSearch(fmt.Sprintf("tx.hash='%v'", txHash), true, 1, 30)
require.Nil(t, err, "%+v", err) require.Nil(t, err, "%+v", err)
require.Len(t, results, 1)
require.Len(t, result.Txs, 1)
ptx := results[0]
ptx := result.Txs[0]
assert.EqualValues(t, txHeight, ptx.Height) assert.EqualValues(t, txHeight, ptx.Height)
assert.EqualValues(t, tx, ptx.Tx) assert.EqualValues(t, tx, ptx.Tx)
assert.Zero(t, ptx.Index) assert.Zero(t, ptx.Index)
@ -352,14 +352,14 @@ func TestTxSearch(t *testing.T) {
} }
// we query for non existing tx // we query for non existing tx
results, err = c.TxSearch(fmt.Sprintf("tx.hash='%X'", anotherTxHash), false)
result, err = c.TxSearch(fmt.Sprintf("tx.hash='%X'", anotherTxHash), false, 1, 30)
require.Nil(t, err, "%+v", err) require.Nil(t, err, "%+v", err)
require.Len(t, results, 0)
require.Len(t, result.Txs, 0)
// we query using a tag (see kvstore application) // we query using a tag (see kvstore application)
results, err = c.TxSearch("app.creator='jae'", false)
result, err = c.TxSearch("app.creator='jae'", false, 1, 30)
require.Nil(t, err, "%+v", err) require.Nil(t, err, "%+v", err)
if len(results) == 0 {
if len(result.Txs) == 0 {
t.Fatal("expected a lot of transactions") t.Fatal("expected a lot of transactions")
} }
} }


+ 6
- 0
rpc/core/README.md View File

@ -13,3 +13,9 @@ go get github.com/melekes/godoc2md
godoc2md -template rpc/core/doc_template.txt github.com/tendermint/tendermint/rpc/core | grep -v -e "pipe.go" -e "routes.go" -e "dev.go" | sed 's$/src/target$https://github.com/tendermint/tendermint/tree/master/rpc/core$' godoc2md -template rpc/core/doc_template.txt github.com/tendermint/tendermint/rpc/core | grep -v -e "pipe.go" -e "routes.go" -e "dev.go" | sed 's$/src/target$https://github.com/tendermint/tendermint/tree/master/rpc/core$'
``` ```
## Pagination
Requests that return multiple items will be paginated to 30 items by default.
You can specify further pages with the ?page parameter. You can also set a
custom page size up to 100 with the ?per_page parameter.

+ 20
- 0
rpc/core/pipe.go View File

@ -14,6 +14,12 @@ import (
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
) )
const (
// see README
defaultPerPage = 30
maxPerPage = 100
)
var subscribeTimeout = 5 * time.Second var subscribeTimeout = 5 * time.Second
//---------------------------------------------- //----------------------------------------------
@ -117,3 +123,17 @@ func SetLogger(l log.Logger) {
func SetEventBus(b *types.EventBus) { func SetEventBus(b *types.EventBus) {
eventBus = b eventBus = b
} }
func validatePage(page int) int {
if page < 1 {
return 1
}
return page
}
func validatePerPage(perPage int) int {
if perPage < 1 || perPage > maxPerPage {
return defaultPerPage
}
return perPage
}

+ 1
- 1
rpc/core/routes.go View File

@ -22,7 +22,7 @@ var Routes = map[string]*rpc.RPCFunc{
"block_results": rpc.NewRPCFunc(BlockResults, "height"), "block_results": rpc.NewRPCFunc(BlockResults, "height"),
"commit": rpc.NewRPCFunc(Commit, "height"), "commit": rpc.NewRPCFunc(Commit, "height"),
"tx": rpc.NewRPCFunc(Tx, "hash,prove"), "tx": rpc.NewRPCFunc(Tx, "hash,prove"),
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove"),
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page"),
"validators": rpc.NewRPCFunc(Validators, "height"), "validators": rpc.NewRPCFunc(Validators, "height"),
"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""), "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""),
"consensus_state": rpc.NewRPCFunc(ConsensusState, ""), "consensus_state": rpc.NewRPCFunc(ConsensusState, ""),


+ 43
- 34
rpc/core/tx.go View File

@ -6,6 +6,7 @@ import (
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/state/txindex/null" "github.com/tendermint/tendermint/state/txindex/null"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
tmquery "github.com/tendermint/tmlibs/pubsub/query" tmquery "github.com/tendermint/tmlibs/pubsub/query"
) )
@ -104,7 +105,8 @@ func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
}, nil }, nil
} }
// TxSearch allows you to query for multiple transactions results.
// TxSearch allows you to query for multiple transactions results. It returns a
// list of transactions (maximum ?per_page entries) and the total count.
// //
// ```shell // ```shell
// curl "localhost:46657/tx_search?query=\"account.owner='Ivan'\"&prove=true" // curl "localhost:46657/tx_search?query=\"account.owner='Ivan'\"&prove=true"
@ -120,43 +122,46 @@ func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
// //
// ```json // ```json
// { // {
// "result": [
// {
// "proof": {
// "Proof": {
// "aunts": [
// "J3LHbizt806uKnABNLwG4l7gXCA=",
// "iblMO/M1TnNtlAefJyNCeVhjAb0=",
// "iVk3ryurVaEEhdeS0ohAJZ3wtB8=",
// "5hqMkTeGqpct51ohX0lZLIdsn7Q=",
// "afhsNxFnLlZgFDoyPpdQSe0bR8g="
// ]
// },
// "Data": "mvZHHa7HhZ4aRT0xMDA=",
// "RootHash": "F6541223AA46E428CB1070E9840D2C3DF3B6D776",
// "Total": 32,
// "Index": 31
// },
// "tx": "mvZHHa7HhZ4aRT0xMDA=",
// "tx_result": {},
// "index": 31,
// "height": 12,
// "hash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF"
// }
// ],
// "jsonrpc": "2.0",
// "id": "", // "id": "",
// "jsonrpc": "2.0"
// "result": {
// "txs": [
// {
// "proof": {
// "Proof": {
// "aunts": [
// "J3LHbizt806uKnABNLwG4l7gXCA=",
// "iblMO/M1TnNtlAefJyNCeVhjAb0=",
// "iVk3ryurVaEEhdeS0ohAJZ3wtB8=",
// "5hqMkTeGqpct51ohX0lZLIdsn7Q=",
// "afhsNxFnLlZgFDoyPpdQSe0bR8g="
// ]
// },
// "Data": "mvZHHa7HhZ4aRT0xMDA=",
// "RootHash": "F6541223AA46E428CB1070E9840D2C3DF3B6D776",
// "Total": 32,
// "Index": 31
// },
// "tx": "mvZHHa7HhZ4aRT0xMDA=",
// "tx_result": {},
// "index": 31,
// "height": 12,
// "hash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF"
// }
// ],
// "total_count": 1
// }
// } // }
// ``` // ```
// //
// Returns transactions matching the given query.
//
// ### Query Parameters // ### Query Parameters
// //
// | Parameter | Type | Default | Required | Description | // | Parameter | Type | Default | Required | Description |
// |-----------+--------+---------+----------+-----------------------------------------------------------| // |-----------+--------+---------+----------+-----------------------------------------------------------|
// | query | string | "" | true | Query | // | query | string | "" | true | Query |
// | prove | bool | false | false | Include proofs of the transactions inclusion in the block | // | prove | bool | false | false | Include proofs of the transactions inclusion in the block |
// | page | int | 1 | false | Page number (1-based) |
// | per_page | int | 30 | false | Number of entries per page (max: 100) |
// //
// ### Returns // ### Returns
// //
@ -166,7 +171,7 @@ func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
// - `index`: `int` - index of the transaction // - `index`: `int` - index of the transaction
// - `height`: `int` - height of the block where this transaction was in // - `height`: `int` - height of the block where this transaction was in
// - `hash`: `[]byte` - hash of the transaction // - `hash`: `[]byte` - hash of the transaction
func TxSearch(query string, prove bool) ([]*ctypes.ResultTx, error) {
func TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
// if index is disabled, return error // if index is disabled, return error
if _, ok := txIndexer.(*null.TxIndex); ok { if _, ok := txIndexer.(*null.TxIndex); ok {
return nil, fmt.Errorf("Transaction indexing is disabled") return nil, fmt.Errorf("Transaction indexing is disabled")
@ -182,11 +187,15 @@ func TxSearch(query string, prove bool) ([]*ctypes.ResultTx, error) {
return nil, err return nil, err
} }
// TODO: we may want to consider putting a maximum on this length and somehow
// informing the user that things were truncated.
apiResults := make([]*ctypes.ResultTx, len(results))
totalCount := len(results)
page = validatePage(page)
perPage = validatePerPage(perPage)
skipCount := (page - 1) * perPage
apiResults := make([]*ctypes.ResultTx, cmn.MinInt(perPage, totalCount-skipCount))
var proof types.TxProof var proof types.TxProof
for i, r := range results {
for i := 0; i < len(apiResults); i++ {
r := results[skipCount+i]
height := r.Height height := r.Height
index := r.Index index := r.Index
@ -205,5 +214,5 @@ func TxSearch(query string, prove bool) ([]*ctypes.ResultTx, error) {
} }
} }
return apiResults, nil
return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil
} }

+ 6
- 0
rpc/core/types/responses.go View File

@ -172,6 +172,12 @@ type ResultTx struct {
Proof types.TxProof `json:"proof,omitempty"` Proof types.TxProof `json:"proof,omitempty"`
} }
// Result of searching for txs
type ResultTxSearch struct {
Txs []*ResultTx `json:"txs"`
TotalCount int `json:"total_count"`
}
// List of mempool txs // List of mempool txs
type ResultUnconfirmedTxs struct { type ResultUnconfirmedTxs struct {
N int `json:"n_txs"` N int `json:"n_txs"`


Loading…
Cancel
Save