Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Staking Validators Rest Query #4177

Merged
merged 2 commits into from
Apr 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#4099 Update the /staking/validators endpoint to support
status and pagination query flags.
18 changes: 17 additions & 1 deletion client/lcd/swagger-ui/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,23 @@ paths:
description: Internal Server Error
/staking/validators:
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:
- ICS21
produces:
Expand Down
1 change: 0 additions & 1 deletion client/tx/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ func QueryTxsByTagsRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec)
}

tags, page, limit, err = rest.ParseHTTPArgs(r)

if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
Expand Down
16 changes: 10 additions & 6 deletions types/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,23 @@ const (
// Constant as this should not change without a hard fork.
// TODO: Link to some Tendermint docs, this is very unobvious.
ValidatorUpdateDelay int64 = 1

BondStatusUnbonded = "Unbonded"
BondStatusUnbonding = "Unbonding"
BondStatusBonded = "Bonded"
)

//BondStatusToString for pretty prints of Bond Status
func BondStatusToString(b BondStatus) string {
// String implements the Stringer interface for BondStatus.
func (b BondStatus) String() string {
switch b {
case 0x00:
return "Unbonded"
return BondStatusUnbonded
case 0x01:
return "Unbonding"
return BondStatusUnbonding
case 0x02:
return "Bonded"
return BondStatusBonded
default:
panic("improper use of BondStatusToString")
panic("invalid bond status")
}
}

Expand Down
10 changes: 6 additions & 4 deletions x/staking/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type (
QueryValidatorParams = querier.QueryValidatorParams
QueryBondsParams = querier.QueryBondsParams
QueryRedelegationParams = querier.QueryRedelegationParams
QueryValidatorsParams = querier.QueryValidatorsParams
)

var (
Expand Down Expand Up @@ -96,10 +97,11 @@ var (
NewMsgUndelegate = types.NewMsgUndelegate
NewMsgBeginRedelegate = types.NewMsgBeginRedelegate

NewQuerier = querier.NewQuerier
NewQueryDelegatorParams = querier.NewQueryDelegatorParams
NewQueryValidatorParams = querier.NewQueryValidatorParams
NewQueryBondsParams = querier.NewQueryBondsParams
NewQuerier = querier.NewQuerier
NewQueryDelegatorParams = querier.NewQueryDelegatorParams
NewQueryValidatorParams = querier.NewQueryValidatorParams
NewQueryBondsParams = querier.NewQueryBondsParams
NewQueryValidatorsParams = querier.NewQueryValidatorsParams
)

const (
Expand Down
28 changes: 26 additions & 2 deletions x/staking/client/rest/query.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package rest

import (
"fmt"
"net/http"
"strings"

Expand All @@ -14,7 +15,6 @@ import (
)

func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) {

// Get all delegations from a delegator
r.HandleFunc(
"/staking/delegators/{delegatorAddr}/delegations",
Expand Down Expand Up @@ -244,7 +244,31 @@ func delegatorValidatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) ht
// HTTP request handler to query list of validators
func validatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
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 {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
Expand Down
5 changes: 2 additions & 3 deletions x/staking/keeper/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,8 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {

assert.Equal(
t, status, val.GetStatus(),
fmt.Sprintf("expected validator at index %v to have status: %s",
valIdx,
sdk.BondStatusToString(status)))
fmt.Sprintf("expected validator at index %v to have status: %s", valIdx, status),
)
}
}

Expand Down
55 changes: 50 additions & 5 deletions x/staking/querier/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package querier

import (
"fmt"
"strings"

abci "github.com/tendermint/tendermint/abci/types"

Expand Down Expand Up @@ -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) {
switch path[0] {
case QueryValidators:
return queryValidators(ctx, cdc, k)
return queryValidators(ctx, cdc, req, k)
case QueryValidator:
return queryValidator(ctx, cdc, req, k)
case QueryValidatorDelegations:
Expand Down Expand Up @@ -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) {
var params QueryValidatorsParams

err := cdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
}

stakingParams := k.GetParams(ctx)
validators := k.GetValidators(ctx, stakingParams.MaxValidators)
if params.Limit == 0 {
params.Limit = int(stakingParams.MaxValidators)
rigelrozanski marked this conversation as resolved.
Show resolved Hide resolved
}

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)
}

res, errRes := codec.MarshalJSONIndent(cdc, validators)
if start >= len(filteredVals) {
// page is out of bounds
filteredVals = []types.Validator{}
rigelrozanski marked this conversation as resolved.
Show resolved Hide resolved
} else {
filteredVals = filteredVals[start:end]
}

res, err := codec.MarshalJSONIndent(cdc, filteredVals)
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", errRes.Error()))
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error()))
}

