diff --git a/PENDING.md b/PENDING.md index 1fdbd0ec9b9d..975e0a33c29a 100644 --- a/PENDING.md +++ b/PENDING.md @@ -76,7 +76,7 @@ FEATURES * \#2996 Update the `AccountKeeper` to contain params used in the context of the ante handler. * [\#3179](https://github.com/cosmos/cosmos-sdk/pull/3179) New CodeNoSignatures error code. - + * \#3319 [x/distribution] Queriers for all distribution state worth querying; distribution query commands * Tendermint diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 7b235f4cd6e5..8b5ce4e73380 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -153,6 +153,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b AddRoute(gov.RouterKey, gov.NewHandler(app.govKeeper)) app.QueryRouter(). + AddRoute(distr.QuerierRoute, distr.NewQuerier(app.distrKeeper)). AddRoute(gov.QuerierRoute, gov.NewQuerier(app.govKeeper)). AddRoute(slashing.QuerierRoute, slashing.NewQuerier(app.slashingKeeper, app.cdc)). AddRoute(staking.QuerierRoute, staking.NewQuerier(app.stakingKeeper, app.cdc)) diff --git a/docs/gaia/gaiacli.md b/docs/gaia/gaiacli.md index 4484ca52ff99..cab61d10ff94 100644 --- a/docs/gaia/gaiacli.md +++ b/docs/gaia/gaiacli.md @@ -608,6 +608,48 @@ gaiacli query gov param tallying gaiacli query gov param deposit ``` +### Fee Distribution + +#### Query distribution parameters + +To check the current distribution parameters, run: + +```bash +gaiacli query distr params +``` + +#### Query outstanding rewards + +To check the current outstanding (un-withdrawn) rewards, run: + +```bash +gaiacli query distr outstanding-rewards +``` + +#### Query validator commission + +To check the current outstanding commission for a validator, run: + +```bash +gaiacli query distr commission +``` + +#### Query validator slashes + +To check historical slashes for a validator, run: + +```bash +gaiacli query distr slashes +``` + +#### Query delegator rewards + +To check current rewards for a delegation (were they to be withdrawn), run: + +```bash +gaiacli query distr rewards +``` + ### Multisig transactions Multisig transactions require signatures of multiple private keys. Thus, generating and signing diff --git a/x/distribution/alias.go b/x/distribution/alias.go index a38c2e431d76..d050eddad568 100644 --- a/x/distribution/alias.go +++ b/x/distribution/alias.go @@ -21,6 +21,11 @@ type ( StakingKeeper = types.StakingKeeper BankKeeper = types.BankKeeper FeeCollectionKeeper = types.FeeCollectionKeeper + + // querier param types + QueryValidatorCommissionParams = keeper.QueryValidatorCommissionParams + QueryValidatorSlashesParams = keeper.QueryValidatorSlashesParams + QueryDelegationRewardsParams = keeper.QueryDelegationRewardsParams ) const ( @@ -44,8 +49,12 @@ var ( NewMsgWithdrawDelegatorReward = types.NewMsgWithdrawDelegatorReward NewMsgWithdrawValidatorCommission = types.NewMsgWithdrawValidatorCommission - NewKeeper = keeper.NewKeeper - DefaultParamspace = keeper.DefaultParamspace + NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier + NewQueryValidatorCommissionParams = keeper.NewQueryValidatorCommissionParams + NewQueryValidatorSlashesParams = keeper.NewQueryValidatorSlashesParams + NewQueryDelegationRewardsParams = keeper.NewQueryDelegationRewardsParams + DefaultParamspace = keeper.DefaultParamspace RegisterCodec = types.RegisterCodec DefaultGenesisState = types.DefaultGenesisState diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go new file mode 100644 index 000000000000..3427a4509948 --- /dev/null +++ b/x/distribution/client/cli/query.go @@ -0,0 +1,201 @@ +package cli + +import ( + "fmt" + "strconv" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" +) + +// GetCmdQueryParams implements the query params command. +func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Args: cobra.ExactArgs(0), + Short: "Query distribution params", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, err := queryParams(cliCtx, cdc, queryRoute) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + + }, + } + return cmd +} + +func queryParams(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) ([]byte, error) { + retCommunityTax, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/community_tax", queryRoute), []byte{}) + if err != nil { + return nil, err + } + + retBaseProposerReward, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/base_proposer_reward", queryRoute), []byte{}) + if err != nil { + return nil, err + } + + retBonusProposerReward, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/bonus_proposer_reward", queryRoute), []byte{}) + if err != nil { + return nil, err + } + + retWithdrawAddrEnabled, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/withdraw_addr_enabled", queryRoute), []byte{}) + if err != nil { + return nil, err + } + + return codec.MarshalJSONIndent(cdc, NewPrettyParams(retCommunityTax, retBaseProposerReward, retBonusProposerReward, retWithdrawAddrEnabled)) +} + +// GetCmdQueryOutstandingRewards implements the query outstanding rewards command. +func GetCmdQueryOutstandingRewards(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "outstanding-rewards", + Args: cobra.ExactArgs(0), + Short: "Query distribution outstanding (un-withdrawn) rewards", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, err := queryOutstandingRewards(cliCtx, cdc, queryRoute) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + return cmd +} + +func queryOutstandingRewards(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) ([]byte, error) { + return cliCtx.QueryWithData(fmt.Sprintf("custom/%s/outstanding_rewards", queryRoute), []byte{}) +} + +// GetCmdQueryValidatorCommission implements the query validator commission command. +func GetCmdQueryValidatorCommission(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "commission [validator]", + Args: cobra.ExactArgs(1), + Short: "Query distribution validator commission", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + validatorAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + res, err := queryValidatorCommission(cliCtx, cdc, queryRoute, distr.NewQueryValidatorCommissionParams(validatorAddr)) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + return cmd +} + +func queryValidatorCommission(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string, params distr.QueryValidatorCommissionParams) ([]byte, error) { + bz, err := cdc.MarshalJSON(params) + if err != nil { + return nil, err + } + return cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_commission", queryRoute), bz) +} + +// GetCmdQueryValidatorSlashes implements the query validator slashes command. +func GetCmdQueryValidatorSlashes(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "slashes [validator] [start-height] [end-height]", + Args: cobra.ExactArgs(3), + Short: "Query distribution validator slashes", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + validatorAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + startHeight, err := strconv.ParseUint(args[1], 10, 64) + if err != nil { + return fmt.Errorf("start-height %s not a valid uint, please input a valid start-height", args[1]) + } + + endHeight, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return fmt.Errorf("end-height %s not a valid uint, please input a valid end-height", args[2]) + } + + res, err := queryValidatorSlashes(cliCtx, cdc, queryRoute, distr.NewQueryValidatorSlashesParams(validatorAddr, startHeight, endHeight)) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + return cmd +} + +func queryValidatorSlashes(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string, params distr.QueryValidatorSlashesParams) ([]byte, error) { + bz, err := cdc.MarshalJSON(params) + if err != nil { + return nil, err + } + return cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_slashes", queryRoute), bz) +} + +// GetCmdQueryDelegatorRewards implements the query delegator rewards command. +func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "rewards [delegator] [validator]", + Args: cobra.ExactArgs(2), + Short: "Query distribution delegator rewards", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + validatorAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + res, err := queryDelegationRewards(cliCtx, cdc, queryRoute, distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr)) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + return cmd +} + +func queryDelegationRewards(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string, params distr.QueryDelegationRewardsParams) ([]byte, error) { + bz, err := cdc.MarshalJSON(params) + if err != nil { + return nil, err + } + return cliCtx.QueryWithData(fmt.Sprintf("custom/%s/delegation_rewards", queryRoute), bz) +} diff --git a/x/distribution/client/cli/util.go b/x/distribution/client/cli/util.go new file mode 100644 index 000000000000..11eabd8f0475 --- /dev/null +++ b/x/distribution/client/cli/util.go @@ -0,0 +1,23 @@ +package cli + +import ( + "encoding/json" +) + +// Convenience struct for CLI output +type PrettyParams struct { + CommunityTax json.RawMessage `json:"community_tax"` + BaseProposerReward json.RawMessage `json:"base_proposer_reward"` + BonusProposerReward json.RawMessage `json:"bonus_proposer_reward"` + WithdrawAddrEnabled json.RawMessage `json:"withdraw_addr_enabled"` +} + +// Construct a new PrettyParams +func NewPrettyParams(communityTax json.RawMessage, baseProposerReward json.RawMessage, bonusProposerReward json.RawMessage, withdrawAddrEnabled json.RawMessage) PrettyParams { + return PrettyParams{ + CommunityTax: communityTax, + BaseProposerReward: baseProposerReward, + BonusProposerReward: bonusProposerReward, + WithdrawAddrEnabled: withdrawAddrEnabled, + } +} diff --git a/x/distribution/client/module_client.go b/x/distribution/client/module_client.go index 69734b08d0be..efe0084ff778 100644 --- a/x/distribution/client/module_client.go +++ b/x/distribution/client/module_client.go @@ -20,7 +20,20 @@ func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { // GetQueryCmd returns the cli query commands for this module func (mc ModuleClient) GetQueryCmd() *cobra.Command { - return &cobra.Command{Hidden: true} + distQueryCmd := &cobra.Command{ + Use: "distr", + Short: "Querying commands for the distribution module", + } + + distQueryCmd.AddCommand(client.GetCommands( + distCmds.GetCmdQueryParams(mc.storeKey, mc.cdc), + distCmds.GetCmdQueryOutstandingRewards(mc.storeKey, mc.cdc), + distCmds.GetCmdQueryValidatorCommission(mc.storeKey, mc.cdc), + distCmds.GetCmdQueryValidatorSlashes(mc.storeKey, mc.cdc), + distCmds.GetCmdQueryDelegatorRewards(mc.storeKey, mc.cdc), + )...) + + return distQueryCmd } // GetTxCmd returns the transaction commands for this module diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go new file mode 100644 index 000000000000..8aecb9780422 --- /dev/null +++ b/x/distribution/keeper/querier.go @@ -0,0 +1,176 @@ +package keeper + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// nolint +const ( + QueryParams = "params" + QueryOutstandingRewards = "outstanding_rewards" + QueryValidatorCommission = "validator_commission" + QueryValidatorSlashes = "validator_slashes" + QueryDelegationRewards = "delegation_rewards" + + ParamCommunityTax = "community_tax" + ParamBaseProposerReward = "base_proposer_reward" + ParamBonusProposerReward = "bonus_proposer_reward" + ParamWithdrawAddrEnabled = "withdraw_addr_enabled" +) + +func NewQuerier(k Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) { + switch path[0] { + case QueryParams: + return queryParams(ctx, path[1:], req, k) + case QueryOutstandingRewards: + return queryOutstandingRewards(ctx, path[1:], req, k) + case QueryValidatorCommission: + return queryValidatorCommission(ctx, path[1:], req, k) + case QueryValidatorSlashes: + return queryValidatorSlashes(ctx, path[1:], req, k) + case QueryDelegationRewards: + return queryDelegationRewards(ctx, path[1:], req, k) + default: + return nil, sdk.ErrUnknownRequest("unknown distr query endpoint") + } + } +} + +func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + switch path[0] { + case ParamCommunityTax: + bz, err := codec.MarshalJSONIndent(k.cdc, k.GetCommunityTax(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil + case ParamBaseProposerReward: + bz, err := codec.MarshalJSONIndent(k.cdc, k.GetBaseProposerReward(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil + case ParamBonusProposerReward: + bz, err := codec.MarshalJSONIndent(k.cdc, k.GetBonusProposerReward(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil + case ParamWithdrawAddrEnabled: + bz, err := codec.MarshalJSONIndent(k.cdc, k.GetWithdrawAddrEnabled(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil + default: + return nil, sdk.ErrUnknownRequest(fmt.Sprintf("%s is not a valid query request path", req.Path)) + } +} + +func queryOutstandingRewards(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + bz, err := codec.MarshalJSONIndent(k.cdc, k.GetOutstandingRewards(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +// params for query 'custom/distr/validator_commission' +type QueryValidatorCommissionParams struct { + ValidatorAddr sdk.ValAddress `json:"validator_addr"` +} + +// creates a new instance of QueryValidatorCommissionParams +func NewQueryValidatorCommissionParams(validatorAddr sdk.ValAddress) QueryValidatorCommissionParams { + return QueryValidatorCommissionParams{ + ValidatorAddr: validatorAddr, + } +} + +func queryValidatorCommission(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params QueryValidatorCommissionParams + err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + commission := k.GetValidatorAccumulatedCommission(ctx, params.ValidatorAddr) + bz, err := codec.MarshalJSONIndent(k.cdc, commission) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +// params for query 'custom/distr/validator_slashes' +type QueryValidatorSlashesParams struct { + ValidatorAddr sdk.ValAddress `json:"validator_addr"` + StartingHeight uint64 `json:"starting_height"` + EndingHeight uint64 `json:"ending_height"` +} + +// creates a new instance of QueryValidatorSlashesParams +func NewQueryValidatorSlashesParams(validatorAddr sdk.ValAddress, startingHeight uint64, endingHeight uint64) QueryValidatorSlashesParams { + return QueryValidatorSlashesParams{ + ValidatorAddr: validatorAddr, + StartingHeight: startingHeight, + EndingHeight: endingHeight, + } +} + +func queryValidatorSlashes(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params QueryValidatorSlashesParams + err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + events := make([]types.ValidatorSlashEvent, 0) + k.IterateValidatorSlashEventsBetween(ctx, params.ValidatorAddr, params.StartingHeight, params.EndingHeight, + func(height uint64, event types.ValidatorSlashEvent) (stop bool) { + events = append(events, event) + return false + }, + ) + bz, err := codec.MarshalJSONIndent(k.cdc, events) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +// params for query 'custom/distr/delegation_rewards' +type QueryDelegationRewardsParams struct { + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` + ValidatorAddr sdk.ValAddress `json:"validator_addr"` +} + +// creates a new instance of QueryDelegationRewardsParams +func NewQueryDelegationRewardsParams(delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) QueryDelegationRewardsParams { + return QueryDelegationRewardsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + } +} + +func queryDelegationRewards(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params QueryDelegationRewardsParams + err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + ctx, _ = ctx.CacheContext() + val := k.stakingKeeper.Validator(ctx, params.ValidatorAddr) + del := k.stakingKeeper.Delegation(ctx, params.DelegatorAddr, params.ValidatorAddr) + endingPeriod := k.incrementValidatorPeriod(ctx, val) + rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod) + bz, err := codec.MarshalJSONIndent(k.cdc, rewards) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} diff --git a/x/distribution/keeper/querier_test.go b/x/distribution/keeper/querier_test.go new file mode 100644 index 000000000000..f4f99973acf8 --- /dev/null +++ b/x/distribution/keeper/querier_test.go @@ -0,0 +1,171 @@ +package keeper + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/staking" + abci "github.com/tendermint/tendermint/abci/types" +) + +const custom = "custom" + +func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (communityTax sdk.Dec, baseProposerReward sdk.Dec, bonusProposerReward sdk.Dec, withdrawAddrEnabled bool) { + + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, QueryParams, ParamCommunityTax}, "/"), + Data: []byte{}, + } + + bz, err := querier(ctx, []string{QueryParams, ParamCommunityTax}, query) + require.Nil(t, err) + require.Nil(t, cdc.UnmarshalJSON(bz, &communityTax)) + + query = abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, QueryParams, ParamBaseProposerReward}, "/"), + Data: []byte{}, + } + + bz, err = querier(ctx, []string{QueryParams, ParamBaseProposerReward}, query) + require.Nil(t, err) + require.Nil(t, cdc.UnmarshalJSON(bz, &baseProposerReward)) + + query = abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, QueryParams, ParamBonusProposerReward}, "/"), + Data: []byte{}, + } + + bz, err = querier(ctx, []string{QueryParams, ParamBonusProposerReward}, query) + require.Nil(t, err) + require.Nil(t, cdc.UnmarshalJSON(bz, &bonusProposerReward)) + + query = abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, QueryParams, ParamWithdrawAddrEnabled}, "/"), + Data: []byte{}, + } + + bz, err = querier(ctx, []string{QueryParams, ParamWithdrawAddrEnabled}, query) + require.Nil(t, err) + require.Nil(t, cdc.UnmarshalJSON(bz, &withdrawAddrEnabled)) + + return +} + +func getQueriedOutstandingRewards(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (outstandingRewards sdk.DecCoins) { + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, QueryOutstandingRewards}, "/"), + Data: []byte{}, + } + + bz, err := querier(ctx, []string{QueryOutstandingRewards}, query) + require.Nil(t, err) + require.Nil(t, cdc.UnmarshalJSON(bz, &outstandingRewards)) + + return +} + +func getQueriedValidatorCommission(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, validatorAddr sdk.ValAddress) (validatorCommission sdk.DecCoins) { + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, QueryValidatorCommission}, "/"), + Data: cdc.MustMarshalJSON(NewQueryValidatorCommissionParams(validatorAddr)), + } + + bz, err := querier(ctx, []string{QueryValidatorCommission}, query) + require.Nil(t, err) + require.Nil(t, cdc.UnmarshalJSON(bz, &validatorCommission)) + + return +} + +func getQueriedValidatorSlashes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, validatorAddr sdk.ValAddress, startHeight uint64, endHeight uint64) (slashes []types.ValidatorSlashEvent) { + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, QueryValidatorSlashes}, "/"), + Data: cdc.MustMarshalJSON(NewQueryValidatorSlashesParams(validatorAddr, startHeight, endHeight)), + } + + bz, err := querier(ctx, []string{QueryValidatorSlashes}, query) + require.Nil(t, err) + require.Nil(t, cdc.UnmarshalJSON(bz, &slashes)) + + return +} + +func getQueriedDelegationRewards(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) (rewards sdk.DecCoins) { + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, QueryDelegationRewards}, "/"), + Data: cdc.MustMarshalJSON(NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr)), + } + + bz, err := querier(ctx, []string{QueryDelegationRewards}, query) + require.Nil(t, err) + require.Nil(t, cdc.UnmarshalJSON(bz, &rewards)) + + return +} + +func TestQueries(t *testing.T) { + cdc := codec.New() + ctx, _, keeper, sk, _ := CreateTestInputDefault(t, false, 100) + querier := NewQuerier(keeper) + + // test param queries + communityTax := sdk.NewDecWithPrec(3, 1) + baseProposerReward := sdk.NewDecWithPrec(2, 1) + bonusProposerReward := sdk.NewDecWithPrec(1, 1) + withdrawAddrEnabled := true + keeper.SetCommunityTax(ctx, communityTax) + keeper.SetBaseProposerReward(ctx, baseProposerReward) + keeper.SetBonusProposerReward(ctx, bonusProposerReward) + keeper.SetWithdrawAddrEnabled(ctx, withdrawAddrEnabled) + retCommunityTax, retBaseProposerReward, retBonusProposerReward, retWithdrawAddrEnabled := getQueriedParams(t, ctx, cdc, querier) + require.Equal(t, communityTax, retCommunityTax) + require.Equal(t, baseProposerReward, retBaseProposerReward) + require.Equal(t, bonusProposerReward, retBonusProposerReward) + require.Equal(t, withdrawAddrEnabled, retWithdrawAddrEnabled) + + // test outstanding rewards query + outstandingRewards := sdk.DecCoins{{"mytoken", sdk.NewDec(3)}, {"myothertoken", sdk.NewDecWithPrec(3, 7)}} + keeper.SetOutstandingRewards(ctx, outstandingRewards) + retOutstandingRewards := getQueriedOutstandingRewards(t, ctx, cdc, querier) + require.Equal(t, outstandingRewards, retOutstandingRewards) + + // test validator commission query + commission := sdk.DecCoins{{"token1", sdk.NewDec(4)}, {"token2", sdk.NewDec(2)}} + keeper.SetValidatorAccumulatedCommission(ctx, valOpAddr1, commission) + retCommission := getQueriedValidatorCommission(t, ctx, cdc, querier, valOpAddr1) + require.Equal(t, commission, retCommission) + + // test validator slashes query with height range + slashOne := types.NewValidatorSlashEvent(3, sdk.NewDecWithPrec(5, 1)) + slashTwo := types.NewValidatorSlashEvent(7, sdk.NewDecWithPrec(6, 1)) + keeper.SetValidatorSlashEvent(ctx, valOpAddr1, 3, slashOne) + keeper.SetValidatorSlashEvent(ctx, valOpAddr1, 7, slashTwo) + slashes := getQueriedValidatorSlashes(t, ctx, cdc, querier, valOpAddr1, 0, 2) + require.Equal(t, 0, len(slashes)) + slashes = getQueriedValidatorSlashes(t, ctx, cdc, querier, valOpAddr1, 0, 5) + require.Equal(t, []types.ValidatorSlashEvent{slashOne}, slashes) + slashes = getQueriedValidatorSlashes(t, ctx, cdc, querier, valOpAddr1, 0, 10) + require.Equal(t, []types.ValidatorSlashEvent{slashOne, slashTwo}, slashes) + + // test delegation rewards query + sh := staking.NewHandler(sk) + keeper.SetOutstandingRewards(ctx, sdk.DecCoins{}) + comm := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, + sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, comm) + require.True(t, sh(ctx, msg).IsOK()) + staking.EndBlocker(ctx, sk) + val := sk.Validator(ctx, valOpAddr1) + rewards := getQueriedDelegationRewards(t, ctx, cdc, querier, sdk.AccAddress(valOpAddr1), valOpAddr1) + require.True(t, rewards.IsZero()) + initial := int64(10) + tokens := sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}} + keeper.AllocateTokensToValidator(ctx, val, tokens) + rewards = getQueriedDelegationRewards(t, ctx, cdc, querier, sdk.AccAddress(valOpAddr1), valOpAddr1) + require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial / 2)}}, rewards) +}