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

R4R: Querier - Get all delegations to validator #2565

Merged
merged 19 commits into from
Nov 13, 2018
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 2 additions & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ FEATURES
* [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations
* [cli] [\#2524](https://github.com/cosmos/cosmos-sdk/issues/2524) Add support offline mode to `gaiacli tx sign`. Lookups are not performed if the flag `--offline` is on.
* [cli] [\#2558](https://github.com/cosmos/cosmos-sdk/issues/2558) Rename --print-sigs to --validate-signatures. It now performs a complete set of sanity checks and reports to the user. Also added --print-signature-only to print the signature only, not the whole transaction.
* [stake][cli] [\#2027] Add CLI query command for getting all delegations to a specific validator.

* Gaia

Expand All @@ -46,6 +47,7 @@ IMPROVEMENTS
- \#2660 [x/mock/simulation] Staking transactions get tested far more frequently
- #2610 [x/stake] Block redelegation to and from the same validator
- #2652 [x/auth] Add benchmark for get and set account
- \#2027 [x/stake] Add `Querier` for getting all delegations to a specific validator.

* Tendermint

Expand Down
17 changes: 16 additions & 1 deletion client/lcd/lcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,14 +538,18 @@ func TestBonding(t *testing.T) {

require.Equal(t, int64(40), coins.AmountOf(denom).Int64())

// query validator
// query delegation
bond := getDelegation(t, port, addr, operAddrs[0])
require.Equal(t, amt, bond.Shares)

delegatorDels := getDelegatorDelegations(t, port, addr)
require.Len(t, delegatorDels, 1)
require.Equal(t, amt, delegatorDels[0].Shares)

// query all delegations to validator
bonds := getValidatorDelegations(t, port, operAddrs[0])
require.Len(t, bonds, 2)

bondedValidators := getDelegatorValidators(t, port, addr)
require.Len(t, bondedValidators, 1)
require.Equal(t, operAddrs[0], bondedValidators[0].OperatorAddr)
Expand Down Expand Up @@ -1206,6 +1210,17 @@ func getValidator(t *testing.T, port string, validatorAddr sdk.ValAddress) stake
return validator
}

func getValidatorDelegations(t *testing.T, port string, validatorAddr sdk.ValAddress) []stake.Delegation {
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s/delegations", validatorAddr.String()), nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like there should be a helper function like StakeValidatorsDelegationsPath(validatorAddr.String())

https://guides.rubyonrails.org/routing.html#path-and-url-helpers

In Ruby, you can use magic to create such functions on the fly. In Go, we'd need to write them ourselves. Still, there may be a benefit in having them.

require.Equal(t, http.StatusOK, res.StatusCode, body)

var delegations []stake.Delegation
err := cdc.UnmarshalJSON([]byte(body), &delegations)
require.Nil(t, err)

return delegations
}

func getValidatorUnbondingDelegations(t *testing.T, port string, validatorAddr sdk.ValAddress) []stake.UnbondingDelegation {
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s/unbonding_delegations", validatorAddr.String()), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
Expand Down
13 changes: 13 additions & 0 deletions cmd/gaia/cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ func TestGaiaCLICreateValidator(t *testing.T) {
require.Equal(t, validator.OperatorAddr, sdk.ValAddress(barAddr))
require.True(sdk.DecEq(t, sdk.NewDec(2), validator.Tokens))

validatorDelegations := executeGetValidatorDelegations(t, fmt.Sprintf("gaiacli query delegations-to %s --output=json %v", sdk.ValAddress(barAddr), flags))
require.Len(t, validatorDelegations, 1)
require.NotZero(t, validatorDelegations[0].Shares)

// unbond a single share
unbondStr := fmt.Sprintf("gaiacli tx unbond begin %v", flags)
unbondStr += fmt.Sprintf(" --from=%s", "bar")
Expand Down Expand Up @@ -720,6 +724,15 @@ func executeGetValidatorRedelegations(t *testing.T, cmdStr string) []stake.Redel
return reds
}

func executeGetValidatorDelegations(t *testing.T, cmdStr string) []stake.Delegation {
out, _ := tests.ExecuteT(t, cmdStr, "")
var delegations []stake.Delegation
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &delegations)
require.NoError(t, err, "out %v\n, err %v", out, err)
return delegations
}

func executeGetPool(t *testing.T, cmdStr string) stake.Pool {
out, _ := tests.ExecuteT(t, cmdStr, "")
var pool stake.Pool
Expand Down
1 change: 1 addition & 0 deletions cmd/gaia/cmd/gaiacli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func main() {
stakecmd.GetCmdQueryRedelegations(storeStake, cdc),
stakecmd.GetCmdQueryValidator(storeStake, cdc),
stakecmd.GetCmdQueryValidators(storeStake, cdc),
stakecmd.GetCmdQueryValidatorDelegations(queryRouteStake, cdc),
stakecmd.GetCmdQueryValidatorUnbondingDelegations(queryRouteStake, cdc),
stakecmd.GetCmdQueryValidatorRedelegations(queryRouteStake, cdc),
stakecmd.GetCmdQueryParams(storeStake, cdc),
Expand Down
7 changes: 7 additions & 0 deletions docs/sdk/clients.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,13 @@ Additionally, as you can get all the outgoing redelegations from a particular va

To get previous redelegation(s) status on past blocks, try adding the `--height` flag.

##### Query Delegations To Validator

You can also query all of the delegations to a particular validator:
```bash
gaiacli query delegations-to <account_cosmosval>
```

### Governance

Governance is the process from which users in the Cosmos Hub can come to consensus on software upgrades, parameters of the mainnet or on custom text proposals. This is done through voting on proposals, which will be submitted by `Atom` holders on the mainnet.
Expand Down
37 changes: 37 additions & 0 deletions x/stake/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,43 @@ func GetCmdQueryDelegations(storeName string, cdc *codec.Codec) *cobra.Command {
return cmd
}

// GetCmdQueryValidatorDelegations implements the command to query all the
// delegations to a specific validator.
func GetCmdQueryValidatorDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "delegations-to [validator-addr]",
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
Short: "Query all delegations made to one validator",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
validatorAddr, err := sdk.ValAddressFromBech32(args[0])
if err != nil {
return err
}

params := stake.QueryValidatorParams{
ValidatorAddr: validatorAddr,
}
fedekunze marked this conversation as resolved.
Show resolved Hide resolved

bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}

cliCtx := context.NewCLIContext().WithCodec(cdc)

res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validatorDelegations", queryRoute), bz)
if err != nil {
return err
}

fmt.Println(string(res))
return nil
},
}

return cmd
}

// GetCmdQueryUnbondingDelegation implements the command to query a single
// unbonding-delegation record.
func GetCmdQueryUnbondingDelegation(storeName string, cdc *codec.Codec) *cobra.Command {
Expand Down
11 changes: 11 additions & 0 deletions x/stake/client/rest/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Co
validatorHandlerFn(cliCtx, cdc),
).Methods("GET")

// Get all delegations to a validator
sunnya97 marked this conversation as resolved.
Show resolved Hide resolved
r.HandleFunc(
"/stake/validators/{validatorAddr}/delegations",
validatorDelegationsHandlerFn(cliCtx, cdc),
).Methods("GET")

// Get all unbonding delegations from a validator
r.HandleFunc(
"/stake/validators/{validatorAddr}/unbonding_delegations",
Expand Down Expand Up @@ -227,6 +233,11 @@ func validatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Handle
return queryValidator(cliCtx, cdc, "custom/stake/validator")
}

// HTTP request handler to query all unbonding delegations from a validator
func validatorDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
return queryValidator(cliCtx, cdc, "custom/stake/validatorDelegations")
}

// HTTP request handler to query all unbonding delegations from a validator
func validatorUnbondingDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
return queryValidator(cliCtx, cdc, "custom/stake/validatorUnbondingDelegations")
Expand Down
15 changes: 15 additions & 0 deletions x/stake/keeper/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegati
return delegations
}