return res, nil
}

Expand Down Expand Up @@ -354,3 +388,14 @@ func queryParameters(ctx sdk.Context, cdc *codec.Codec, k keep.Keeper) (res []by
}
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}
}
40 changes: 27 additions & 13 deletions x/staking/querier/querier_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package querier

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -44,9 +45,6 @@ func TestNewQuerier(t *testing.T) {
require.NotNil(t, err)
require.Nil(t, bz)

_, err = querier(ctx, []string{"validators"}, query)
require.Nil(t, err)

_, err = querier(ctx, []string{"pool"}, query)
require.Nil(t, err)

Expand Down Expand Up @@ -121,28 +119,44 @@ func TestQueryValidators(t *testing.T) {
params := keeper.GetParams(ctx)

// Create Validators
amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8)}
var validators [2]types.Validator
amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8), sdk.NewInt(7)}
status := []sdk.BondStatus{sdk.Bonded, sdk.Unbonded, sdk.Unbonding}
var validators [3]types.Validator
for i, amt := range amts {
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].UpdateStatus(pool, status[i])
}

keeper.SetPool(ctx, pool)
keeper.SetValidator(ctx, validators[0])
keeper.SetValidator(ctx, validators[1])
keeper.SetValidator(ctx, validators[2])

// Query Validators
queriedValidators := keeper.GetValidators(ctx, params.MaxValidators)

res, err := queryValidators(ctx, cdc, keeper)
require.Nil(t, err)
for i, s := range status {
queryValsParams := NewQueryValidatorsParams(1, int(params.MaxValidators), s.String())
bz, errRes := cdc.MarshalJSON(queryValsParams)
require.Nil(t, errRes)

var validatorsResp []types.Validator
errRes := cdc.UnmarshalJSON(res, &validatorsResp)
require.Nil(t, errRes)
req := abci.RequestQuery{
Path: fmt.Sprintf("/custom/%s/%s", types.QuerierRoute, QueryValidators),
Data: bz,
}

res, err := queryValidators(ctx, cdc, req, keeper)
require.Nil(t, err)

require.Equal(t, len(queriedValidators), len(validatorsResp))
require.ElementsMatch(t, queriedValidators, validatorsResp)
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
queryParams := NewQueryValidatorParams(addrVal1)
Expand All @@ -153,7 +167,7 @@ func TestQueryValidators(t *testing.T) {
Path: "/custom/staking/validator",
Data: bz,
}
res, err = queryValidator(ctx, cdc, query, keeper)
res, err := queryValidator(ctx, cdc, query, keeper)
require.Nil(t, err)

var validator types.Validator
Expand Down
2 changes: 1 addition & 1 deletion x/staking/types/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (v Validator) String() string {
Unbonding Completion Time: %v
Minimum Self Delegation: %v
Commission: %s`, v.OperatorAddress, bechConsPubKey,
v.Jailed, sdk.BondStatusToString(v.Status), v.Tokens,
v.Jailed, v.Status, v.Tokens,
v.DelegatorShares, v.Description,
v.UnbondingHeight, v.UnbondingCompletionTime, v.MinSelfDelegation, v.Commission)
}
Expand Down