From 7d39174fc76572c8bf3c2c9780042c3e9ffb158c Mon Sep 17 00:00:00 2001 From: "Kim, JinSan" Date: Tue, 15 Dec 2020 21:26:00 +0900 Subject: [PATCH 1/3] feat: `tx_by_height` rpc api --- lite2/rpc/client.go | 4 +++ rpc/client/http/http.go | 14 +++++++++ rpc/client/interface.go | 1 + rpc/client/local/local.go | 4 +++ rpc/core/routes.go | 1 + rpc/core/tx.go | 61 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 85 insertions(+) diff --git a/lite2/rpc/client.go b/lite2/rpc/client.go index a876dec28..1f52c1824 100644 --- a/lite2/rpc/client.go +++ b/lite2/rpc/client.go @@ -352,6 +352,10 @@ func (c *Client) TxSearch(query string, prove bool, page, perPage int, orderBy s return c.next.TxSearch(query, prove, page, perPage, orderBy) } +func (c *Client) TxByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { + return c.next.TxByHeight(height, prove, orderBy) +} + // Validators fetches and verifies validators. // // WARNING: only full validator sets are verified (when length of validators is diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index 1475f76e4..2704d7b04 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -408,6 +408,20 @@ func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int, or return result, nil } +func (c *baseRPCClient) TxByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { + result := new(ctypes.ResultTxSearch) + params := map[string]interface{}{ + "height": height, + "prove": prove, + "order_by": orderBy, + } + _, err := c.caller.Call("tx_by_height", params, result) + if err != nil { + return nil, errors.Wrap(err, "TxByHeight") + } + return result, nil +} + func (c *baseRPCClient) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) { result := new(ctypes.ResultValidators) _, err := c.caller.Call("validators", map[string]interface{}{ diff --git a/rpc/client/interface.go b/rpc/client/interface.go index 408d803c8..698257c2d 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -70,6 +70,7 @@ type SignClient interface { Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) TxSearch(query string, prove bool, page, perPage int, orderBy string) (*ctypes.ResultTxSearch, error) + TxByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) } // HistoryClient provides access to data from genesis to now in large chunks. diff --git a/rpc/client/local/local.go b/rpc/client/local/local.go index c7592bb83..9a18ce43d 100644 --- a/rpc/client/local/local.go +++ b/rpc/client/local/local.go @@ -166,6 +166,10 @@ func (c *Local) TxSearch(query string, prove bool, page, perPage int, orderBy st return core.TxSearch(c.ctx, query, prove, page, perPage, orderBy) } +func (c *Local) TxByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { + return core.TxByHeight(c.ctx, height, prove, orderBy) +} + func (c *Local) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { return core.BroadcastEvidence(c.ctx, ev) } diff --git a/rpc/core/routes.go b/rpc/core/routes.go index ea4a6e4d2..a49c9438e 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -25,6 +25,7 @@ var Routes = map[string]*rpc.RPCFunc{ "commit": rpc.NewRPCFunc(Commit, "height"), "tx": rpc.NewRPCFunc(Tx, "hash,prove"), "tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"), + "tx_by_height": rpc.NewRPCFunc(TxByHeight, "height,prove,order_by"), "validators": rpc.NewRPCFunc(Validators, "height,page,per_page"), "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""), "consensus_state": rpc.NewRPCFunc(ConsensusState, ""), diff --git a/rpc/core/tx.go b/rpc/core/tx.go index 4e20f0921..4a99a71b7 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -124,3 +124,64 @@ func TxSearch(ctx *rpctypes.Context, query string, prove bool, page, perPage int return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil } + +func TxByHeight(ctx *rpctypes.Context, height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { + // if index is disabled, return error + if _, ok := env.TxIndexer.(*null.TxIndex); ok { + return nil, errors.New("transaction indexing is disabled") + } + + q, err := tmquery.New(fmt.Sprintf("tx.height=%d", height)) + if err != nil { + return nil, err + } + + results, err := env.TxIndexer.Search(ctx.Context(), q) + if err != nil { + return nil, err + } + + // sort results + switch orderBy { + case "desc": + sort.Slice(results, func(i, j int) bool { + return results[i].Index > results[j].Index + }) + case "asc", "": + sort.Slice(results, func(i, j int) bool { + return results[i].Index < results[j].Index + }) + default: + return nil, errors.New("expected order_by to be either `asc` or `desc` or empty") + } + + totalCount := len(results) + + var block *types.Block + if prove { + block = env.BlockStore.LoadBlock(height) + } + + apiResults := make([]*ctypes.ResultTx, 0, totalCount) + for _, r := range results { + + var proof types.TxProof + if prove { + if block == nil { + panic("block must not be nil") + } + proof = block.Data.Txs.Proof(int(r.Index)) // XXX: overflow on 32-bit machines + } + + apiResults = append(apiResults, &ctypes.ResultTx{ + Hash: r.Tx.Hash(), + Height: r.Height, + Index: r.Index, + TxResult: r.Result, + Tx: r.Tx, + Proof: proof, + }) + } + + return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil +} From 205117947e01041f139993d0a6658c4f37d37406 Mon Sep 17 00:00:00 2001 From: "Kim, JinSan" Date: Tue, 15 Dec 2020 22:16:02 +0900 Subject: [PATCH 2/3] test: TestTxByHeight() --- rpc/client/rpc_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 9d4d6c3c4..faf7661a8 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -554,6 +554,36 @@ func TestTxSearch(t *testing.T) { } } +func TestTxByHeight(t *testing.T) { + c := getHTTPClient() + + heights := make([]int64, 10) + + // first we broadcast a few txs + for i := 0; i < 10; i++ { + _, _, tx := MakeTxKV() + result, err := c.BroadcastTxCommit(tx) + require.NoError(t, err) + heights[i] = result.Height + } + + // not prove and orderBy asc + for _, height := range heights { + res, err := c.TxByHeight(height, false, "asc") + require.NoError(t, err) + require.Equal(t, 1, res.TotalCount) + require.Equal(t, 1, len(res.Txs)) + } + + // prove and orderBy desc + for _, height := range heights { + res, err := c.TxByHeight(height, true, "desc") + require.NoError(t, err) + require.Equal(t, 1, res.TotalCount) + require.Equal(t, 1, len(res.Txs)) + } +} + func deepcpVote(vote *types.Vote) (res *types.Vote) { res = &types.Vote{ ValidatorAddress: make([]byte, len(vote.ValidatorAddress)), From b33d0c6701f5d626ca1c31b3cd0ddf8102e10a3f Mon Sep 17 00:00:00 2001 From: "Kim, JinSan" Date: Wed, 16 Dec 2020 12:59:11 +0900 Subject: [PATCH 3/3] chore: rename as `txs_by_height` --- lite2/rpc/client.go | 4 ++-- rpc/client/http/http.go | 6 +++--- rpc/client/interface.go | 2 +- rpc/client/local/local.go | 4 ++-- rpc/client/rpc_test.go | 6 +++--- rpc/core/routes.go | 2 +- rpc/core/tx.go | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lite2/rpc/client.go b/lite2/rpc/client.go index 1f52c1824..6b64652ee 100644 --- a/lite2/rpc/client.go +++ b/lite2/rpc/client.go @@ -352,8 +352,8 @@ func (c *Client) TxSearch(query string, prove bool, page, perPage int, orderBy s return c.next.TxSearch(query, prove, page, perPage, orderBy) } -func (c *Client) TxByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { - return c.next.TxByHeight(height, prove, orderBy) +func (c *Client) TxsByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { + return c.next.TxsByHeight(height, prove, orderBy) } // Validators fetches and verifies validators. diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index 2704d7b04..d7045614e 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -408,16 +408,16 @@ func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int, or return result, nil } -func (c *baseRPCClient) TxByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { +func (c *baseRPCClient) TxsByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { result := new(ctypes.ResultTxSearch) params := map[string]interface{}{ "height": height, "prove": prove, "order_by": orderBy, } - _, err := c.caller.Call("tx_by_height", params, result) + _, err := c.caller.Call("txs_by_height", params, result) if err != nil { - return nil, errors.Wrap(err, "TxByHeight") + return nil, errors.Wrap(err, "TxsByHeight") } return result, nil } diff --git a/rpc/client/interface.go b/rpc/client/interface.go index 698257c2d..50b6978e2 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -70,7 +70,7 @@ type SignClient interface { Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) TxSearch(query string, prove bool, page, perPage int, orderBy string) (*ctypes.ResultTxSearch, error) - TxByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) + TxsByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) } // HistoryClient provides access to data from genesis to now in large chunks. diff --git a/rpc/client/local/local.go b/rpc/client/local/local.go index 9a18ce43d..e92304470 100644 --- a/rpc/client/local/local.go +++ b/rpc/client/local/local.go @@ -166,8 +166,8 @@ func (c *Local) TxSearch(query string, prove bool, page, perPage int, orderBy st return core.TxSearch(c.ctx, query, prove, page, perPage, orderBy) } -func (c *Local) TxByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { - return core.TxByHeight(c.ctx, height, prove, orderBy) +func (c *Local) TxsByHeight(height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { + return core.TxsByHeight(c.ctx, height, prove, orderBy) } func (c *Local) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index faf7661a8..f4936bd3b 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -554,7 +554,7 @@ func TestTxSearch(t *testing.T) { } } -func TestTxByHeight(t *testing.T) { +func TestTxsByHeight(t *testing.T) { c := getHTTPClient() heights := make([]int64, 10) @@ -569,7 +569,7 @@ func TestTxByHeight(t *testing.T) { // not prove and orderBy asc for _, height := range heights { - res, err := c.TxByHeight(height, false, "asc") + res, err := c.TxsByHeight(height, false, "asc") require.NoError(t, err) require.Equal(t, 1, res.TotalCount) require.Equal(t, 1, len(res.Txs)) @@ -577,7 +577,7 @@ func TestTxByHeight(t *testing.T) { // prove and orderBy desc for _, height := range heights { - res, err := c.TxByHeight(height, true, "desc") + res, err := c.TxsByHeight(height, true, "desc") require.NoError(t, err) require.Equal(t, 1, res.TotalCount) require.Equal(t, 1, len(res.Txs)) diff --git a/rpc/core/routes.go b/rpc/core/routes.go index a49c9438e..ba014a45d 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -25,7 +25,7 @@ var Routes = map[string]*rpc.RPCFunc{ "commit": rpc.NewRPCFunc(Commit, "height"), "tx": rpc.NewRPCFunc(Tx, "hash,prove"), "tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"), - "tx_by_height": rpc.NewRPCFunc(TxByHeight, "height,prove,order_by"), + "txs_by_height": rpc.NewRPCFunc(TxsByHeight, "height,prove,order_by"), "validators": rpc.NewRPCFunc(Validators, "height,page,per_page"), "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""), "consensus_state": rpc.NewRPCFunc(ConsensusState, ""), diff --git a/rpc/core/tx.go b/rpc/core/tx.go index 4a99a71b7..bef934f10 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -125,7 +125,7 @@ func TxSearch(ctx *rpctypes.Context, query string, prove bool, page, perPage int return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil } -func TxByHeight(ctx *rpctypes.Context, height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { +func TxsByHeight(ctx *rpctypes.Context, height int64, prove bool, orderBy string) (*ctypes.ResultTxSearch, error) { // if index is disabled, return error if _, ok := env.TxIndexer.(*null.TxIndex); ok { return nil, errors.New("transaction indexing is disabled")