// return all delegations to a specific validator. Useful for querier.
func (k Keeper) GetValidatorDelegations(ctx sdk.Context, valAddr sdk.ValAddress) (delegations []types.Delegation) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, DelegationKey)
defer iterator.Close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because this is such a simple function, we should probably just close the iterator after the for loop and avoid using a defer call


for ; iterator.Valid(); iterator.Next() {
delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value())
if delegation.GetValidatorAddr().Equals(valAddr) {
delegations = append(delegations, delegation)
}
}
return delegations
}

// return a given amount of all the delegations from a delegator
func (k Keeper) GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress,
maxRetrieve uint16) (delegations []types.Delegation) {
Expand Down
7 changes: 7 additions & 0 deletions x/stake/keeper/delegation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ func TestDelegation(t *testing.T) {
resVals = keeper.GetDelegatorValidators(ctx, addrDels[1], 4)
require.Equal(t, 3, len(resVals))

resDels := keeper.GetValidatorDelegations(ctx, addrVals[0])
sunnya97 marked this conversation as resolved.
Show resolved Hide resolved
require.Len(t, resDels, 2)
resDels = keeper.GetValidatorDelegations(ctx, addrVals[1])
require.Len(t, resDels, 2)
resDels = keeper.GetValidatorDelegations(ctx, addrVals[2])
require.Len(t, resDels, 2)

for i := 0; i < 3; i++ {

resVal, err := keeper.GetDelegatorValidator(ctx, addrDels[0], addrVals[i])
Expand Down
21 changes: 21 additions & 0 deletions x/stake/querier/queryable.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const (
QueryDelegatorDelegations = "delegatorDelegations"
QueryDelegatorUnbondingDelegations = "delegatorUnbondingDelegations"
QueryDelegatorRedelegations = "delegatorRedelegations"
QueryValidatorDelegations = "validatorDelegations"
QueryValidatorUnbondingDelegations = "validatorUnbondingDelegations"
QueryValidatorRedelegations = "validatorRedelegations"
QueryDelegator = "delegator"
Expand All @@ -34,6 +35,8 @@ func NewQuerier(k keep.Keeper, cdc *codec.Codec) sdk.Querier {
return queryValidators(ctx, cdc, k)
case QueryValidator:
return queryValidator(ctx, cdc, req, k)
case QueryValidatorDelegations:
return queryValidatorDelegations(ctx, cdc, req, k)
case QueryValidatorUnbondingDelegations:
return queryValidatorUnbondingDelegations(ctx, cdc, req, k)
case QueryValidatorRedelegations:
Expand Down Expand Up @@ -73,6 +76,7 @@ type QueryDelegatorParams struct {

// defines the params for the following queries:
// - 'custom/stake/validator'
// - 'custom/stake/validatorDelegations'
// - 'custom/stake/validatorUnbondingDelegations'
// - 'custom/stake/validatorRedelegations'
type QueryValidatorParams struct {
Expand Down Expand Up @@ -119,6 +123,23 @@ func queryValidator(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k
return res, nil
}

func queryValidatorDelegations(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) {
var params QueryValidatorParams

errRes := cdc.UnmarshalJSON(req.Data, &params)
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
if errRes != nil {
return []byte{}, sdk.ErrUnknownAddress("")
}

delegations := k.GetValidatorDelegations(ctx, params.ValidatorAddr)

res, errRes = codec.MarshalJSONIndent(cdc, delegations)
if errRes != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", errRes.Error()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split this line up?

}
return res, nil
}

func queryValidatorUnbondingDelegations(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) {
var params QueryValidatorParams

Expand Down
31 changes: 31 additions & 0 deletions x/stake/querier/queryable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ func TestNewQuerier(t *testing.T) {
_, err = querier(ctx, []string{"validator"}, query)
require.Nil(t, err)

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

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

Expand Down Expand Up @@ -189,6 +192,11 @@ func TestQueryDelegation(t *testing.T) {
pool := keeper.GetPool(ctx)
keeper.SetValidatorByPowerIndex(ctx, val1, pool)

val2 := types.NewValidator(addrVal2, pk2, types.Description{})
keeper.SetValidator(ctx, val2)
pool = keeper.GetPool(ctx)
keeper.SetValidatorByPowerIndex(ctx, val2, pool)

keeper.Delegate(ctx, addrAcc2, sdk.NewCoin("steak", sdk.NewInt(20)), val1, true)

// apply TM updates
Expand Down Expand Up @@ -288,9 +296,32 @@ func TestQueryDelegation(t *testing.T) {
_, err = queryDelegation(ctx, cdc, query, keeper)
require.NotNil(t, err)

// Query validator delegations

bz, errRes = cdc.MarshalJSON(newTestValidatorQuery(addrVal1))
require.Nil(t, errRes)

query = abci.RequestQuery{
Path: "custom/stake/validatorDelegations",
Data: bz,
}

res, err = queryValidatorDelegations(ctx, cdc, query, keeper)
require.Nil(t, err)

var delegationsRes []types.Delegation
errRes = cdc.UnmarshalJSON(res, &delegationsRes)
require.Nil(t, errRes)

require.Equal(t, delegationsRes[0], delegation)

// Query unbonging delegation
keeper.BeginUnbonding(ctx, addrAcc2, val1.OperatorAddr, sdk.NewDec(10))

queryBondParams = newTestBondQuery(addrAcc2, addrVal1)
bz, errRes = cdc.MarshalJSON(queryBondParams)
require.Nil(t, errRes)

query = abci.RequestQuery{
Path: "/custom/stake/unbondingDelegation",
Data: bz,
Expand Down