Merge PR #4177: Update Staking Validators Rest Query
* Support pagination and status query params for /staking/validators * Rename BondStatusToString to String
This commit is contained in:
parent
29755e66f6
commit
0e54369850
|
@ -0,0 +1,2 @@
|
||||||
|
#4099 Update the /staking/validators endpoint to support
|
||||||
|
status and pagination query flags.
|
|
@ -763,7 +763,23 @@ paths:
|
||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
/staking/validators:
|
/staking/validators:
|
||||||
get:
|
get:
|
||||||
summary: Get all validator candidates
|
summary: Get all validator candidates. By default it returns only the bonded validators.
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: status
|
||||||
|
type: string
|
||||||
|
description: The validator bond status. Must be either 'bonded', 'unbonded', or 'unbonding'.
|
||||||
|
x-example: bonded
|
||||||
|
- in: query
|
||||||
|
name: page
|
||||||
|
description: The gage number.
|
||||||
|
type: integer
|
||||||
|
x-example: 1
|
||||||
|
- in: query
|
||||||
|
name: limit
|
||||||
|
description: The maximum number of items per page.
|
||||||
|
type: integer
|
||||||
|
x-example: 1
|
||||||
tags:
|
tags:
|
||||||
- ICS21
|
- ICS21
|
||||||
produces:
|
produces:
|
||||||
|
|
|
@ -162,7 +162,6 @@ func QueryTxsByTagsRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec)
|
||||||
}
|
}
|
||||||
|
|
||||||
tags, page, limit, err = rest.ParseHTTPArgs(r)
|
tags, page, limit, err = rest.ParseHTTPArgs(r)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
|
|
|
@ -25,19 +25,23 @@ const (
|
||||||
// Constant as this should not change without a hard fork.
|
// Constant as this should not change without a hard fork.
|
||||||
// TODO: Link to some Tendermint docs, this is very unobvious.
|
// TODO: Link to some Tendermint docs, this is very unobvious.
|
||||||
ValidatorUpdateDelay int64 = 1
|
ValidatorUpdateDelay int64 = 1
|
||||||
|
|
||||||
|
BondStatusUnbonded = "Unbonded"
|
||||||
|
BondStatusUnbonding = "Unbonding"
|
||||||
|
BondStatusBonded = "Bonded"
|
||||||
)
|
)
|
||||||
|
|
||||||
//BondStatusToString for pretty prints of Bond Status
|
// String implements the Stringer interface for BondStatus.
|
||||||
func BondStatusToString(b BondStatus) string {
|
func (b BondStatus) String() string {
|
||||||
switch b {
|
switch b {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
return "Unbonded"
|
return BondStatusUnbonded
|
||||||
case 0x01:
|
case 0x01:
|
||||||
return "Unbonding"
|
return BondStatusUnbonding
|
||||||
case 0x02:
|
case 0x02:
|
||||||
return "Bonded"
|
return BondStatusBonded
|
||||||
default:
|
default:
|
||||||
panic("improper use of BondStatusToString")
|
panic("invalid bond status")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ type (
|
||||||
QueryValidatorParams = querier.QueryValidatorParams
|
QueryValidatorParams = querier.QueryValidatorParams
|
||||||
QueryBondsParams = querier.QueryBondsParams
|
QueryBondsParams = querier.QueryBondsParams
|
||||||
QueryRedelegationParams = querier.QueryRedelegationParams
|
QueryRedelegationParams = querier.QueryRedelegationParams
|
||||||
|
QueryValidatorsParams = querier.QueryValidatorsParams
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -96,10 +97,11 @@ var (
|
||||||
NewMsgUndelegate = types.NewMsgUndelegate
|
NewMsgUndelegate = types.NewMsgUndelegate
|
||||||
NewMsgBeginRedelegate = types.NewMsgBeginRedelegate
|
NewMsgBeginRedelegate = types.NewMsgBeginRedelegate
|
||||||
|
|
||||||
NewQuerier = querier.NewQuerier
|
NewQuerier = querier.NewQuerier
|
||||||
NewQueryDelegatorParams = querier.NewQueryDelegatorParams
|
NewQueryDelegatorParams = querier.NewQueryDelegatorParams
|
||||||
NewQueryValidatorParams = querier.NewQueryValidatorParams
|
NewQueryValidatorParams = querier.NewQueryValidatorParams
|
||||||
NewQueryBondsParams = querier.NewQueryBondsParams
|
NewQueryBondsParams = querier.NewQueryBondsParams
|
||||||
|
NewQueryValidatorsParams = querier.NewQueryValidatorsParams
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package rest
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -14,7 +15,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) {
|
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) {
|
||||||
|
|
||||||
// Get all delegations from a delegator
|
// Get all delegations from a delegator
|
||||||
r.HandleFunc(
|
r.HandleFunc(
|
||||||
"/staking/delegators/{delegatorAddr}/delegations",
|
"/staking/delegators/{delegatorAddr}/delegations",
|
||||||
|
@ -244,7 +244,31 @@ func delegatorValidatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) ht
|
||||||
// HTTP request handler to query list of validators
|
// HTTP request handler to query list of validators
|
||||||
func validatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
|
func validatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
res, err := cliCtx.QueryWithData("custom/staking/validators", nil)
|
_, page, limit, err := rest.ParseHTTPArgs(r)
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// override default limit if it wasn't provided
|
||||||
|
if l := r.FormValue("limit"); l == "" {
|
||||||
|
limit = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
status := r.FormValue("status")
|
||||||
|
if status == "" {
|
||||||
|
status = sdk.BondStatusBonded
|
||||||
|
}
|
||||||
|
|
||||||
|
params := staking.NewQueryValidatorsParams(page, limit, status)
|
||||||
|
bz, err := cdc.MarshalJSON(params)
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
route := fmt.Sprintf("custom/%s/%s", staking.QuerierRoute, staking.QueryValidators)
|
||||||
|
res, err := cliCtx.QueryWithData(route, bz)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
|
|
|
@ -162,9 +162,8 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
t, status, val.GetStatus(),
|
t, status, val.GetStatus(),
|
||||||
fmt.Sprintf("expected validator at index %v to have status: %s",
|
fmt.Sprintf("expected validator at index %v to have status: %s", valIdx, status),
|
||||||
valIdx,
|
)
|
||||||
sdk.BondStatusToString(status)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package querier
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ func NewQuerier(k keep.Keeper, cdc *codec.Codec) sdk.Querier {
|
||||||
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
|
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
|
||||||
switch path[0] {
|
switch path[0] {
|
||||||
case QueryValidators:
|
case QueryValidators:
|
||||||
return queryValidators(ctx, cdc, k)
|
return queryValidators(ctx, cdc, req, k)
|
||||||
case QueryValidator:
|
case QueryValidator:
|
||||||
return queryValidator(ctx, cdc, req, k)
|
return queryValidator(ctx, cdc, req, k)
|
||||||
case QueryValidatorDelegations:
|
case QueryValidatorDelegations:
|
||||||
|
@ -128,14 +129,47 @@ func NewQueryRedelegationParams(delegatorAddr sdk.AccAddress, srcValidatorAddr s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryValidators(ctx sdk.Context, cdc *codec.Codec, k keep.Keeper) (res []byte, err sdk.Error) {
|
func queryValidators(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k keep.Keeper) ([]byte, sdk.Error) {
|
||||||
stakingParams := k.GetParams(ctx)
|
var params QueryValidatorsParams
|
||||||
validators := k.GetValidators(ctx, stakingParams.MaxValidators)
|
|
||||||
|
|
||||||
res, errRes := codec.MarshalJSONIndent(cdc, validators)
|
err := cdc.UnmarshalJSON(req.Data, ¶ms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", errRes.Error()))
|
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stakingParams := k.GetParams(ctx)
|
||||||
|
if params.Limit == 0 {
|
||||||
|
params.Limit = int(stakingParams.MaxValidators)
|
||||||
|
}
|
||||||
|
|
||||||
|
validators := k.GetAllValidators(ctx)
|
||||||
|
filteredVals := make([]types.Validator, 0, len(validators))
|
||||||
|
|
||||||
|
for _, val := range validators {
|
||||||
|
if strings.ToLower(val.GetStatus().String()) == strings.ToLower(params.Status) {
|
||||||
|
filteredVals = append(filteredVals, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get pagination bounds
|
||||||
|
start := (params.Page - 1) * params.Limit
|
||||||
|
end := params.Limit + start
|
||||||
|
if end >= len(filteredVals) {
|
||||||
|
end = len(filteredVals)
|
||||||
|
}
|
||||||
|
|
||||||
|
if start >= len(filteredVals) {
|
||||||
|
// page is out of bounds
|
||||||
|
filteredVals = []types.Validator{}
|
||||||
|
} else {
|
||||||
|
filteredVals = filteredVals[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := codec.MarshalJSONIndent(cdc, filteredVals)
|
||||||
|
if err != nil {
|
||||||
|
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,3 +388,14 @@ func queryParameters(ctx sdk.Context, cdc *codec.Codec, k keep.Keeper) (res []by
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryValidatorsParams defines the params for the following queries:
|
||||||
|
// - 'custom/staking/validators'
|
||||||
|
type QueryValidatorsParams struct {
|
||||||
|
Page, Limit int
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQueryValidatorsParams(page, limit int, status string) QueryValidatorsParams {
|
||||||
|
return QueryValidatorsParams{page, limit, status}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package querier
|
package querier
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -44,9 +45,6 @@ func TestNewQuerier(t *testing.T) {
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
require.Nil(t, bz)
|
require.Nil(t, bz)
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"validators"}, query)
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"pool"}, query)
|
_, err = querier(ctx, []string{"pool"}, query)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
@ -121,28 +119,44 @@ func TestQueryValidators(t *testing.T) {
|
||||||
params := keeper.GetParams(ctx)
|
params := keeper.GetParams(ctx)
|
||||||
|
|
||||||
// Create Validators
|
// Create Validators
|
||||||
amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8)}
|
amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8), sdk.NewInt(7)}
|
||||||
var validators [2]types.Validator
|
status := []sdk.BondStatus{sdk.Bonded, sdk.Unbonded, sdk.Unbonding}
|
||||||
|
var validators [3]types.Validator
|
||||||
for i, amt := range amts {
|
for i, amt := range amts {
|
||||||
validators[i] = types.NewValidator(sdk.ValAddress(keep.Addrs[i]), keep.PKs[i], types.Description{})
|
validators[i] = types.NewValidator(sdk.ValAddress(keep.Addrs[i]), keep.PKs[i], types.Description{})
|
||||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||||
|
validators[i], pool = validators[i].UpdateStatus(pool, status[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
keeper.SetValidator(ctx, validators[0])
|
keeper.SetValidator(ctx, validators[0])
|
||||||
keeper.SetValidator(ctx, validators[1])
|
keeper.SetValidator(ctx, validators[1])
|
||||||
|
keeper.SetValidator(ctx, validators[2])
|
||||||
|
|
||||||
// Query Validators
|
// Query Validators
|
||||||
queriedValidators := keeper.GetValidators(ctx, params.MaxValidators)
|
queriedValidators := keeper.GetValidators(ctx, params.MaxValidators)
|
||||||
|
|
||||||
res, err := queryValidators(ctx, cdc, keeper)
|
for i, s := range status {
|
||||||
require.Nil(t, err)
|
queryValsParams := NewQueryValidatorsParams(1, int(params.MaxValidators), s.String())
|
||||||
|
bz, errRes := cdc.MarshalJSON(queryValsParams)
|
||||||
|
require.Nil(t, errRes)
|
||||||
|
|
||||||
var validatorsResp []types.Validator
|
req := abci.RequestQuery{
|
||||||
errRes := cdc.UnmarshalJSON(res, &validatorsResp)
|
Path: fmt.Sprintf("/custom/%s/%s", types.QuerierRoute, QueryValidators),
|
||||||
require.Nil(t, errRes)
|
Data: bz,
|
||||||
|
}
|
||||||
|
|
||||||
require.Equal(t, len(queriedValidators), len(validatorsResp))
|
res, err := queryValidators(ctx, cdc, req, keeper)
|
||||||
require.ElementsMatch(t, queriedValidators, validatorsResp)
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
var validatorsResp []types.Validator
|
||||||
|
errRes = cdc.UnmarshalJSON(res, &validatorsResp)
|
||||||
|
require.Nil(t, errRes)
|
||||||
|
|
||||||
|
require.Equal(t, 1, len(validatorsResp))
|
||||||
|
require.ElementsMatch(t, validators[i].OperatorAddress, validatorsResp[0].OperatorAddress)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Query each validator
|
// Query each validator
|
||||||
queryParams := NewQueryValidatorParams(addrVal1)
|
queryParams := NewQueryValidatorParams(addrVal1)
|
||||||
|
@ -153,7 +167,7 @@ func TestQueryValidators(t *testing.T) {
|
||||||
Path: "/custom/staking/validator",
|
Path: "/custom/staking/validator",
|
||||||
Data: bz,
|
Data: bz,
|
||||||
}
|
}
|
||||||
res, err = queryValidator(ctx, cdc, query, keeper)
|
res, err := queryValidator(ctx, cdc, query, keeper)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
var validator types.Validator
|
var validator types.Validator
|
||||||
|
|
|
@ -117,7 +117,7 @@ func (v Validator) String() string {
|
||||||
Unbonding Completion Time: %v
|
Unbonding Completion Time: %v
|
||||||
Minimum Self Delegation: %v
|
Minimum Self Delegation: %v
|
||||||
Commission: %s`, v.OperatorAddress, bechConsPubKey,
|
Commission: %s`, v.OperatorAddress, bechConsPubKey,
|
||||||
v.Jailed, sdk.BondStatusToString(v.Status), v.Tokens,
|
v.Jailed, v.Status, v.Tokens,
|
||||||
v.DelegatorShares, v.Description,
|
v.DelegatorShares, v.Description,
|
||||||
v.UnbondingHeight, v.UnbondingCompletionTime, v.MinSelfDelegation, v.Commission)
|
v.UnbondingHeight, v.UnbondingCompletionTime, v.MinSelfDelegation, v.Commission)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue