Skip to content

Commit

Permalink
Merge PR cosmos#4101: Return per-validator rewards when querying dele…
Browse files Browse the repository at this point in the history
…gator rewards
  • Loading branch information
alessio authored and alexanderbez committed Apr 16, 2019
1 parent 9cdd1d3 commit 7693708
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 16 deletions.
2 changes: 2 additions & 0 deletions .pending/breaking/gaiacli/3715-query-distr-rew
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#3715 query distr rewards returns per-validator
rewards along with rewards total amount.
3 changes: 3 additions & 0 deletions .pending/breaking/gaiarest/3715-Update-distribu
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#3715 Update /distribution/delegators/{delegatorAddr}/rewards GET endpoint
as per new specs. For a given delegation, the endpoint now returns the
comprehensive list of validator-reward tuples along with the grand total.
5 changes: 4 additions & 1 deletion client/lcd/lcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package lcd
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"os"
Expand All @@ -27,6 +28,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
dclcommon "github.com/cosmos/cosmos-sdk/x/distribution/client/common"
distrrest "github.com/cosmos/cosmos-sdk/x/distribution/client/rest"
disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/staking"
Expand Down Expand Up @@ -1003,9 +1005,10 @@ func TestDistributionFlow(t *testing.T) {
require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards))

// Query delegator's rewards total
var delRewards disttypes.QueryDelegatorTotalRewardsResponse
res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/delegators/%s/rewards", operAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards))
require.NoError(t, json.Unmarshal([]byte(body), &delRewards))

// Query delegator's withdrawal address
var withdrawAddr string
Expand Down
24 changes: 21 additions & 3 deletions client/lcd/swagger-ui/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1470,9 +1470,7 @@ paths:
200:
description: OK
schema:
type: array
items:
$ref: "#/definitions/Coin"
$ref: "#/definitions/DelegatorTotalRewards"
400:
description: Invalid delegator address
500:
Expand Down Expand Up @@ -2096,6 +2094,26 @@ definitions:
$ref: "#/definitions/BlockID"
block:
$ref: "#/definitions/Block"
DelegationDelegatorReward:
type: object
properties:
validator_address:
$ref: "#/definitions/ValidatorAddress"
reward:
type: array
items:
$ref: "#/definitions/Coin"
DelegatorTotalRewards:
type: object
properties:
rewards:
type: array
items:
$ref: "#/definitions/DelegationDelegatorReward"
total:
type: array
items:
$ref: "#/definitions/Coin"
BaseReq:
type: object
properties:
Expand Down
27 changes: 27 additions & 0 deletions cmd/gaia/cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,33 @@ func TestGaiaCLICreateValidator(t *testing.T) {
f.Cleanup()
}

func TestGaiaCLIQueryRewards(t *testing.T) {
t.Parallel()
f := InitFixtures(t)

genesisState := f.GenesisState()
inflationMin := sdk.MustNewDecFromStr("10000.0")
genesisState.MintData.Minter.Inflation = inflationMin
genesisState.MintData.Params.InflationMin = inflationMin
genesisState.MintData.Params.InflationMax = sdk.MustNewDecFromStr("15000.0")
genFile := filepath.Join(f.GaiadHome, "config", "genesis.json")
genDoc, err := tmtypes.GenesisDocFromFile(genFile)
require.NoError(t, err)
cdc := app.MakeCodec()
genDoc.AppState, err = cdc.MarshalJSON(genesisState)
require.NoError(t, genDoc.SaveAs(genFile))

// start gaiad server
proc := f.GDStart()
defer proc.Stop(false)

fooAddr := f.KeyAddress(keyFoo)
rewards := f.QueryRewards(fooAddr)
require.Equal(t, 1, len(rewards.Rewards))

f.Cleanup()
}

