From 626f9b62c5a64ced1ca3379dd04fbcddc407879b Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Sat, 6 Jun 2020 19:32:15 +0200 Subject: [PATCH] add pagination (#6163) * add pagination * make test pass with validators * add pagination option * unbonding delegations test added * test unbondedvalidators pagination * clean test * remove comment * remove comment * add page and limit to unbonding delegations * add flag limit and page for delegations-to command * refactor hardcoded string values * udpate page and limit for validator * update changelog * add pagination option * add defaults * Update CHANGELOG.md Co-authored-by: Alexander Bezobchuk Co-authored-by: Alexander Bezobchuk Co-authored-by: Alessio Treglia --- CHANGELOG.md | 3 + x/staking/client/cli/query.go | 19 ++++-- x/staking/client/rest/query.go | 30 ++++---- x/staking/client/rest/utils.go | 7 +- x/staking/keeper/querier.go | 14 ++++ x/staking/keeper/querier_test.go | 114 +++++++++++++++++++++++++++++-- x/staking/types/querier.go | 6 +- 7 files changed, 167 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e952b10b..88999132f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -271,6 +271,9 @@ functionality that requires an online connection. * (simulation) [\#6002](https://github.com/cosmos/cosmos-sdk/pull/6002) Add randomized consensus params into simulation. * (x/staking) [\#6059](https://github.com/cosmos/cosmos-sdk/pull/6059) Updated `HistoricalEntries` parameter default to 100. * (x/ibc) [\#5948](https://github.com/cosmos/cosmos-sdk/issues/5948) Add `InitGenesis` and `ExportGenesis` functions for `ibc` module. +* (types) [\#6128](https://github.com/cosmos/cosmos-sdk/pull/6137) Add String() method to GasMeter +* (x/staking) [\#6163](https://github.com/cosmos/cosmos-sdk/pull/6163) CLI and REST call to unbonding delegations and delegations now accept +pagination. * (types) [\#6128](https://github.com/cosmos/cosmos-sdk/pull/6137) Add `String()` method to `GasMeter`. * (types) [\#6195](https://github.com/cosmos/cosmos-sdk/pull/6195) Add codespace to broadcast(sync/async) response. * (baseapp) [\#6053](https://github.com/cosmos/cosmos-sdk/pull/6053) Customizable panic recovery handling added for `app.runTx()` method (as proposed in the [ADR 22](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-022-custom-panic-handling.md)). Adds ability for developers to register custom panic handlers extending standard ones. diff --git a/x/staking/client/cli/query.go b/x/staking/client/cli/query.go index dd1819e01..a93ee34ae 100644 --- a/x/staking/client/cli/query.go +++ b/x/staking/client/cli/query.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -125,7 +126,7 @@ $ %s query staking validators // GetCmdQueryValidatorUnbondingDelegations implements the query all unbonding delegatations from a validator command. func GetCmdQueryValidatorUnbondingDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "unbonding-delegations-from [validator-addr]", Short: "Query all unbonding delegatations from a validator", Long: strings.TrimSpace( @@ -146,7 +147,7 @@ $ %s query staking unbonding-delegations-from cosmosvaloper1gghjut3ccd8ay0zduzj6 return err } - bz, err := cdc.MarshalJSON(types.NewQueryValidatorParams(valAddr)) + bz, err := cdc.MarshalJSON(types.NewQueryValidatorParams(valAddr, viper.GetInt(flags.FlagPage), viper.GetInt(flags.FlagLimit))) if err != nil { return err } @@ -162,6 +163,11 @@ $ %s query staking unbonding-delegations-from cosmosvaloper1gghjut3ccd8ay0zduzj6 return clientCtx.PrintOutput(ubds) }, } + + cmd.Flags().Int(flags.FlagPage, 1, "pagination page of unbonding delegations to query for") + cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of unbonding delegations to query for") + + return cmd } // GetCmdQueryValidatorRedelegations implements the query all redelegatations @@ -306,7 +312,7 @@ $ %s query staking delegations cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p // GetCmdQueryValidatorDelegations implements the command to query all the // delegations to a specific validator. func GetCmdQueryValidatorDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "delegations-to [validator-addr]", Short: "Query all delegations made to one validator", Long: strings.TrimSpace( @@ -327,7 +333,7 @@ $ %s query staking delegations-to cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ld return err } - bz, err := cdc.MarshalJSON(types.NewQueryValidatorParams(valAddr)) + bz, err := cdc.MarshalJSON(types.NewQueryValidatorParams(valAddr, viper.GetInt(flags.FlagPage), viper.GetInt(flags.FlagLimit))) if err != nil { return err } @@ -346,6 +352,11 @@ $ %s query staking delegations-to cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ld return clientCtx.PrintOutput(resp) }, } + + cmd.Flags().Int(flags.FlagPage, 1, "pagination page of delegations to query for") + cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of delegations to query for") + + return cmd } // GetCmdQueryUnbondingDelegation implements the command to query a single diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go index 1453d70fc..0232bff53 100644 --- a/x/staking/client/rest/query.go +++ b/x/staking/client/rest/query.go @@ -112,8 +112,8 @@ func delegatorDelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { } // HTTP request handler to query a delegator unbonding delegations -func delegatorUnbondingDelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { - return queryDelegator(clientCtx, "custom/staking/delegatorUnbondingDelegations") +func delegatorUnbondingDelegationsHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryDelegator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorUnbondingDelegations)) } // HTTP request handler to query all staking txs (msgs) from a delegator @@ -189,8 +189,8 @@ func delegatorTxsHandlerFn(clientCtx client.Context) http.HandlerFunc { } // HTTP request handler to query an unbonding-delegation -func unbondingDelegationHandlerFn(clientCtx client.Context) http.HandlerFunc { - return queryBonds(clientCtx, "custom/staking/unbondingDelegation") +func unbondingDelegationHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryBonds(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryUnbondingDelegation)) } // HTTP request handler to query redelegations @@ -239,7 +239,7 @@ func redelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - res, height, err := clientCtx.QueryWithData("custom/staking/redelegations", bz) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryRedelegations), bz) if rest.CheckInternalServerError(w, err) { return } @@ -255,13 +255,13 @@ func delegationHandlerFn(clientCtx client.Context) http.HandlerFunc { } // HTTP request handler to query all delegator bonded validators -func delegatorValidatorsHandlerFn(clientCtx client.Context) http.HandlerFunc { - return queryDelegator(clientCtx, "custom/staking/delegatorValidators") +func delegatorValidatorsHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryDelegator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorValidators)) } // HTTP request handler to get information from a currently bonded validator -func delegatorValidatorHandlerFn(clientCtx client.Context) http.HandlerFunc { - return queryBonds(clientCtx, "custom/staking/delegatorValidator") +func delegatorValidatorHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryBonds(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorValidator)) } // HTTP request handler to query list of validators @@ -302,8 +302,8 @@ func validatorsHandlerFn(clientCtx client.Context) http.HandlerFunc { } // HTTP request handler to query the validator information from a given validator address -func validatorHandlerFn(clientCtx client.Context) http.HandlerFunc { - return queryValidator(clientCtx, "custom/staking/validator") +func validatorHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryValidator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidator)) } // HTTP request handler to query all unbonding delegations from a validator @@ -312,8 +312,8 @@ func validatorDelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { } // HTTP request handler to query all unbonding delegations from a validator -func validatorUnbondingDelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { - return queryValidator(clientCtx, "custom/staking/validatorUnbondingDelegations") +func validatorUnbondingDelegationsHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryValidator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidatorUnbondingDelegations)) } // HTTP request handler to query historical info at a given height @@ -353,7 +353,7 @@ func poolHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - res, height, err := clientCtx.QueryWithData("custom/staking/pool", nil) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryPool), nil) if rest.CheckInternalServerError(w, err) { return } @@ -371,7 +371,7 @@ func paramsHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - res, height, err := clientCtx.QueryWithData("custom/staking/parameters", nil) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters), nil) if rest.CheckInternalServerError(w, err) { return } diff --git a/x/staking/client/rest/utils.go b/x/staking/client/rest/utils.go index e4e3328ab..e08b5f0c9 100644 --- a/x/staking/client/rest/utils.go +++ b/x/staking/client/rest/utils.go @@ -111,6 +111,11 @@ func queryValidator(clientCtx client.Context, endpoint string) http.HandlerFunc vars := mux.Vars(r) bech32validatorAddr := vars["validatorAddr"] + _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) + if rest.CheckBadRequestError(w, err) { + return + } + validatorAddr, err := sdk.ValAddressFromBech32(bech32validatorAddr) if rest.CheckBadRequestError(w, err) { return @@ -121,7 +126,7 @@ func queryValidator(clientCtx client.Context, endpoint string) http.HandlerFunc return } - params := types.NewQueryValidatorParams(validatorAddr) + params := types.NewQueryValidatorParams(validatorAddr, page, limit) bz, err := clientCtx.Codec.MarshalJSON(params) if rest.CheckBadRequestError(w, err) { diff --git a/x/staking/keeper/querier.go b/x/staking/keeper/querier.go index 7eb052348..31c6b986b 100644 --- a/x/staking/keeper/querier.go +++ b/x/staking/keeper/querier.go @@ -128,6 +128,13 @@ func queryValidatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) delegations := k.GetValidatorDelegations(ctx, params.ValidatorAddr) + start, end := client.Paginate(len(delegations), params.Page, params.Limit, int(k.GetParams(ctx).MaxValidators)) + if start < 0 || end < 0 { + delegations = []types.Delegation{} + } else { + delegations = delegations[start:end] + } + delegationResps, err := delegationsToDelegationResponses(ctx, k, delegations) if err != nil { return nil, err @@ -158,6 +165,13 @@ func queryValidatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, unbonds = types.UnbondingDelegations{} } + start, end := client.Paginate(len(unbonds), params.Page, params.Limit, int(k.GetParams(ctx).MaxValidators)) + if start < 0 || end < 0 { + unbonds = types.UnbondingDelegations{} + } else { + unbonds = unbonds[start:end] + } + res, err := codec.MarshalJSONIndent(types.ModuleCdc, unbonds) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) diff --git a/x/staking/keeper/querier_test.go b/x/staking/keeper/querier_test.go index 5e395e3bd..3c3d820c1 100644 --- a/x/staking/keeper/querier_test.go +++ b/x/staking/keeper/querier_test.go @@ -55,7 +55,7 @@ func TestNewQuerier(t *testing.T) { _, err = querier(ctx, []string{"parameters"}, query) require.NoError(t, err) - queryValParams := types.NewQueryValidatorParams(addrVal1) + queryValParams := types.NewQueryValidatorParams(addrVal1, 0, 0) bz, errRes := cdc.MarshalJSON(queryValParams) require.NoError(t, errRes) @@ -178,7 +178,7 @@ func TestQueryValidators(t *testing.T) { // Query each validator for _, validator := range validators { - queryParams := types.NewQueryValidatorParams(validator.OperatorAddress) + queryParams := types.NewQueryValidatorParams(validator.OperatorAddress, 0, 0) bz, err := cdc.MarshalJSON(queryParams) require.NoError(t, err) @@ -323,7 +323,7 @@ func TestQueryDelegation(t *testing.T) { require.Error(t, err) // Query validator delegations - bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(addrVal1)) + bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(addrVal1, 1, 100)) require.NoError(t, errRes) query = abci.RequestQuery{ @@ -424,6 +424,112 @@ func TestQueryDelegation(t *testing.T) { require.Len(t, redel.Entries, len(redelRes[0].Entries)) } +func TestQueryValidatorDelegations_Pagination(t *testing.T) { + cases := []struct { + page int + limit int + expectedResults int + }{ + { + page: 1, + limit: 75, + expectedResults: 75, + }, + { + page: 2, + limit: 75, + expectedResults: 25, + }, + { + page: 1, + limit: 100, + expectedResults: 100, + }, + } + + cdc, app, ctx := createTestInput() + querier := staking.NewQuerier(app.StakingKeeper) + + addrs := simapp.AddTestAddrs(app, ctx, 100, sdk.TokensFromConsensusPower(10000)) + pubKeys := simapp.CreateTestPubKeys(1) + + valAddress := sdk.ValAddress(addrs[0]) + + val1 := types.NewValidator(valAddress, pubKeys[0], types.Description{}) + app.StakingKeeper.SetValidator(ctx, val1) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, val1) + + // Create Validators and Delegation + for _, addr := range addrs { + validator, found := app.StakingKeeper.GetValidator(ctx, valAddress) + if !found { + t.Error("expected validator not found") + } + + delTokens := sdk.TokensFromConsensusPower(20) + _, err := app.StakingKeeper.Delegate(ctx, addr, delTokens, sdk.Unbonded, validator, true) + require.NoError(t, err) + } + + // apply TM updates + app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + + for _, c := range cases { + // Query Delegator bonded validators + queryParams := types.NewQueryDelegatorParams(addrs[0]) + bz, errRes := cdc.MarshalJSON(queryParams) + require.NoError(t, errRes) + + // Query valAddress delegations + bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(valAddress, c.page, c.limit)) + require.NoError(t, errRes) + + query := abci.RequestQuery{ + Path: "custom/staking/validatorDelegations", + Data: bz, + } + + res, err := querier(ctx, []string{types.QueryValidatorDelegations}, query) + require.NoError(t, err) + + var delegationsRes types.DelegationResponses + errRes = cdc.UnmarshalJSON(res, &delegationsRes) + require.NoError(t, errRes) + require.Len(t, delegationsRes, c.expectedResults) + } + + // Undelegate + for _, addr := range addrs { + delTokens := sdk.TokensFromConsensusPower(20) + _, err := app.StakingKeeper.Undelegate(ctx, addr, val1.GetOperator(), delTokens.ToDec()) + require.NoError(t, err) + } + + // apply TM updates + app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + + for _, c := range cases { + // Query Unbonding delegations with pagination. + queryParams := types.NewQueryDelegatorParams(addrs[0]) + bz, errRes := cdc.MarshalJSON(queryParams) + require.NoError(t, errRes) + + bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(valAddress, c.page, c.limit)) + require.NoError(t, errRes) + query := abci.RequestQuery{ + Data: bz, + } + + unbondingDelegations := types.UnbondingDelegations{} + res, err := querier(ctx, []string{types.QueryValidatorUnbondingDelegations}, query) + require.NoError(t, err) + + errRes = cdc.UnmarshalJSON(res, &unbondingDelegations) + require.NoError(t, errRes) + require.Len(t, unbondingDelegations, c.expectedResults) + } +} + func TestQueryRedelegations(t *testing.T) { cdc, app, ctx := createTestInput() querier := staking.NewQuerier(app.StakingKeeper) @@ -474,7 +580,7 @@ func TestQueryRedelegations(t *testing.T) { require.Len(t, redel.Entries, len(redelRes[0].Entries)) // validator redelegations - queryValidatorParams := types.NewQueryValidatorParams(val1.GetOperator()) + queryValidatorParams := types.NewQueryValidatorParams(val1.GetOperator(), 0, 0) bz, errRes = cdc.MarshalJSON(queryValidatorParams) require.NoError(t, errRes) diff --git a/x/staking/types/querier.go b/x/staking/types/querier.go index 94dd59818..dfde91403 100644 --- a/x/staking/types/querier.go +++ b/x/staking/types/querier.go @@ -26,7 +26,6 @@ const ( // defines the params for the following queries: // - 'custom/staking/delegatorDelegations' // - 'custom/staking/delegatorUnbondingDelegations' -// - 'custom/staking/delegatorRedelegations' // - 'custom/staking/delegatorValidators' type QueryDelegatorParams struct { DelegatorAddr sdk.AccAddress @@ -45,11 +44,14 @@ func NewQueryDelegatorParams(delegatorAddr sdk.AccAddress) QueryDelegatorParams // - 'custom/staking/validatorRedelegations' type QueryValidatorParams struct { ValidatorAddr sdk.ValAddress + Page, Limit int } -func NewQueryValidatorParams(validatorAddr sdk.ValAddress) QueryValidatorParams { +func NewQueryValidatorParams(validatorAddr sdk.ValAddress, page, limit int) QueryValidatorParams { return QueryValidatorParams{ ValidatorAddr: validatorAddr, + Page: page, + Limit: limit, } }