diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 3ab1632e3..bbf4c35f6 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -21,6 +21,7 @@ program](https://hackerone.com/tendermint). - [rpc] \#2010 Add NewHTTPWithClient and NewJSONRPCClientWithHTTPClient (note these and NewHTTP, NewJSONRPCClient functions panic if remote is invalid) (@gracenoah) - [rpc] \#3984 Add `MempoolClient` interface to `Client` interface +- [rpc] \#3471 Paginate `/validator` response, default returns all validators with no limit ### BUG FIXES: diff --git a/docs/spec/rpc/swagger.yaml b/docs/spec/rpc/swagger.yaml index ef16bc72b..d498ec508 100644 --- a/docs/spec/rpc/swagger.yaml +++ b/docs/spec/rpc/swagger.yaml @@ -528,6 +528,20 @@ paths: description: height to return. If no height is provided, it will fetch validato set at the latest block. 0 means latest default: 0 x-example: 1 + - in: query + name: page + type: number + description: "Page number (1-based)" + required: false + x-example: 1 + default: 0 + - in: query + name: per_page + type: number + description: "Number of entries per page (max: 100)" + required: false + x-example: 30 + default: 0 tags: - Info description: | diff --git a/rpc/client/mock/client.go b/rpc/client/mock/client.go index b7fdd602d..ca3f2f013 100644 --- a/rpc/client/mock/client.go +++ b/rpc/client/mock/client.go @@ -146,8 +146,8 @@ func (c Client) Commit(height *int64) (*ctypes.ResultCommit, error) { return core.Commit(&rpctypes.Context{}, height) } -func (c Client) Validators(height *int64) (*ctypes.ResultValidators, error) { - return core.Validators(&rpctypes.Context{}, height) +func (c Client) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) { + return core.Validators(&rpctypes.Context{}, height, page, perPage) } func (c Client) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 8bcbd313d..71eaf6c5d 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -151,7 +151,7 @@ func TestGenesisAndValidators(t *testing.T) { gval := gen.Genesis.Validators[0] // get the current validators - vals, err := c.Validators(nil) + vals, err := c.Validators(nil, 0, 0) require.Nil(t, err, "%d: %+v", i, err) require.Equal(t, 1, len(vals.Validators)) val := vals.Validators[0] diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index 9bdfe89b8..6779d6d2b 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -51,6 +51,12 @@ import ( // "jsonrpc": "2.0" // } // ``` +// | Parameter | Type | Default | Required | Description | +// |-----------+--------+---------+----------+------------------------------------------------| +// | height | int64 | 0 | false | Height (0 means latest) | +// | page | int | 0 | false | Page number (1-based) | +// | per_page | int | 0 | false | Number of entries per page (max: 100) | + func Validators(ctx *rpctypes.Context, heightPtr *int64, page, perPage int) (*ctypes.ResultValidators, error) { // The latest validator that we know is the // NextValidator of the last block. @@ -64,6 +70,14 @@ func Validators(ctx *rpctypes.Context, heightPtr *int64, page, perPage int) (*ct if err != nil { return nil, err } + // page & perPage === 0 then all validators are returned, no pagination + if page == 0 && perPage == 0 { + return &ctypes.ResultValidators{ + BlockHeight: height, + Validators: validators.Validators, + }, nil + } + totalCount := len(validators.Validators) perPage = validatePerPage(perPage) page, err = validatePage(page, perPage, totalCount) @@ -74,7 +88,8 @@ func Validators(ctx *rpctypes.Context, heightPtr *int64, page, perPage int) (*ct apiResults := make([]*types.Validator, cmn.MinInt(perPage, totalCount-skipCount)) for i := 0; i < len(apiResults); i++ { - v := validators.Validators[skipCount+1] + v := validators.Validators[skipCount+i] + apiResults[i] = &types.Validator{ Address: v.Address, PubKey: v.PubKey, diff --git a/rpc/core/routes.go b/rpc/core/routes.go index dbd9c6059..8fb47e62a 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -23,7 +23,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"), - "validators": rpc.NewRPCFunc(Validators, "height"), + "validators": rpc.NewRPCFunc(Validators, "height,page,per_page"), "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""), "consensus_state": rpc.NewRPCFunc(ConsensusState, ""), "consensus_params": rpc.NewRPCFunc(ConsensusParams, "height"),