func TestGaiaCLISubmitProposal(t *testing.T) {
t.Parallel()
f := InitFixtures(t)
Expand Down
16 changes: 16 additions & 0 deletions cmd/gaia/cli_test/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/staking"
Expand Down Expand Up @@ -617,6 +618,21 @@ func (f *Fixtures) QuerySlashingParams() slashing.Params {
return params
}

//___________________________________________________________________________________
// query distribution

// QuerySigningInfo returns the signing info for a validator
func (f *Fixtures) QueryRewards(delAddr sdk.AccAddress, flags ...string) distribution.QueryDelegatorTotalRewardsResponse {
cmd := fmt.Sprintf("%s query distr rewards %s %s", f.GaiacliBinary, delAddr, f.Flags())
res, errStr := tests.ExecuteT(f.T, cmd, "")
require.Empty(f.T, errStr)
cdc := app.MakeCodec()
var rewards distribution.QueryDelegatorTotalRewardsResponse
err := cdc.UnmarshalJSON([]byte(res), &rewards)
require.NoError(f.T, err)
return rewards
}

//___________________________________________________________________________________
// executors

Expand Down
4 changes: 4 additions & 0 deletions x/distribution/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ type (
QueryValidatorSlashesParams = keeper.QueryValidatorSlashesParams
QueryDelegationRewardsParams = keeper.QueryDelegationRewardsParams
QueryDelegatorWithdrawAddrParams = keeper.QueryDelegatorWithdrawAddrParams

// querier response types
QueryDelegatorTotalRewardsResponse = types.QueryDelegatorTotalRewardsResponse
DelegationDelegatorReward = types.DelegationDelegatorReward
)

const (
Expand Down
19 changes: 12 additions & 7 deletions x/distribution/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,20 +144,25 @@ $ gaiacli query distr rewards cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p cosm
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)

var resp []byte
var err error
if len(args) == 2 {
// query for rewards from a particular delegation
resp, err = common.QueryDelegationRewards(cliCtx, cdc, queryRoute, args[0], args[1])
} else {
// query for delegator total rewards
resp, err = common.QueryDelegatorTotalRewards(cliCtx, cdc, queryRoute, args[0])
resp, err := common.QueryDelegationRewards(cliCtx, cdc, queryRoute, args[0], args[1])
if err != nil {
return err
}

var result sdk.DecCoins
cdc.MustUnmarshalJSON(resp, &result)
return cliCtx.PrintOutput(result)
}

// query for delegator total rewards
resp, err := common.QueryDelegatorTotalRewards(cliCtx, cdc, queryRoute, args[0])
if err != nil {
return err
}

var result sdk.DecCoins
var result distr.QueryDelegatorTotalRewardsResponse
cdc.MustUnmarshalJSON(resp, &result)
return cliCtx.PrintOutput(result)
},
Expand Down
15 changes: 10 additions & 5 deletions x/distribution/keeper/querier.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper

import (
"encoding/json"
"fmt"

abci "github.com/tendermint/tendermint/abci/types"
Expand Down Expand Up @@ -251,21 +252,25 @@ func queryDelegatorTotalRewards(ctx sdk.Context, _ []string, req abci.RequestQue
// cache-wrap context as to not persist state changes during querying
ctx, _ = ctx.CacheContext()

totalRewards := sdk.DecCoins{}
total := sdk.DecCoins{}
var delRewards []types.DelegationDelegatorReward

k.stakingKeeper.IterateDelegations(
ctx, params.DelegatorAddress,
func(_ int64, del sdk.Delegation) (stop bool) {
val := k.stakingKeeper.Validator(ctx, del.GetValidatorAddr())
valAddr := del.GetValidatorAddr()
val := k.stakingKeeper.Validator(ctx, valAddr)
endingPeriod := k.incrementValidatorPeriod(ctx, val)
rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod)
delReward := k.calculateDelegationRewards(ctx, val, del, endingPeriod)

totalRewards = totalRewards.Add(rewards)
delRewards = append(delRewards, types.NewDelegationDelegatorReward(valAddr, delReward))
total = total.Add(delReward)
return false
},
)

bz, err := codec.MarshalJSONIndent(k.cdc, totalRewards)
totalRewards := types.NewQueryDelegatorTotalRewardsResponse(delRewards, total)
bz, err := json.Marshal(totalRewards)
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error()))
}
Expand Down
26 changes: 26 additions & 0 deletions x/distribution/keeper/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ func getQueriedDelegationRewards(t *testing.T, ctx sdk.Context, cdc *codec.Codec
return
}

func getQueriedDelegatorTotalRewards(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, delegatorAddr sdk.AccAddress) (response types.QueryDelegatorTotalRewardsResponse) {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, QueryDelegatorTotalRewards}, "/"),
Data: cdc.MustMarshalJSON(NewQueryDelegatorParams(delegatorAddr)),
}

bz, err := querier(ctx, []string{QueryDelegatorTotalRewards}, query)
require.Nil(t, err)
require.Nil(t, cdc.UnmarshalJSON(bz, &response))

return
}

func getQueriedCommunityPool(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (ptr []byte) {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, QueryCommunityPool}, ""),
Expand All @@ -124,6 +137,7 @@ func getQueriedCommunityPool(t *testing.T, ctx sdk.Context, cdc *codec.Codec, qu

func TestQueries(t *testing.T) {
cdc := codec.New()
types.RegisterCodec(cdc)
ctx, _, keeper, sk, _ := CreateTestInputDefault(t, false, 100)
querier := NewQuerier(keeper)

Expand Down Expand Up @@ -154,6 +168,10 @@ func TestQueries(t *testing.T) {
retCommission := getQueriedValidatorCommission(t, ctx, cdc, querier, valOpAddr1)
require.Equal(t, commission, retCommission)

// test delegator's total rewards query
delRewards := getQueriedDelegatorTotalRewards(t, ctx, cdc, querier, sdk.AccAddress(valOpAddr1))
require.Equal(t, types.QueryDelegatorTotalRewardsResponse{}, delRewards)

// test validator slashes query with height range
slashOne := types.NewValidatorSlashEvent(3, sdk.NewDecWithPrec(5, 1))
slashTwo := types.NewValidatorSlashEvent(7, sdk.NewDecWithPrec(6, 1))
Expand Down Expand Up @@ -183,6 +201,14 @@ func TestQueries(t *testing.T) {
rewards = getQueriedDelegationRewards(t, ctx, cdc, querier, sdk.AccAddress(valOpAddr1), valOpAddr1)
require.Equal(t, sdk.DecCoins{{sdk.DefaultBondDenom, sdk.NewDec(initial / 2)}}, rewards)

// test delegator's total rewards query
delRewards = getQueriedDelegatorTotalRewards(t, ctx, cdc, querier, sdk.AccAddress(valOpAddr1))
expectedDelReward := types.NewDelegationDelegatorReward(valOpAddr1,
sdk.DecCoins{sdk.NewInt64DecCoin("stake", 5)})
wantDelRewards := types.NewQueryDelegatorTotalRewardsResponse(
[]types.DelegationDelegatorReward{expectedDelReward}, expectedDelReward.Reward)
require.Equal(t, wantDelRewards, delRewards)

// currently community pool hold nothing so we should return null
communityPool := getQueriedCommunityPool(t, ctx, cdc, querier)
require.Nil(t, communityPool)
Expand Down
46 changes: 46 additions & 0 deletions x/distribution/types/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package types

import (
"fmt"
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"
)

// QueryDelegatorTotalRewardsResponse defines the properties of
// QueryDelegatorTotalRewards query's response.
type QueryDelegatorTotalRewardsResponse struct {
Rewards []DelegationDelegatorReward `json:"rewards"`
Total sdk.DecCoins `json:"total"`
}

// NewQueryDelegatorTotalRewardsResponse constructs a QueryDelegatorTotalRewardsResponse
func NewQueryDelegatorTotalRewardsResponse(rewards []DelegationDelegatorReward,
total sdk.DecCoins) QueryDelegatorTotalRewardsResponse {
return QueryDelegatorTotalRewardsResponse{Rewards: rewards, Total: total}
}

func (res QueryDelegatorTotalRewardsResponse) String() string {
out := "Delegator Total Rewards:\n"
out += " Rewards:"
for _, reward := range res.Rewards {
out += fmt.Sprintf(`
ValidatorAddress: %s
Reward: %s`, reward.ValidatorAddress, reward.Reward)
}
out += fmt.Sprintf("\n Total: %s\n", res.Total)
return strings.TrimSpace(out)
}

// DelegationDelegatorReward defines the properties
// of a delegator's delegation reward.
type DelegationDelegatorReward struct {
ValidatorAddress sdk.ValAddress `json:"validator_address"`
Reward sdk.DecCoins `json:"reward"`
}

// NewDelegationDelegatorReward constructs a DelegationDelegatorReward.
func NewDelegationDelegatorReward(valAddr sdk.ValAddress,
reward sdk.DecCoins) DelegationDelegatorReward {
return DelegationDelegatorReward{ValidatorAddress: valAddr, Reward: reward}
}

0 comments on commit 7693708

Please sign in to comment.