From d14e9193a4d14075caedbaa395c4a48a1ca26a95 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 30 Jan 2019 11:31:52 -0800 Subject: [PATCH 01/29] Factor params query logic out --- x/distribution/client/cli/query.go | 57 +++++++++++++++++------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 4f4eb93908df..577ecfe5f68e 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -21,34 +21,10 @@ func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Query distribution params", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - - route := fmt.Sprintf("custom/%s/params/community_tax", queryRoute) - retCommunityTax, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return err - } - - route = fmt.Sprintf("custom/%s/params/base_proposer_reward", queryRoute) - retBaseProposerReward, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return err - } - - route = fmt.Sprintf("custom/%s/params/bonus_proposer_reward", queryRoute) - retBonusProposerReward, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return err - } - - route = fmt.Sprintf("custom/%s/params/withdraw_addr_enabled", queryRoute) - retWithdrawAddrEnabled, err := cliCtx.QueryWithData(route, []byte{}) + params, err := QueryParams(cliCtx, queryRoute) if err != nil { return err } - - params := NewPrettyParams(retCommunityTax, retBaseProposerReward, - retBonusProposerReward, retWithdrawAddrEnabled) - return cliCtx.PrintOutput(params) }, } @@ -200,3 +176,34 @@ func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Com }, } } + +// QueryParams actually queries distribution params. +func QueryParams(cliCtx context.CLIContext, queryRoute string) (PrettyParams, error) { + route := fmt.Sprintf("custom/%s/params/community_tax", queryRoute) + + retCommunityTax, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + route = fmt.Sprintf("custom/%s/params/base_proposer_reward", queryRoute) + retBaseProposerReward, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + route = fmt.Sprintf("custom/%s/params/bonus_proposer_reward", queryRoute) + retBonusProposerReward, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + route = fmt.Sprintf("custom/%s/params/withdraw_addr_enabled", queryRoute) + retWithdrawAddrEnabled, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + return NewPrettyParams(retCommunityTax, retBaseProposerReward, + retBonusProposerReward, retWithdrawAddrEnabled), nil +} From 9dc2199cddd4ea61b036069bfc8b54c69eb64f58 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 30 Jan 2019 11:32:26 -0800 Subject: [PATCH 02/29] REST skeleton --- x/distribution/client/rest/query.go | 36 +++++++++++++++++++++++++++++ x/distribution/client/rest/rest.go | 11 +++++++++ 2 files changed, 47 insertions(+) create mode 100644 x/distribution/client/rest/query.go create mode 100644 x/distribution/client/rest/rest.go diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go new file mode 100644 index 000000000000..c95cf0ad5871 --- /dev/null +++ b/x/distribution/client/rest/query.go @@ -0,0 +1,36 @@ +package rest + +import ( + "net/http" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" + "github.com/gorilla/mux" +) + +func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, + cdc *codec.Codec, queryRoute string) { + + // Get the current staking parameter values + r.HandleFunc( + "/distribution/parameters", + paramsHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") +} + +// HTTP request handler to query the staking params values +func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + cliCtx := context.NewCLIContext().WithCodec(cdc) + params, err := cli.QueryParams(cliCtx, queryRoute) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + utils.PostProcessResponse(w, cdc, params, cliCtx.Indent) + } +} diff --git a/x/distribution/client/rest/rest.go b/x/distribution/client/rest/rest.go new file mode 100644 index 000000000000..72ef843d41af --- /dev/null +++ b/x/distribution/client/rest/rest.go @@ -0,0 +1,11 @@ +package rest + +import ( + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/gorilla/mux" +) + +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { + +} From 14f1b07d5fc3bbe81753f76a47b4001c5da0225f Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 30 Jan 2019 11:47:21 -0800 Subject: [PATCH 03/29] Start working on params query --- cmd/gaia/cmd/gaiacli/main.go | 6 ++++-- x/distribution/client/rest/rest.go | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 3042c35d60eb..3065ee64c0c9 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -25,7 +25,7 @@ import ( at "github.com/cosmos/cosmos-sdk/x/auth" auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" - dist "github.com/cosmos/cosmos-sdk/x/distribution" + dist "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" gv "github.com/cosmos/cosmos-sdk/x/gov" gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" sl "github.com/cosmos/cosmos-sdk/x/slashing" @@ -35,6 +35,7 @@ import ( authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + distcmd "github.com/cosmos/cosmos-sdk/x/distribution" distClient "github.com/cosmos/cosmos-sdk/x/distribution/client" govClient "github.com/cosmos/cosmos-sdk/x/gov/client" slashingClient "github.com/cosmos/cosmos-sdk/x/slashing/client" @@ -65,7 +66,7 @@ func main() { // TODO: Make the lcd command take a list of ModuleClient mc := []sdk.ModuleClients{ govClient.NewModuleClient(gv.StoreKey, cdc), - distClient.NewModuleClient(dist.StoreKey, cdc), + distClient.NewModuleClient(distcmd.StoreKey, cdc), stakingClient.NewModuleClient(st.StoreKey, cdc), slashingClient.NewModuleClient(sl.StoreKey, cdc), } @@ -161,6 +162,7 @@ func registerRoutes(rs *lcd.RestServer) { tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, at.StoreKey) bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + dist.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, distcmd.StoreKey) staking.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) gov.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) diff --git a/x/distribution/client/rest/rest.go b/x/distribution/client/rest/rest.go index 72ef843d41af..ba72bfce62e7 100644 --- a/x/distribution/client/rest/rest.go +++ b/x/distribution/client/rest/rest.go @@ -6,6 +6,7 @@ import ( "github.com/gorilla/mux" ) -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - +// RegisterRoutes register distribution REST routes. +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, queryRoute string) { + registerQueryRoutes(cliCtx, r, cdc, queryRoute) } From 27a8f2cbbc10d2704a09ea7c6551f57739d671d9 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 30 Jan 2019 18:36:04 -0800 Subject: [PATCH 04/29] Implement pool endpoint and querier --- x/distribution/client/rest/query.go | 23 ++++++++++++++++++++++- x/distribution/keeper/querier.go | 7 +++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index c95cf0ad5871..0918eb2765c3 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -1,6 +1,7 @@ package rest import ( + "fmt" "net/http" "github.com/cosmos/cosmos-sdk/client/context" @@ -18,9 +19,15 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, "/distribution/parameters", paramsHandlerFn(cliCtx, cdc, queryRoute), ).Methods("GET") + + // Get the current staking parameter values + r.HandleFunc( + "/distribution/pool", + poolHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") } -// HTTP request handler to query the staking params values +// HTTP request handler to query the distribution params values func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) http.HandlerFunc { @@ -34,3 +41,17 @@ func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, utils.PostProcessResponse(w, cdc, params, cliCtx.Indent) } } + +// HTTP request handler to query the pool information +func poolHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/fee_pool", queryRoute), []byte{}) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go index 619f2c39eae0..78488dbc8ba6 100644 --- a/x/distribution/keeper/querier.go +++ b/x/distribution/keeper/querier.go @@ -22,6 +22,7 @@ const ( ParamCommunityTax = "community_tax" ParamBaseProposerReward = "base_proposer_reward" ParamBonusProposerReward = "bonus_proposer_reward" + ParamFeePool = "fee_pool" ParamWithdrawAddrEnabled = "withdraw_addr_enabled" ) @@ -78,6 +79,12 @@ func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil + case ParamFeePool: + bz, err := codec.MarshalJSONIndent(k.cdc, k.GetFeePool(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)) } From 4eac5e8cbfffd92804e7db02496e31a07a34bd42 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 30 Jan 2019 19:13:36 -0800 Subject: [PATCH 05/29] Implement GET /distribution/delegators/{delegatorAddr}/rewards --- x/distribution/client/rest/query.go | 42 +++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 0918eb2765c3..319805ceb8fb 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -7,6 +7,8 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" "github.com/gorilla/mux" ) @@ -14,19 +16,55 @@ import ( func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, queryRoute string) { - // Get the current staking parameter values + // Get delegator's rewards + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/rewards", + delegatorRewardsHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + + // Get the current distribution parameter values r.HandleFunc( "/distribution/parameters", paramsHandlerFn(cliCtx, cdc, queryRoute), ).Methods("GET") - // Get the current staking parameter values + // Get the current distribution pool r.HandleFunc( "/distribution/pool", poolHandlerFn(cliCtx, cdc, queryRoute), ).Methods("GET") } +// HTTP request handler to query delegators rewards +func delegatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + // query for rewards from a particular delegator + delegatorAddr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + params := distr.NewQueryDelegationRewardsParams(delegatorAddr, nil) + bz, err := cdc.MarshalJSON(params) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + route := fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) + res, err := cliCtx.QueryWithData(route, bz) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} + // HTTP request handler to query the distribution params values func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) http.HandlerFunc { From d5939a1fb251b2a6fceadfd2541a0d22dad22c2a Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 31 Jan 2019 06:49:56 -0800 Subject: [PATCH 06/29] Add delegation query --- x/distribution/client/cli/query.go | 69 +++++++++++++++++------------ x/distribution/client/rest/query.go | 33 ++++++++------ 2 files changed, 60 insertions(+), 42 deletions(-) diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 577ecfe5f68e..a5e784b8069b 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -135,42 +135,18 @@ func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Com RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) - if err != nil { - return err - } - - var ( - route string - params distr.QueryDelegationRewardsParams - result sdk.DecCoins - ) - - if len(args) == 1 { - // query for all rewards - params = distr.NewQueryDelegationRewardsParams(delegatorAddr, nil) - route = fmt.Sprintf("custom/%s/all_delegation_rewards", queryRoute) - } else { + valAddr := "" + if len(args) == 2 { // query for rewards from a particular validator - validatorAddr, err := sdk.ValAddressFromBech32(args[1]) - if err != nil { - return err - } - - params = distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) - route = fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) - } - - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err + valAddr = args[1] } - resp, err := cliCtx.QueryWithData(route, bz) + resp, err := QueryRewards(cliCtx, cdc, queryRoute, args[0], valAddr) if err != nil { return err } + var result sdk.DecCoins cdc.MustUnmarshalJSON(resp, &result) return cliCtx.PrintOutput(result) }, @@ -207,3 +183,38 @@ func QueryParams(cliCtx context.CLIContext, queryRoute string) (PrettyParams, er return NewPrettyParams(retCommunityTax, retBaseProposerReward, retBonusProposerReward, retWithdrawAddrEnabled), nil } + +// QueryParams queries delegator rewards. If valAddr is empty string, +// it returns all delegations rewards for the given delegator; else +// it returns the rewards for the specific delegation. +func QueryRewards(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute, delAddr, valAddr string) ([]byte, error) { + + delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) + if err != nil { + return nil, err + } + + var params distr.QueryDelegationRewardsParams + var route string + + if valAddr == "" { + params = distr.NewQueryDelegationRewardsParams(delegatorAddr, nil) + route = fmt.Sprintf("custom/%s/all_delegation_rewards", queryRoute) + } else { + validatorAddr, err := sdk.ValAddressFromBech32(valAddr) + if err != nil { + return nil, err + } + + params = distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) + route = fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return nil, err + } + + return cliCtx.QueryWithData(route, bz) +} diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 319805ceb8fb..1ade4262a258 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -7,8 +7,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" "github.com/gorilla/mux" ) @@ -16,12 +14,18 @@ import ( func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, queryRoute string) { - // Get delegator's rewards + // Get the total rewards balance from all delegations r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards", delegatorRewardsHandlerFn(cliCtx, cdc, queryRoute), ).Methods("GET") + // Query a delegation reward + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", + delegatorRewardsHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + // Get the current distribution parameter values r.HandleFunc( "/distribution/parameters", @@ -41,21 +45,24 @@ func delegatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { // query for rewards from a particular delegator - delegatorAddr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - params := distr.NewQueryDelegationRewardsParams(delegatorAddr, nil) - bz, err := cdc.MarshalJSON(params) + res, err := cli.QueryRewards(cliCtx, cdc, queryRoute, mux.Vars(r)["delegatorAddr"], "") if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - route := fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) - res, err := cliCtx.QueryWithData(route, bz) + utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} + +// HTTP request handler to query a delegation rewards +func delegationRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + // query for rewards from a particular delegation + res, err := cli.QueryRewards(cliCtx, cdc, queryRoute, + mux.Vars(r)["delegatorAddr"], mux.Vars(r)["validatorAddr"]) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return From 50bf781baac075213fccda0ac10014efac792704 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 31 Jan 2019 08:56:15 -0800 Subject: [PATCH 07/29] Refactor, make new client common sub-package --- x/distribution/client/cli/query.go | 71 +---------------- x/distribution/client/common/common.go | 76 +++++++++++++++++++ .../{cli/util.go => common/pretty_params.go} | 2 +- x/distribution/client/rest/query.go | 8 +- 4 files changed, 84 insertions(+), 73 deletions(-) create mode 100644 x/distribution/client/common/common.go rename x/distribution/client/{cli/util.go => common/pretty_params.go} (98%) diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index a5e784b8069b..6020dbffb7e9 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) @@ -21,7 +22,7 @@ func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Query distribution params", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - params, err := QueryParams(cliCtx, queryRoute) + params, err := common.QueryParams(cliCtx, queryRoute) if err != nil { return err } @@ -141,7 +142,7 @@ func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Com valAddr = args[1] } - resp, err := QueryRewards(cliCtx, cdc, queryRoute, args[0], valAddr) + resp, err := common.QueryRewards(cliCtx, cdc, queryRoute, args[0], valAddr) if err != nil { return err } @@ -152,69 +153,3 @@ func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Com }, } } - -// QueryParams actually queries distribution params. -func QueryParams(cliCtx context.CLIContext, queryRoute string) (PrettyParams, error) { - route := fmt.Sprintf("custom/%s/params/community_tax", queryRoute) - - retCommunityTax, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return PrettyParams{}, err - } - - route = fmt.Sprintf("custom/%s/params/base_proposer_reward", queryRoute) - retBaseProposerReward, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return PrettyParams{}, err - } - - route = fmt.Sprintf("custom/%s/params/bonus_proposer_reward", queryRoute) - retBonusProposerReward, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return PrettyParams{}, err - } - - route = fmt.Sprintf("custom/%s/params/withdraw_addr_enabled", queryRoute) - retWithdrawAddrEnabled, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return PrettyParams{}, err - } - - return NewPrettyParams(retCommunityTax, retBaseProposerReward, - retBonusProposerReward, retWithdrawAddrEnabled), nil -} - -// QueryParams queries delegator rewards. If valAddr is empty string, -// it returns all delegations rewards for the given delegator; else -// it returns the rewards for the specific delegation. -func QueryRewards(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute, delAddr, valAddr string) ([]byte, error) { - - delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) - if err != nil { - return nil, err - } - - var params distr.QueryDelegationRewardsParams - var route string - - if valAddr == "" { - params = distr.NewQueryDelegationRewardsParams(delegatorAddr, nil) - route = fmt.Sprintf("custom/%s/all_delegation_rewards", queryRoute) - } else { - validatorAddr, err := sdk.ValAddressFromBech32(valAddr) - if err != nil { - return nil, err - } - - params = distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) - route = fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) - } - - bz, err := cdc.MarshalJSON(params) - if err != nil { - return nil, err - } - - return cliCtx.QueryWithData(route, bz) -} diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go new file mode 100644 index 000000000000..333026c446d7 --- /dev/null +++ b/x/distribution/client/common/common.go @@ -0,0 +1,76 @@ +package common + +import ( + "fmt" + + "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" +) + +// QueryParams actually queries distribution params. +func QueryParams(cliCtx context.CLIContext, queryRoute string) (PrettyParams, error) { + route := fmt.Sprintf("custom/%s/params/community_tax", queryRoute) + + retCommunityTax, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + route = fmt.Sprintf("custom/%s/params/base_proposer_reward", queryRoute) + retBaseProposerReward, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + route = fmt.Sprintf("custom/%s/params/bonus_proposer_reward", queryRoute) + retBonusProposerReward, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + route = fmt.Sprintf("custom/%s/params/withdraw_addr_enabled", queryRoute) + retWithdrawAddrEnabled, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + return NewPrettyParams(retCommunityTax, retBaseProposerReward, + retBonusProposerReward, retWithdrawAddrEnabled), nil +} + +// QueryParams queries delegator rewards. If valAddr is empty string, +// it returns all delegations rewards for the given delegator; else +// it returns the rewards for the specific delegation. +func QueryRewards(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute, delAddr, valAddr string) ([]byte, error) { + + delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) + if err != nil { + return nil, err + } + + var params distr.QueryDelegationRewardsParams + var route string + + if valAddr == "" { + params = distr.NewQueryDelegationRewardsParams(delegatorAddr, nil) + route = fmt.Sprintf("custom/%s/all_delegation_rewards", queryRoute) + } else { + validatorAddr, err := sdk.ValAddressFromBech32(valAddr) + if err != nil { + return nil, err + } + + params = distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) + route = fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return nil, err + } + + return cliCtx.QueryWithData(route, bz) +} diff --git a/x/distribution/client/cli/util.go b/x/distribution/client/common/pretty_params.go similarity index 98% rename from x/distribution/client/cli/util.go rename to x/distribution/client/common/pretty_params.go index 06e24a6b7e60..cbe22869b7d8 100644 --- a/x/distribution/client/cli/util.go +++ b/x/distribution/client/common/pretty_params.go @@ -1,4 +1,4 @@ -package cli +package common import ( "encoding/json" diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 1ade4262a258..eebca99cadb2 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -2,12 +2,12 @@ package rest import ( "fmt" + "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "net/http" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" "github.com/gorilla/mux" ) @@ -45,7 +45,7 @@ func delegatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { // query for rewards from a particular delegator - res, err := cli.QueryRewards(cliCtx, cdc, queryRoute, mux.Vars(r)["delegatorAddr"], "") + res, err := common.QueryRewards(cliCtx, cdc, queryRoute, mux.Vars(r)["delegatorAddr"], "") if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return @@ -61,7 +61,7 @@ func delegationRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { // query for rewards from a particular delegation - res, err := cli.QueryRewards(cliCtx, cdc, queryRoute, + res, err := common.QueryRewards(cliCtx, cdc, queryRoute, mux.Vars(r)["delegatorAddr"], mux.Vars(r)["validatorAddr"]) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) @@ -78,7 +78,7 @@ func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { cliCtx := context.NewCLIContext().WithCodec(cdc) - params, err := cli.QueryParams(cliCtx, queryRoute) + params, err := common.QueryParams(cliCtx, queryRoute) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return From 71d2898dde3dd261c4d340a57d606702a9251540 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 31 Jan 2019 14:07:09 -0800 Subject: [PATCH 08/29] Implement GET /distribution/delegators/{delegatorAddr}/withdraw_address --- x/distribution/alias.go | 20 ++++++++------- x/distribution/client/rest/query.go | 40 +++++++++++++++++++++++++++-- x/distribution/keeper/querier.go | 33 ++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 11 deletions(-) diff --git a/x/distribution/alias.go b/x/distribution/alias.go index d050eddad568..a7fa5812c10c 100644 --- a/x/distribution/alias.go +++ b/x/distribution/alias.go @@ -23,9 +23,10 @@ type ( FeeCollectionKeeper = types.FeeCollectionKeeper // querier param types - QueryValidatorCommissionParams = keeper.QueryValidatorCommissionParams - QueryValidatorSlashesParams = keeper.QueryValidatorSlashesParams - QueryDelegationRewardsParams = keeper.QueryDelegationRewardsParams + QueryValidatorCommissionParams = keeper.QueryValidatorCommissionParams + QueryValidatorSlashesParams = keeper.QueryValidatorSlashesParams + QueryDelegationRewardsParams = keeper.QueryDelegationRewardsParams + QueryDelegatorWithdrawAddrParams = keeper.QueryDelegatorWithdrawAddrParams ) const ( @@ -49,12 +50,13 @@ var ( NewMsgWithdrawDelegatorReward = types.NewMsgWithdrawDelegatorReward NewMsgWithdrawValidatorCommission = types.NewMsgWithdrawValidatorCommission - NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier - NewQueryValidatorCommissionParams = keeper.NewQueryValidatorCommissionParams - NewQueryValidatorSlashesParams = keeper.NewQueryValidatorSlashesParams - NewQueryDelegationRewardsParams = keeper.NewQueryDelegationRewardsParams - DefaultParamspace = keeper.DefaultParamspace + NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier + NewQueryValidatorCommissionParams = keeper.NewQueryValidatorCommissionParams + NewQueryValidatorSlashesParams = keeper.NewQueryValidatorSlashesParams + NewQueryDelegationRewardsParams = keeper.NewQueryDelegationRewardsParams + NewQueryDelegatorWithdrawAddrParams = keeper.NewQueryDelegatorWithdrawAddrParams + DefaultParamspace = keeper.DefaultParamspace RegisterCodec = types.RegisterCodec DefaultGenesisState = types.DefaultGenesisState diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index eebca99cadb2..ede808d21653 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -2,12 +2,15 @@ package rest import ( "fmt" - "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "net/http" + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/distribution/client/common" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/gorilla/mux" ) @@ -26,6 +29,12 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, delegatorRewardsHandlerFn(cliCtx, cdc, queryRoute), ).Methods("GET") + // Get the rewards withdrawal address + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/withdraw_address", + delegatorWithdrawalAddrHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + // Get the current distribution parameter values r.HandleFunc( "/distribution/parameters", @@ -72,12 +81,39 @@ func delegationRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, } } +// HTTP request handler to query a delegation rewards +func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + delegatorAddr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + params := distribution.NewQueryDelegatorWithdrawAddrParams(delegatorAddr) + bz, err := cdc.MarshalJSON(params) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/withdraw_addr", queryRoute), bz) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} + // HTTP request handler to query the distribution params values func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - cliCtx := context.NewCLIContext().WithCodec(cdc) params, err := common.QueryParams(cliCtx, queryRoute) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go index 78488dbc8ba6..be4163f8c0ff 100644 --- a/x/distribution/keeper/querier.go +++ b/x/distribution/keeper/querier.go @@ -18,6 +18,7 @@ const ( QueryValidatorSlashes = "validator_slashes" QueryDelegationRewards = "delegation_rewards" QueryAllDelegationRewards = "all_delegation_rewards" + QueryWithdrawAddr = "withdraw_addr" ParamCommunityTax = "community_tax" ParamBaseProposerReward = "base_proposer_reward" @@ -47,6 +48,9 @@ func NewQuerier(k Keeper) sdk.Querier { case QueryAllDelegationRewards: return queryAllDelegationRewards(ctx, path[1:], req, k) + case QueryWithdrawAddr: + return queryDelegatorWithdrawAddress(ctx, path[1:], req, k) + default: return nil, sdk.ErrUnknownRequest("unknown distr query endpoint") } @@ -228,3 +232,32 @@ func queryAllDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuer return bz, nil } + +// params for query 'custom/distr/withdraw_addr' +type QueryDelegatorWithdrawAddrParams struct { + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` +} + +// NewQueryDelegatorWithdrawAddrParams creates a new instance of QueryDelegatorWithdrawAddrParams. +func NewQueryDelegatorWithdrawAddrParams(delegatorAddr sdk.AccAddress) QueryDelegatorWithdrawAddrParams { + return QueryDelegatorWithdrawAddrParams{DelegatorAddr: delegatorAddr} +} + +func queryDelegatorWithdrawAddress(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params QueryDelegatorWithdrawAddrParams + err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + // cache-wrap context as to not persist state changes during querying + ctx, _ = ctx.CacheContext() + withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, params.DelegatorAddr) + + bz, err := codec.MarshalJSONIndent(k.cdc, withdrawAddr) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return bz, nil +} From effc2e9b09f5bb48a4ca15fef65fe769cbd97035 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 31 Jan 2019 16:16:21 -0800 Subject: [PATCH 09/29] WIP, issue is blocked --- x/distribution/client/rest/query.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index ede808d21653..98d81a722943 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -35,6 +35,18 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, delegatorWithdrawalAddrHandlerFn(cliCtx, cdc, queryRoute), ).Methods("GET") + // Validator distribution information + r.HandleFunc( + "/distribution/validator/{validatorAddr}", + validatorInfoHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + + // Commission and self-delegation rewards of a single a validator + r.HandleFunc( + "/distribution/validator/{validatorAddr}/rewards", + validatorRewardsHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + // Get the current distribution parameter values r.HandleFunc( "/distribution/parameters", @@ -109,6 +121,20 @@ func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, cdc *codec.Code } } +// HTTP request handler to query validator's distribution information +func validatorInfoHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) {} +} + +// HTTP request handler to query validator's commission and self-delegation rewards +func validatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) {} +} + // HTTP request handler to query the distribution params values func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) http.HandlerFunc { From 094aa52749ee01c426309a82fccfd5f0dfa5a9fc Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 1 Feb 2019 02:57:13 +0100 Subject: [PATCH 10/29] Update distribution swagger.yaml section for F1 --- client/lcd/swagger-ui/swagger.yaml | 38 +++++++----------------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 363345917aac..38e9c1864399 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -18,7 +18,7 @@ tags: - name: ICS23 description: Slashing module APIs - name: ICS24 - description: WIP - Fee distribution module APIs + description: Fee distribution module APIs - name: version description: Query app version schemes: @@ -1846,9 +1846,9 @@ paths: type: string 500: description: Internal Server Error - /distribution/pool: + /distribution/outstanding_rewards: get: - summary: Fee distribution pool + summary: Fee distribution outstanding rewards tags: - ICS24 produces: @@ -1857,7 +1857,9 @@ paths: 200: description: OK schema: - $ref: "#/definitions/FeePool" + type: array + items: + $ref: "#/definitions/Coin" 500: description: Internal Server Error definitions: @@ -2198,7 +2200,7 @@ definitions: power: type: string example: "1000" - accum: + proposer_priority: type: string example: "1000" TextProposal: @@ -2367,36 +2369,12 @@ definitions: type: string shares_dst: type: string - FeePool: - type: object - properties: - community_pool: - type: array - items: - $ref: "#/definitions/Coin" - val_accum: - $ref: "#/definitions/TotalAccum" - val_pool: - type: array - items: - $ref: "#/definitions/Coin" - TotalAccum: - type: object - properties: - update_height: - type: integer - accum: - type: string ValidatorDistInfo: type: object properties: operator_addr: $ref: "#/definitions/ValidatorAddress" - fee_pool_withdrawal_height: - type: integer - del_accum: - $ref: "#/definitions/TotalAccum" - del_pool: + self_bond_rewards: type: array items: $ref: "#/definitions/Coin" From 009e927c9934e9191e837eeaca74eb0eb2314ede Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 31 Jan 2019 20:16:36 -0800 Subject: [PATCH 11/29] Replace pool with outstanding_rewards --- x/distribution/client/rest/query.go | 10 +++++----- x/distribution/keeper/querier.go | 7 ------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 98d81a722943..e3a5815eae2f 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -55,8 +55,8 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, // Get the current distribution pool r.HandleFunc( - "/distribution/pool", - poolHandlerFn(cliCtx, cdc, queryRoute), + "/distribution/outstanding_rewards", + outstandingRewardsHandlerFn(cliCtx, cdc, queryRoute), ).Methods("GET") } @@ -149,12 +149,12 @@ func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, } } -// HTTP request handler to query the pool information -func poolHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, +// HTTP request handler to query the outstanding rewards +func outstandingRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/fee_pool", queryRoute), []byte{}) + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/outstanding_rewards", queryRoute), []byte{}) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go index be4163f8c0ff..abc190d8d907 100644 --- a/x/distribution/keeper/querier.go +++ b/x/distribution/keeper/querier.go @@ -23,7 +23,6 @@ const ( ParamCommunityTax = "community_tax" ParamBaseProposerReward = "base_proposer_reward" ParamBonusProposerReward = "bonus_proposer_reward" - ParamFeePool = "fee_pool" ParamWithdrawAddrEnabled = "withdraw_addr_enabled" ) @@ -83,12 +82,6 @@ func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil - case ParamFeePool: - bz, err := codec.MarshalJSONIndent(k.cdc, k.GetFeePool(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)) } From fd26e55b61eb1066f58db5a4e44661cf0c901d27 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 31 Jan 2019 22:21:52 -0800 Subject: [PATCH 12/29] Finalize query endpoints --- x/distribution/client/cli/query.go | 8 +- x/distribution/client/common/common.go | 10 +++ x/distribution/client/common/common_test.go | 35 ++++++++ x/distribution/client/rest/query.go | 99 ++++++++++++++++++--- 4 files changed, 134 insertions(+), 18 deletions(-) create mode 100644 x/distribution/client/common/common_test.go diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 6020dbffb7e9..4218cca51f0e 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -67,13 +67,7 @@ func GetCmdQueryValidatorCommission(queryRoute string, cdc *codec.Codec) *cobra. return err } - bz, err := cdc.MarshalJSON(distr.NewQueryValidatorCommissionParams(validatorAddr)) - if err != nil { - return err - } - - route := fmt.Sprintf("custom/%s/validator_commission", queryRoute) - res, err := cliCtx.QueryWithData(route, bz) + res, err := common.QueryValidatorCommission(cliCtx, cdc, queryRoute, validatorAddr) if err != nil { return err } diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go index 333026c446d7..161eec121a0d 100644 --- a/x/distribution/client/common/common.go +++ b/x/distribution/client/common/common.go @@ -74,3 +74,13 @@ func QueryRewards(cliCtx context.CLIContext, cdc *codec.Codec, return cliCtx.QueryWithData(route, bz) } + +// QueryValidatorCommission returns a validator's commission. +func QueryValidatorCommission(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string, validatorAddr sdk.ValAddress) ([]byte, error) { + + return cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/validator_commission", queryRoute), + cdc.MustMarshalJSON(distr.NewQueryValidatorCommissionParams(validatorAddr)), + ) +} diff --git a/x/distribution/client/common/common_test.go b/x/distribution/client/common/common_test.go new file mode 100644 index 000000000000..86a936443449 --- /dev/null +++ b/x/distribution/client/common/common_test.go @@ -0,0 +1,35 @@ +package common + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/stretchr/testify/require" +) + +func TestQueryRewardsAddrValidation(t *testing.T) { + cdc := codec.New() + ctx := context.NewCLIContext().WithCodec(cdc) + type args struct { + delAddr string + valAddr string + } + tests := []struct { + name string + args args + want []byte + wantErr bool + }{ + {"invalid delegator address", args{"invalid", ""}, nil, true}, + {"empty delegator address", args{"", ""}, nil, true}, + {"invalid validator address", args{"cosmos1zxcsu7l5qxs53lvp0fqgd09a9r2g6kqrk2cdpa", "invalid"}, nil, true}, + {"empty validator address", args{"cosmos1zxcsu7l5qxs53lvp0fqgd09a9r2g6kqrk2cdpa", ""}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := QueryRewards(ctx, cdc, "", tt.args.delAddr, tt.args.valAddr) + require.True(t, err != nil, tt.wantErr) + }) + } +} diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index e3a5815eae2f..1e522d61fe06 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/distribution/client/common" + "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" @@ -26,7 +27,7 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, // Query a delegation reward r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", - delegatorRewardsHandlerFn(cliCtx, cdc, queryRoute), + delegationRewardsHandlerFn(cliCtx, cdc, queryRoute), ).Methods("GET") // Get the rewards withdrawal address @@ -37,13 +38,13 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, // Validator distribution information r.HandleFunc( - "/distribution/validator/{validatorAddr}", + "/distribution/validators/{validatorAddr}", validatorInfoHandlerFn(cliCtx, cdc, queryRoute), ).Methods("GET") // Commission and self-delegation rewards of a single a validator r.HandleFunc( - "/distribution/validator/{validatorAddr}/rewards", + "/distribution/validators/{validatorAddr}/rewards", validatorRewardsHandlerFn(cliCtx, cdc, queryRoute), ).Methods("GET") @@ -66,9 +67,9 @@ func delegatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { // query for rewards from a particular delegator - res, err := common.QueryRewards(cliCtx, cdc, queryRoute, mux.Vars(r)["delegatorAddr"], "") - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, abort := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, + mux.Vars(r)["delegatorAddr"], "") + if abort { return } @@ -82,10 +83,9 @@ func delegationRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { // query for rewards from a particular delegation - res, err := common.QueryRewards(cliCtx, cdc, queryRoute, + res, abort := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, mux.Vars(r)["delegatorAddr"], mux.Vars(r)["validatorAddr"]) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if abort { return } @@ -121,18 +121,83 @@ func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, cdc *codec.Code } } +// ValidatorDistInfo defines the properties of +// validator distribution information response. +type ValidatorDistInfo struct { + OperatorAddress sdk.AccAddress `json:"operator_addr"` + SelfBondRewards sdk.DecCoins `json:"self_bond_rewards"` + ValidatorCommission types.ValidatorAccumulatedCommission `json:"val_commission"` +} + +// NewValidatorDistInfo creates a new instance of ValidatorDistInfo. +func NewValidatorDistInfo(operatorAddr sdk.AccAddress, rewards sdk.DecCoins, + commission types.ValidatorAccumulatedCommission) ValidatorDistInfo { + return ValidatorDistInfo{ + OperatorAddress: operatorAddr, + SelfBondRewards: rewards, + ValidatorCommission: commission, + } +} + // HTTP request handler to query validator's distribution information func validatorInfoHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) {} + return func(w http.ResponseWriter, r *http.Request) { + valAddr := mux.Vars(r)["validatorAddr"] + validatorAddr, err := sdk.ValAddressFromBech32(valAddr) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + // query commission + commissionRes, err := common.QueryValidatorCommission(cliCtx, cdc, queryRoute, validatorAddr) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + var valCom types.ValidatorAccumulatedCommission + cdc.MustUnmarshalJSON(commissionRes, &valCom) + + // self bond rewards + delAddr := sdk.AccAddress(validatorAddr) + rewardsRes, abort := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, + delAddr.String(), valAddr) + if abort { + return + } + + var rewards sdk.DecCoins + cdc.MustUnmarshalJSON(rewardsRes, &rewards) + + // Prepare response + res := cdc.MustMarshalJSON(NewValidatorDistInfo(delAddr, rewards, valCom)) + utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } } // HTTP request handler to query validator's commission and self-delegation rewards func validatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) {} + return func(w http.ResponseWriter, r *http.Request) { + valAddr := mux.Vars(r)["validatorAddr"] + validatorAddr, err := sdk.ValAddressFromBech32(valAddr) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + delAddr := sdk.AccAddress(validatorAddr).String() + res, abort := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, delAddr, valAddr) + if abort { + return + } + + utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } } // HTTP request handler to query the distribution params values @@ -162,3 +227,15 @@ func outstandingRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } + +func checkResponseQueryRewards(w http.ResponseWriter, cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute, delAddr, valAddr string) (res []byte, abort bool) { + + res, err := common.QueryRewards(cliCtx, cdc, queryRoute, delAddr, valAddr) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return nil, true + } + + return res, false +} From be16e5cdbeeff2bcad2319e59cba6863161ca788 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 09:02:16 -0800 Subject: [PATCH 13/29] Implement POST /delegators/{delegatorAddr}/rewards/{validatorAddr} --- client/rest/rest.go | 110 +++++++++++++++++++++++++++++ x/distribution/client/rest/rest.go | 1 + x/distribution/client/rest/tx.go | 94 ++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 x/distribution/client/rest/tx.go diff --git a/client/rest/rest.go b/client/rest/rest.go index 0b87e084df87..705e3fef3de6 100644 --- a/client/rest/rest.go +++ b/client/rest/rest.go @@ -99,6 +99,116 @@ func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEm //----------------------------------------------------------------------------- // Building / Sending utilities +// BaseReq defines a structure that can be embedded in other request structures +// that all share common "base" fields. +type BaseReq struct { + From string `json:"from"` + Password string `json:"password"` + Memo string `json:"memo"` + ChainID string `json:"chain_id"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` + Fees sdk.Coins `json:"fees"` + GasPrices sdk.DecCoins `json:"gas_prices"` + Gas string `json:"gas"` + GasAdjustment string `json:"gas_adjustment"` + GenerateOnly bool `json:"generate_only"` + Simulate bool `json:"simulate"` +} + +// NewBaseReq creates a new basic request instance and sanitizes its values +func NewBaseReq( + from, password, memo, chainID string, gas, gasAdjustment string, + accNumber, seq uint64, fees sdk.Coins, gasPrices sdk.DecCoins, genOnly, simulate bool, +) BaseReq { + + return BaseReq{ + From: strings.TrimSpace(from), + Password: password, + Memo: strings.TrimSpace(memo), + ChainID: strings.TrimSpace(chainID), + Fees: fees, + GasPrices: gasPrices, + Gas: strings.TrimSpace(gas), + GasAdjustment: strings.TrimSpace(gasAdjustment), + AccountNumber: accNumber, + Sequence: seq, + GenerateOnly: genOnly, + Simulate: simulate, + } +} + +// Sanitize performs basic sanitization on a BaseReq object. +func (br BaseReq) Sanitize() BaseReq { + return NewBaseReq( + br.From, br.Password, br.Memo, br.ChainID, br.Gas, br.GasAdjustment, + br.AccountNumber, br.Sequence, br.Fees, br.GasPrices, br.GenerateOnly, br.Simulate, + ) +} + +// ValidateBasic performs basic validation of a BaseReq. If custom validation +// logic is needed, the implementing request handler should perform those +// checks manually. Returns true if validation passes; false otherwise. +func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { + if !br.GenerateOnly && !br.Simulate { + switch { + case len(br.Password) == 0: + WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified") + return false + + case len(br.ChainID) == 0: + WriteErrorResponse(w, http.StatusUnauthorized, "chain-id required but not specified") + return false + + case !br.Fees.IsZero() && !br.GasPrices.IsZero(): + // both fees and gas prices were provided + WriteErrorResponse(w, http.StatusBadRequest, "cannot provide both fees and gas prices") + return false + + case !br.Fees.IsValid() && !br.GasPrices.IsValid(): + // neither fees or gas prices were provided + WriteErrorResponse(w, http.StatusPaymentRequired, "invalid fees or gas prices provided") + return false + } + } + + if len(br.From) == 0 { + WriteErrorResponse(w, http.StatusUnauthorized, "name or address required but not specified") + return false + } + + return true +} + +/* +ReadRESTReq is a simple convenience wrapper that reads the body and +unmarshals to the req interface. + + Usage: + type SomeReq struct { + BaseReq `json:"base_req"` + CustomField string `json:"custom_field"` + } + + req := new(SomeReq) + err := ReadRESTReq(w, r, cdc, req) +*/ +func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) error { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return err + } + + err = cdc.UnmarshalJSON(body, req) + if err != nil { + WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to decode JSON payload: %s", err)) + return err + } + + return nil +} + // CompleteAndBroadcastTxREST implements a utility function that facilitates // sending a series of messages in a signed tx. In addition, it will handle // tx gas simulation and estimation. diff --git a/x/distribution/client/rest/rest.go b/x/distribution/client/rest/rest.go index ba72bfce62e7..903db1a6aaa9 100644 --- a/x/distribution/client/rest/rest.go +++ b/x/distribution/client/rest/rest.go @@ -9,4 +9,5 @@ import ( // RegisterRoutes register distribution REST routes. func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, queryRoute string) { registerQueryRoutes(cliCtx, r, cdc, queryRoute) + registerTxRoutes(cliCtx, r, cdc) } diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go new file mode 100644 index 000000000000..2f9ca49e1e51 --- /dev/null +++ b/x/distribution/client/rest/tx.go @@ -0,0 +1,94 @@ +package rest + +import ( + "net/http" + + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, + cdc *codec.Codec) { + + // Withdraw delegation rewards + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", + withdrawDelegationRewardsHandlerFn(cdc, cliCtx), + ).Methods("POST") + +} + +type withdrawDelegationRewardsReq struct { + BaseReq utils.BaseReq `json:"base_req"` +} + +func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req withdrawDelegationRewardsReq + + if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + delAddr, abort := checkDelegatorAddressVar(w, r) + if abort { + return + } + + valAddr, abort := checkValidatorAddressVar(w, r) + if abort { + return + } + + // derive the from account address and name from the Keybase + fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) + + msg := types.NewMsgWithdrawDelegatorReward(delAddr, valAddr) + if err := msg.ValidateBasic(); err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + return + } + + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + } +} + +func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAddress, bool) { + addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return nil, true + } + return addr, false +} + +func checkValidatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.ValAddress, bool) { + addr, err := sdk.ValAddressFromBech32(mux.Vars(r)["validatorAddr"]) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return nil, true + } + return addr, false +} From 9e9e665c3fcec03daf140fef27f28f0e20a350f1 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 1 Feb 2019 15:06:55 -0800 Subject: [PATCH 14/29] Implement /validators/{validatorAddr}/rewards --- x/distribution/client/rest/tx.go | 67 +++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index 2f9ca49e1e51..31aada79f178 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -22,15 +22,21 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, withdrawDelegationRewardsHandlerFn(cdc, cliCtx), ).Methods("POST") + // Withdraw validator rewards and commission + r.HandleFunc( + "/distribution/validators/{validatorAddr}/rewards", + withdrawValidatorRewardsHandlerFn(cdc, cliCtx), + ).Methods("POST") + } -type withdrawDelegationRewardsReq struct { +type withdrawRewardsReq struct { BaseReq utils.BaseReq `json:"base_req"` } func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var req withdrawDelegationRewardsReq + var req withdrawRewardsReq if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) @@ -42,6 +48,7 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont return } + // read and validate URL's variables delAddr, abort := checkDelegatorAddressVar(w, r) if abort { return @@ -75,6 +82,62 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont } } +func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req withdrawRewardsReq + + if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + // read and validate URL's variable + valAddr, abort := checkValidatorAddressVar(w, r) + if abort { + return + } + + // derive the from account address and name from the Keybase + fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) + + // build and validate MsgWithdrawValidatorCommission + commissionMsg := types.NewMsgWithdrawValidatorCommission(valAddr) + if err := commissionMsg.ValidateBasic(); err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + // build and validate MsgWithdrawDelegatorReward + delAddr := sdk.AccAddress(valAddr.Bytes()) + rewardMsg := types.NewMsgWithdrawDelegatorReward(delAddr, valAddr) + if err := rewardMsg.ValidateBasic(); err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + // prepare multi-message transaction + msgs := []sdk.Msg{rewardMsg, commissionMsg} + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, msgs) + return + } + + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, msgs, cdc) + } +} + +// Auxiliary + func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAddress, bool) { addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) if err != nil { From 32811f95c4b77f7bfc960bbfd4c55cf97635f663 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 1 Feb 2019 15:34:43 -0800 Subject: [PATCH 15/29] Factour out logic to prepare multi-msg withdrawal --- x/distribution/client/common/common.go | 19 ++++++++++++ x/distribution/client/rest/tx.go | 43 +++++++++----------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go index 161eec121a0d..1258b049e990 100644 --- a/x/distribution/client/common/common.go +++ b/x/distribution/client/common/common.go @@ -84,3 +84,22 @@ func QueryValidatorCommission(cliCtx context.CLIContext, cdc *codec.Codec, cdc.MustMarshalJSON(distr.NewQueryValidatorCommissionParams(validatorAddr)), ) } + +// WithdrawValidatorRewardsAndCommission builds a two-message message slice to be +// used to withdraw both validation's commission and self-delegation reward. +func WithdrawValidatorRewardsAndCommission(validatorAddr sdk.ValAddress) ([]sdk.Msg, error) { + + commissionMsg := distr.NewMsgWithdrawValidatorCommission(validatorAddr) + if err := commissionMsg.ValidateBasic(); err != nil { + return nil, err + } + + // build and validate MsgWithdrawDelegatorReward + rewardMsg := distr.NewMsgWithdrawDelegatorReward( + sdk.AccAddress(validatorAddr.Bytes()), validatorAddr) + if err := rewardMsg.ValidateBasic(); err != nil { + return nil, err + } + + return []sdk.Msg{commissionMsg, rewardMsg}, nil +} diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index 31aada79f178..5a4cc336c352 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -16,6 +17,12 @@ import ( func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { + // Withdraw delegator rewards + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/rewards", + withdrawDelegatorRewardsHandlerFn(cdc, cliCtx), + ).Methods("POST") + // Withdraw delegation rewards r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", @@ -34,6 +41,12 @@ type withdrawRewardsReq struct { BaseReq utils.BaseReq `json:"base_req"` } +func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + } +} + func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq @@ -59,14 +72,6 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont return } - // derive the from account address and name from the Keybase - fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) - msg := types.NewMsgWithdrawDelegatorReward(delAddr, valAddr) if err := msg.ValidateBasic(); err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) @@ -102,31 +107,13 @@ func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte return } - // derive the from account address and name from the Keybase - fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) + // prepare multi-message transaction + msgs, err := common.WithdrawValidatorRewardsAndCommission(valAddr) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) - - // build and validate MsgWithdrawValidatorCommission - commissionMsg := types.NewMsgWithdrawValidatorCommission(valAddr) - if err := commissionMsg.ValidateBasic(); err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - // build and validate MsgWithdrawDelegatorReward - delAddr := sdk.AccAddress(valAddr.Bytes()) - rewardMsg := types.NewMsgWithdrawDelegatorReward(delAddr, valAddr) - if err := rewardMsg.ValidateBasic(); err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - // prepare multi-message transaction - msgs := []sdk.Msg{rewardMsg, commissionMsg} if req.BaseReq.GenerateOnly { utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, msgs) return From 1d5cf3fa11f32215becf447c97cbea1e08e97169 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 1 Feb 2019 16:13:42 -0800 Subject: [PATCH 16/29] WIP --- x/distribution/client/rest/rest.go | 2 +- x/distribution/client/rest/tx.go | 31 ++++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/x/distribution/client/rest/rest.go b/x/distribution/client/rest/rest.go index 903db1a6aaa9..769e7360e8da 100644 --- a/x/distribution/client/rest/rest.go +++ b/x/distribution/client/rest/rest.go @@ -9,5 +9,5 @@ import ( // RegisterRoutes register distribution REST routes. func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, queryRoute string) { registerQueryRoutes(cliCtx, r, cdc, queryRoute) - registerTxRoutes(cliCtx, r, cdc) + registerTxRoutes(cliCtx, r, cdc, queryRoute) } diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index 5a4cc336c352..f253d3041a4c 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -15,12 +15,12 @@ import ( ) func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, - cdc *codec.Codec) { + cdc *codec.Codec, queryRoute string) { // Withdraw delegator rewards r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards", - withdrawDelegatorRewardsHandlerFn(cdc, cliCtx), + withdrawDelegatorRewardsHandlerFn(cdc, cliCtx, queryRoute), ).Methods("POST") // Withdraw delegation rewards @@ -41,9 +41,32 @@ type withdrawRewardsReq struct { BaseReq utils.BaseReq `json:"base_req"` } -func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext, + queryRoute string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - + // var req withdrawRewardsReq + + // if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { + // utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + // return + // } + + // req.BaseReq = req.BaseReq.Sanitize() + // if !req.BaseReq.ValidateBasic(w) { + // return + // } + + // // read and validate URL's variables + // delAddr, abort := checkDelegatorAddressVar(w, r) + // if abort { + // return + // } + + // queryRes, abort := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, delAddr, "") + // if abort { + // return + // } + // return } } From f12c7727ba3662b1de67c21d535a1b6e0c82fea2 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 09:03:04 -0800 Subject: [PATCH 17/29] all_delegation_rewards -> total_delegation_rewards --- PENDING.md | 1 + x/distribution/client/common/common.go | 2 +- x/distribution/keeper/querier.go | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/PENDING.md b/PENDING.md index 73e2f4ce3f79..76e03c476a12 100644 --- a/PENDING.md +++ b/PENDING.md @@ -6,6 +6,7 @@ BREAKING CHANGES * [\#3284](https://github.com/cosmos/cosmos-sdk/issues/3284) Rename the `name` field to `from` in the `base_req` body. * [\#3485](https://github.com/cosmos/cosmos-sdk/pull/3485) Error responses are now JSON objects. + * [distribution] endpoint changed "all_delegation_rewards" -> "total_delegation_rewards" * Gaia CLI (`gaiacli`) - [#3399](https://github.com/cosmos/cosmos-sdk/pull/3399) Add `gaiad validate-genesis` command to facilitate checking of genesis files diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go index 1258b049e990..caa8c1129456 100644 --- a/x/distribution/client/common/common.go +++ b/x/distribution/client/common/common.go @@ -56,7 +56,7 @@ func QueryRewards(cliCtx context.CLIContext, cdc *codec.Codec, if valAddr == "" { params = distr.NewQueryDelegationRewardsParams(delegatorAddr, nil) - route = fmt.Sprintf("custom/%s/all_delegation_rewards", queryRoute) + route = fmt.Sprintf("custom/%s/total_delegation_rewards", queryRoute) } else { validatorAddr, err := sdk.ValAddressFromBech32(valAddr) if err != nil { diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go index abc190d8d907..885d83f6b893 100644 --- a/x/distribution/keeper/querier.go +++ b/x/distribution/keeper/querier.go @@ -12,13 +12,13 @@ import ( // nolint const ( - QueryParams = "params" - QueryOutstandingRewards = "outstanding_rewards" - QueryValidatorCommission = "validator_commission" - QueryValidatorSlashes = "validator_slashes" - QueryDelegationRewards = "delegation_rewards" - QueryAllDelegationRewards = "all_delegation_rewards" - QueryWithdrawAddr = "withdraw_addr" + QueryParams = "params" + QueryOutstandingRewards = "outstanding_rewards" + QueryValidatorCommission = "validator_commission" + QueryValidatorSlashes = "validator_slashes" + QueryDelegationRewards = "delegation_rewards" + QueryTotalDelegationRewards = "total_delegation_rewards" + QueryWithdrawAddr = "withdraw_addr" ParamCommunityTax = "community_tax" ParamBaseProposerReward = "base_proposer_reward" @@ -44,8 +44,8 @@ func NewQuerier(k Keeper) sdk.Querier { case QueryDelegationRewards: return queryDelegationRewards(ctx, path[1:], req, k) - case QueryAllDelegationRewards: - return queryAllDelegationRewards(ctx, path[1:], req, k) + case QueryTotalDelegationRewards: + return queryTotalDelegationRewards(ctx, path[1:], req, k) case QueryWithdrawAddr: return queryDelegatorWithdrawAddress(ctx, path[1:], req, k) @@ -194,7 +194,7 @@ func queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, return bz, nil } -func queryAllDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { +func queryTotalDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { var params QueryDelegationRewardsParams err := k.cdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { From 834dd566ae02cfdb4a9bbb849e6fa78098bcbba3 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 09:03:29 -0800 Subject: [PATCH 18/29] rigel/distr-queriers --- x/distribution/alias.go | 1 + x/distribution/client/common/common.go | 28 +++++++---- x/distribution/keeper/querier.go | 65 +++++++++++++++++++++----- 3 files changed, 73 insertions(+), 21 deletions(-) diff --git a/x/distribution/alias.go b/x/distribution/alias.go index a7fa5812c10c..85b93fc1745b 100644 --- a/x/distribution/alias.go +++ b/x/distribution/alias.go @@ -55,6 +55,7 @@ var ( NewQueryValidatorCommissionParams = keeper.NewQueryValidatorCommissionParams NewQueryValidatorSlashesParams = keeper.NewQueryValidatorSlashesParams NewQueryDelegationRewardsParams = keeper.NewQueryDelegationRewardsParams + NewQueryDelegatorParams = keeper.NewQueryDelegatorParams NewQueryDelegatorWithdrawAddrParams = keeper.NewQueryDelegatorWithdrawAddrParams DefaultParamspace = keeper.DefaultParamspace diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go index caa8c1129456..be6d3c10415c 100644 --- a/x/distribution/client/common/common.go +++ b/x/distribution/client/common/common.go @@ -43,6 +43,9 @@ func QueryParams(cliCtx context.CLIContext, queryRoute string) (PrettyParams, er // QueryParams queries delegator rewards. If valAddr is empty string, // it returns all delegations rewards for the given delegator; else // it returns the rewards for the specific delegation. +// +// TODO refactor remove this function, we should likely not clump these two +// cases together func QueryRewards(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute, delAddr, valAddr string) ([]byte, error) { @@ -51,27 +54,32 @@ func QueryRewards(cliCtx context.CLIContext, cdc *codec.Codec, return nil, err } - var params distr.QueryDelegationRewardsParams var route string + var bz []byte if valAddr == "" { - params = distr.NewQueryDelegationRewardsParams(delegatorAddr, nil) - route = fmt.Sprintf("custom/%s/total_delegation_rewards", queryRoute) + + params := distr.NewQueryDelegatorParams(delegatorAddr) + route = fmt.Sprintf("custom/%s/delegator_total_rewards", queryRoute) + bz, err = cdc.MarshalJSON(params) + if err != nil { + return nil, err + } + } else { + validatorAddr, err := sdk.ValAddressFromBech32(valAddr) if err != nil { return nil, err } - - params = distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) + params := distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) + bz, err = cdc.MarshalJSON(params) + if err != nil { + return nil, err + } route = fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) } - bz, err := cdc.MarshalJSON(params) - if err != nil { - return nil, err - } - return cliCtx.QueryWithData(route, bz) } diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go index 885d83f6b893..d8948f2fbd86 100644 --- a/x/distribution/keeper/querier.go +++ b/x/distribution/keeper/querier.go @@ -12,13 +12,14 @@ import ( // nolint const ( - QueryParams = "params" - QueryOutstandingRewards = "outstanding_rewards" - QueryValidatorCommission = "validator_commission" - QueryValidatorSlashes = "validator_slashes" - QueryDelegationRewards = "delegation_rewards" - QueryTotalDelegationRewards = "total_delegation_rewards" - QueryWithdrawAddr = "withdraw_addr" + QueryParams = "params" + QueryOutstandingRewards = "outstanding_rewards" + QueryValidatorCommission = "validator_commission" + QueryValidatorSlashes = "validator_slashes" + QueryDelegationRewards = "delegation_rewards" + QueryDelegatorTotalRewards = "delegator_total_rewards" + QueryDelegatorValidators = "delegator_validators" + QueryWithdrawAddr = "withdraw_addr" ParamCommunityTax = "community_tax" ParamBaseProposerReward = "base_proposer_reward" @@ -44,8 +45,11 @@ func NewQuerier(k Keeper) sdk.Querier { case QueryDelegationRewards: return queryDelegationRewards(ctx, path[1:], req, k) - case QueryTotalDelegationRewards: - return queryTotalDelegationRewards(ctx, path[1:], req, k) + case QueryDelegatorTotalRewards: + return queryDelegatorTotalRewards(ctx, path[1:], req, k) + + case QueryDelegatorValidators: + return queryDelegatorValidators(ctx, path[1:], req, k) case QueryWithdrawAddr: return queryDelegatorWithdrawAddress(ctx, path[1:], req, k) @@ -194,8 +198,20 @@ func queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, return bz, nil } -func queryTotalDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { - var params QueryDelegationRewardsParams +// params for query 'custom/distr/delegator_total_rewards' and 'custom/distr/delegator_validators' +type QueryDelegatorParams struct { + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` +} + +// creates a new instance of QueryDelegationRewardsParams +func NewQueryDelegatorParams(delegatorAddr sdk.AccAddress) QueryDelegatorParams { + return QueryDelegatorParams{ + DelegatorAddr: delegatorAddr, + } +} + +func queryDelegatorTotalRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params QueryDelegatorParams err := k.cdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) @@ -226,6 +242,33 @@ func queryTotalDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQu return bz, nil } +func queryDelegatorValidators(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params QueryDelegatorParams + err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + // cache-wrap context as to not persist state changes during querying + ctx, _ = ctx.CacheContext() + + var validators []sdk.ValAddress + + k.stakingKeeper.IterateDelegations( + ctx, params.DelegatorAddr, + func(_ int64, del sdk.Delegation) (stop bool) { + validators = append(validators[:], del.GetValidatorAddr()) + return false + }, + ) + + bz, err := codec.MarshalJSONIndent(k.cdc, validators) + 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/withdraw_addr' type QueryDelegatorWithdrawAddrParams struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` From bbc8f99d35916b93b3741188ed53d72d56c0925c Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 1 Feb 2019 17:57:17 -0800 Subject: [PATCH 19/29] Implement /delegators/{delegatorAddr}/rewards --- x/distribution/client/common/common.go | 56 ++++++++++++++++++------ x/distribution/client/rest/tx.go | 59 +++++++++++++++----------- 2 files changed, 77 insertions(+), 38 deletions(-) diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go index be6d3c10415c..15d816c88509 100644 --- a/x/distribution/client/common/common.go +++ b/x/distribution/client/common/common.go @@ -58,31 +58,31 @@ func QueryRewards(cliCtx context.CLIContext, cdc *codec.Codec, var bz []byte if valAddr == "" { - - params := distr.NewQueryDelegatorParams(delegatorAddr) + bz = cdc.MustMarshalJSON(distr.NewQueryDelegatorParams(delegatorAddr)) route = fmt.Sprintf("custom/%s/delegator_total_rewards", queryRoute) - bz, err = cdc.MarshalJSON(params) - if err != nil { - return nil, err - } - } else { - validatorAddr, err := sdk.ValAddressFromBech32(valAddr) if err != nil { return nil, err } - params := distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) - bz, err = cdc.MarshalJSON(params) - if err != nil { - return nil, err - } + bz = cdc.MustMarshalJSON(distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr)) route = fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) } return cliCtx.QueryWithData(route, bz) } +// QueryDelegatorValidators returns delegator's list of validators +// it submitted delegations to. +func QueryDelegatorValidators(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string, delegatorAddr sdk.AccAddress) ([]byte, error) { + + return cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/delegator_validators", queryRoute), + cdc.MustMarshalJSON(distr.NewQueryDelegatorParams(delegatorAddr)), + ) +} + // QueryValidatorCommission returns a validator's commission. func QueryValidatorCommission(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string, validatorAddr sdk.ValAddress) ([]byte, error) { @@ -93,6 +93,36 @@ func QueryValidatorCommission(cliCtx context.CLIContext, cdc *codec.Codec, ) } +// WithdrawAllDelegatorRewards builds a multi-message slice to be used +// to withdraw all delegations rewards for the given delegator. +func WithdrawAllDelegatorRewards(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string, delegatorAddr sdk.AccAddress) ([]sdk.Msg, error) { + + // retrieve the comprehensive list of all validators which the + // delegator had submitted delegations to + bz, err := QueryDelegatorValidators(cliCtx, cdc, queryRoute, delegatorAddr) + if err != nil { + return nil, err + } + + var validators []sdk.ValAddress + if err := cdc.UnmarshalJSON(bz, &validators); err != nil { + return nil, err + } + + // build multi-message transaction + var msgs []sdk.Msg + for _, valAddr := range validators { + msg := distr.NewMsgWithdrawDelegatorReward(delegatorAddr, valAddr) + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + msgs = append(msgs, msg) + } + + return msgs, nil +} + // WithdrawValidatorRewardsAndCommission builds a two-message message slice to be // used to withdraw both validation's commission and self-delegation reward. func WithdrawValidatorRewardsAndCommission(validatorAddr sdk.ValAddress) ([]sdk.Msg, error) { diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index f253d3041a4c..ec218026bfca 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -41,35 +41,43 @@ type withdrawRewardsReq struct { BaseReq utils.BaseReq `json:"base_req"` } +// Withdraw delegator rewards func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - // var req withdrawRewardsReq - - // if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { - // utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - // return - // } - - // req.BaseReq = req.BaseReq.Sanitize() - // if !req.BaseReq.ValidateBasic(w) { - // return - // } - - // // read and validate URL's variables - // delAddr, abort := checkDelegatorAddressVar(w, r) - // if abort { - // return - // } - - // queryRes, abort := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, delAddr, "") - // if abort { - // return - // } - // return + var req withdrawRewardsReq + if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + // read and validate URL's variables + delAddr, abort := checkDelegatorAddressVar(w, r) + if abort { + return + } + + msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, cdc, queryRoute, delAddr) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs) + return + } + + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, msgs, cdc) } } +// Withdraw delegation rewards func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq @@ -102,7 +110,7 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } @@ -110,6 +118,7 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont } } +// Withdraw validator rewards and commission func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq @@ -138,7 +147,7 @@ func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, msgs) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs) return } From bb8767da88f100803f17d5c5a9408d58fdea2ce9 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 1 Feb 2019 19:01:42 -0800 Subject: [PATCH 20/29] Implement cli withdraw-all-rewards command as multi-message tx ref #3476 --- x/distribution/client/cli/tx.go | 33 ++++++++++++++++++++++++++ x/distribution/client/module_client.go | 1 + 2 files changed, 34 insertions(+) diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index 9a761d8de6c1..dfe7c5558460 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -16,6 +16,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" + "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) @@ -89,6 +90,38 @@ func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { return cmd } +// command to withdraw all rewards +func GetCmdWithdrawAllRewards(cdc *codec.Codec, queryRoute string) *cobra.Command { + cmd := &cobra.Command{ + Use: "withdraw-all-rewards [delegator-addr]", + Short: "withdraw all delegations rewards for a delegator", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + + txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContext(). + WithCodec(cdc). + WithAccountDecoder(cdc) + + delAddr := cliCtx.GetFromAddress() + msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, cdc, queryRoute, delAddr) + if err != nil { + return err + } + + if cliCtx.GenerateOnly { + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, msgs, false) + } + + // build and sign the transaction, then broadcast to Tendermint + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, msgs) + }, + } + cmd.Flags().String(flagOnlyFromValidator, "", "only withdraw from this validator address (in bech)") + cmd.Flags().Bool(flagIsValidator, false, "also withdraw validator's commission") + return cmd +} + // GetCmdDelegate implements the delegate command. func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ diff --git a/x/distribution/client/module_client.go b/x/distribution/client/module_client.go index 412bf9d6a439..b9dcb1c82bac 100644 --- a/x/distribution/client/module_client.go +++ b/x/distribution/client/module_client.go @@ -46,6 +46,7 @@ func (mc ModuleClient) GetTxCmd() *cobra.Command { distTxCmd.AddCommand(client.PostCommands( distCmds.GetCmdWithdrawRewards(mc.cdc), distCmds.GetCmdSetWithdrawAddr(mc.cdc), + distCmds.GetCmdWithdrawAllRewards(mc.cdc, mc.storeKey), )...) return distTxCmd From ec342370c81af9a2edc4b3fccf206e0ebbee333f Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 1 Feb 2019 19:23:13 -0800 Subject: [PATCH 21/29] Implement /delegators/{delegatorAddr}/withdraw_address --- x/distribution/client/cli/tx.go | 2 +- x/distribution/client/rest/tx.go | 81 +++++++++++++++++++++++++------- 2 files changed, 66 insertions(+), 17 deletions(-) diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index dfe7c5558460..21a3bb4c3674 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -122,7 +122,7 @@ func GetCmdWithdrawAllRewards(cdc *codec.Codec, queryRoute string) *cobra.Comman return cmd } -// GetCmdDelegate implements the delegate command. +// command to replace a delegator's withdrawal address func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "set-withdraw-addr [withdraw-addr]", diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index ec218026bfca..6826e36abf4d 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -17,7 +17,7 @@ import ( func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, queryRoute string) { - // Withdraw delegator rewards + // Withdraw all delegator rewards r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards", withdrawDelegatorRewardsHandlerFn(cdc, cliCtx, queryRoute), @@ -29,6 +29,12 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, withdrawDelegationRewardsHandlerFn(cdc, cliCtx), ).Methods("POST") + // Replace the rewards withdrawal address + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/withdraw_address", + setDelegatorWithdrawalAddrHandlerFn(cdc, cliCtx), + ).Methods("POST") + // Withdraw validator rewards and commission r.HandleFunc( "/distribution/validators/{validatorAddr}/rewards", @@ -37,9 +43,16 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, } -type withdrawRewardsReq struct { - BaseReq utils.BaseReq `json:"base_req"` -} +type ( + withdrawRewardsReq struct { + BaseReq utils.BaseReq `json:"base_req"` + } + + setWithdrawalAddrReq struct { + BaseReq utils.BaseReq `json:"base_req"` + WithdrawAddress sdk.AccAddress `json:"withdraw_address"` + } +) // Withdraw delegator rewards func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext, @@ -57,8 +70,8 @@ func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte } // read and validate URL's variables - delAddr, abort := checkDelegatorAddressVar(w, r) - if abort { + delAddr, ok := checkDelegatorAddressVar(w, r) + if !ok { return } @@ -93,13 +106,13 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont } // read and validate URL's variables - delAddr, abort := checkDelegatorAddressVar(w, r) - if abort { + delAddr, ok := checkDelegatorAddressVar(w, r) + if !ok { return } - valAddr, abort := checkValidatorAddressVar(w, r) - if abort { + valAddr, ok := checkValidatorAddressVar(w, r) + if !ok { return } @@ -118,6 +131,42 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont } } +// Replace the rewards withdrawal address +func setDelegatorWithdrawalAddrHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req setWithdrawalAddrReq + + if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + // read and validate URL's variables + delAddr, ok := checkDelegatorAddressVar(w, r) + if !ok { + return + } + + msg := types.NewMsgSetWithdrawAddress(delAddr, req.WithdrawAddress) + if err := msg.ValidateBasic(); err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + return + } + + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + } +} + // Withdraw validator rewards and commission func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { @@ -134,8 +183,8 @@ func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte } // read and validate URL's variable - valAddr, abort := checkValidatorAddressVar(w, r) - if abort { + valAddr, ok := checkValidatorAddressVar(w, r) + if !ok { return } @@ -161,16 +210,16 @@ func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAd addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return nil, true + return nil, false } - return addr, false + return addr, true } func checkValidatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.ValAddress, bool) { addr, err := sdk.ValAddressFromBech32(mux.Vars(r)["validatorAddr"]) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return nil, true + return nil, false } - return addr, false + return addr, true } From 20826e8cfc2ef4a9fcd448a019a86ff842fa81b5 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 1 Feb 2019 19:25:02 -0800 Subject: [PATCH 22/29] invert abort with ok --- x/distribution/client/rest/query.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 1e522d61fe06..6a98b55df6f1 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -67,9 +67,9 @@ func delegatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { // query for rewards from a particular delegator - res, abort := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, + res, ok := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, mux.Vars(r)["delegatorAddr"], "") - if abort { + if !ok { return } @@ -83,9 +83,9 @@ func delegationRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { // query for rewards from a particular delegation - res, abort := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, + res, ok := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, mux.Vars(r)["delegatorAddr"], mux.Vars(r)["validatorAddr"]) - if abort { + if !ok { return } @@ -163,9 +163,9 @@ func validatorInfoHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, // self bond rewards delAddr := sdk.AccAddress(validatorAddr) - rewardsRes, abort := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, + rewardsRes, ok := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, delAddr.String(), valAddr) - if abort { + if !ok { return } @@ -191,8 +191,8 @@ func validatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, } delAddr := sdk.AccAddress(validatorAddr).String() - res, abort := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, delAddr, valAddr) - if abort { + res, ok := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, delAddr, valAddr) + if !ok { return } @@ -229,13 +229,13 @@ func outstandingRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, } func checkResponseQueryRewards(w http.ResponseWriter, cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute, delAddr, valAddr string) (res []byte, abort bool) { + queryRoute, delAddr, valAddr string) (res []byte, ok bool) { res, err := common.QueryRewards(cliCtx, cdc, queryRoute, delAddr, valAddr) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return nil, true + return nil, false } - return res, false + return res, true } From 4e23cf375984a4539a4ad5a13b9c36695152aa1a Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 1 Feb 2019 19:40:22 -0800 Subject: [PATCH 23/29] Clean up --- x/distribution/client/rest/query.go | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 6a98b55df6f1..0d5ee9690fe1 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -98,19 +98,12 @@ func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, cdc *codec.Code queryRoute string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - delegatorAddr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - params := distribution.NewQueryDelegatorWithdrawAddrParams(delegatorAddr) - bz, err := cdc.MarshalJSON(params) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + delegatorAddr, ok := checkDelegatorAddressVar(w, r) + if !ok { return } + bz := cdc.MustMarshalJSON(distribution.NewQueryDelegatorWithdrawAddrParams(delegatorAddr)) res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/withdraw_addr", queryRoute), bz) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) @@ -145,9 +138,8 @@ func validatorInfoHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { valAddr := mux.Vars(r)["validatorAddr"] - validatorAddr, err := sdk.ValAddressFromBech32(valAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + validatorAddr, ok := checkValidatorAddressVar(w, r) + if !ok { return } @@ -184,9 +176,8 @@ func validatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { valAddr := mux.Vars(r)["validatorAddr"] - validatorAddr, err := sdk.ValAddressFromBech32(valAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + validatorAddr, ok := checkValidatorAddressVar(w, r) + if !ok { return } From a285f1040654e49ac7a091db09ced0541d7c128a Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 09:04:00 -0800 Subject: [PATCH 24/29] Update PENDING.md --- PENDING.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/PENDING.md b/PENDING.md index 76e03c476a12..3b8cf028ada9 100644 --- a/PENDING.md +++ b/PENDING.md @@ -6,7 +6,7 @@ BREAKING CHANGES * [\#3284](https://github.com/cosmos/cosmos-sdk/issues/3284) Rename the `name` field to `from` in the `base_req` body. * [\#3485](https://github.com/cosmos/cosmos-sdk/pull/3485) Error responses are now JSON objects. - * [distribution] endpoint changed "all_delegation_rewards" -> "total_delegation_rewards" + * [\#3477][distribution] endpoint changed "all_delegation_rewards" -> "delegator_total_rewards" * Gaia CLI (`gaiacli`) - [#3399](https://github.com/cosmos/cosmos-sdk/pull/3399) Add `gaiad validate-genesis` command to facilitate checking of genesis files @@ -26,6 +26,7 @@ BREAKING CHANGES FEATURES * Gaia REST API + * [\#2358](https://github.com/cosmos/cosmos-sdk/issues/2358) Add distribution module REST interface * Gaia CLI (`gaiacli`) * [\#3429](https://github.com/cosmos/cosmos-sdk/issues/3429) Support querying @@ -38,6 +39,7 @@ FEATURES * SDK * \#3270 [x/staking] limit number of ongoing unbonding delegations /redelegations per pair/trio + * [\#3477][distribution] new query endpoint "delegator_validators" * Tendermint @@ -54,6 +56,7 @@ IMPROVEMENTS (auto gas) to work with generate only. * Gaia CLI (`gaiacli`) + * [\#3476](https://github.com/cosmos/cosmos-sdk/issues/3476) New `withdraw-all-rewards` command to withdraw all delegations rewards for delegators. * Gaia * [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account From 0e9504c93c134fadb4cb0f255f11c35fe6cef1a8 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 09:15:01 -0800 Subject: [PATCH 25/29] Clean up rebase leftover, port to new rest package --- client/rest/rest.go | 110 ---------------------------- x/distribution/client/rest/query.go | 26 +++---- x/distribution/client/rest/tx.go | 51 ++++++------- 3 files changed, 39 insertions(+), 148 deletions(-) diff --git a/client/rest/rest.go b/client/rest/rest.go index 705e3fef3de6..0b87e084df87 100644 --- a/client/rest/rest.go +++ b/client/rest/rest.go @@ -99,116 +99,6 @@ func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEm //----------------------------------------------------------------------------- // Building / Sending utilities -// BaseReq defines a structure that can be embedded in other request structures -// that all share common "base" fields. -type BaseReq struct { - From string `json:"from"` - Password string `json:"password"` - Memo string `json:"memo"` - ChainID string `json:"chain_id"` - AccountNumber uint64 `json:"account_number"` - Sequence uint64 `json:"sequence"` - Fees sdk.Coins `json:"fees"` - GasPrices sdk.DecCoins `json:"gas_prices"` - Gas string `json:"gas"` - GasAdjustment string `json:"gas_adjustment"` - GenerateOnly bool `json:"generate_only"` - Simulate bool `json:"simulate"` -} - -// NewBaseReq creates a new basic request instance and sanitizes its values -func NewBaseReq( - from, password, memo, chainID string, gas, gasAdjustment string, - accNumber, seq uint64, fees sdk.Coins, gasPrices sdk.DecCoins, genOnly, simulate bool, -) BaseReq { - - return BaseReq{ - From: strings.TrimSpace(from), - Password: password, - Memo: strings.TrimSpace(memo), - ChainID: strings.TrimSpace(chainID), - Fees: fees, - GasPrices: gasPrices, - Gas: strings.TrimSpace(gas), - GasAdjustment: strings.TrimSpace(gasAdjustment), - AccountNumber: accNumber, - Sequence: seq, - GenerateOnly: genOnly, - Simulate: simulate, - } -} - -// Sanitize performs basic sanitization on a BaseReq object. -func (br BaseReq) Sanitize() BaseReq { - return NewBaseReq( - br.From, br.Password, br.Memo, br.ChainID, br.Gas, br.GasAdjustment, - br.AccountNumber, br.Sequence, br.Fees, br.GasPrices, br.GenerateOnly, br.Simulate, - ) -} - -// ValidateBasic performs basic validation of a BaseReq. If custom validation -// logic is needed, the implementing request handler should perform those -// checks manually. Returns true if validation passes; false otherwise. -func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { - if !br.GenerateOnly && !br.Simulate { - switch { - case len(br.Password) == 0: - WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified") - return false - - case len(br.ChainID) == 0: - WriteErrorResponse(w, http.StatusUnauthorized, "chain-id required but not specified") - return false - - case !br.Fees.IsZero() && !br.GasPrices.IsZero(): - // both fees and gas prices were provided - WriteErrorResponse(w, http.StatusBadRequest, "cannot provide both fees and gas prices") - return false - - case !br.Fees.IsValid() && !br.GasPrices.IsValid(): - // neither fees or gas prices were provided - WriteErrorResponse(w, http.StatusPaymentRequired, "invalid fees or gas prices provided") - return false - } - } - - if len(br.From) == 0 { - WriteErrorResponse(w, http.StatusUnauthorized, "name or address required but not specified") - return false - } - - return true -} - -/* -ReadRESTReq is a simple convenience wrapper that reads the body and -unmarshals to the req interface. - - Usage: - type SomeReq struct { - BaseReq `json:"base_req"` - CustomField string `json:"custom_field"` - } - - req := new(SomeReq) - err := ReadRESTReq(w, r, cdc, req) -*/ -func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) error { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return err - } - - err = cdc.UnmarshalJSON(body, req) - if err != nil { - WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to decode JSON payload: %s", err)) - return err - } - - return nil -} - // CompleteAndBroadcastTxREST implements a utility function that facilitates // sending a series of messages in a signed tx. In addition, it will handle // tx gas simulation and estimation. diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 0d5ee9690fe1..00d459bb9bfa 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" + "github.com/cosmos/cosmos-sdk/client/rest" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/gorilla/mux" @@ -73,7 +73,7 @@ func delegatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -89,7 +89,7 @@ func delegationRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -106,11 +106,11 @@ func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, cdc *codec.Code bz := cdc.MustMarshalJSON(distribution.NewQueryDelegatorWithdrawAddrParams(delegatorAddr)) res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/withdraw_addr", queryRoute), bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -146,7 +146,7 @@ func validatorInfoHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, // query commission commissionRes, err := common.QueryValidatorCommission(cliCtx, cdc, queryRoute, validatorAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -166,7 +166,7 @@ func validatorInfoHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, // Prepare response res := cdc.MustMarshalJSON(NewValidatorDistInfo(delAddr, rewards, valCom)) - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -187,7 +187,7 @@ func validatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -198,10 +198,10 @@ func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { params, err := common.QueryParams(cliCtx, queryRoute) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, params, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, params, cliCtx.Indent) } } @@ -212,10 +212,10 @@ func outstandingRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/outstanding_rewards", queryRoute), []byte{}) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -224,7 +224,7 @@ func checkResponseQueryRewards(w http.ResponseWriter, cliCtx context.CLIContext, res, err := common.QueryRewards(cliCtx, cdc, queryRoute, delAddr, valAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return nil, false } diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index 6826e36abf4d..d8fa5a0dc440 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -3,10 +3,11 @@ package rest import ( "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" @@ -45,11 +46,11 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, type ( withdrawRewardsReq struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` } setWithdrawalAddrReq struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` WithdrawAddress sdk.AccAddress `json:"withdraw_address"` } ) @@ -59,8 +60,8 @@ func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte queryRoute string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq - if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if err := rest.ReadRESTReq(w, r, cdc, &req); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -77,16 +78,16 @@ func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, cdc, queryRoute, delAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs) return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, msgs, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, msgs, cdc) } } @@ -95,8 +96,8 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq - if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if err := rest.ReadRESTReq(w, r, cdc, &req); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -118,16 +119,16 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont msg := types.NewMsgWithdrawDelegatorReward(delAddr, valAddr) if err := msg.ValidateBasic(); err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -136,8 +137,8 @@ func setDelegatorWithdrawalAddrHandlerFn(cdc *codec.Codec, cliCtx context.CLICon return func(w http.ResponseWriter, r *http.Request) { var req setWithdrawalAddrReq - if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if err := rest.ReadRESTReq(w, r, cdc, &req); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -154,16 +155,16 @@ func setDelegatorWithdrawalAddrHandlerFn(cdc *codec.Codec, cliCtx context.CLICon msg := types.NewMsgSetWithdrawAddress(delAddr, req.WithdrawAddress) if err := msg.ValidateBasic(); err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -172,8 +173,8 @@ func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq - if err := utils.ReadRESTReq(w, r, cdc, &req); err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if err := rest.ReadRESTReq(w, r, cdc, &req); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -191,16 +192,16 @@ func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte // prepare multi-message transaction msgs, err := common.WithdrawValidatorRewardsAndCommission(valAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs) return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, msgs, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, msgs, cdc) } } @@ -209,7 +210,7 @@ func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAddress, bool) { addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return nil, false } return addr, true @@ -218,7 +219,7 @@ func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAd func checkValidatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.ValAddress, bool) { addr, err := sdk.ValAddressFromBech32(mux.Vars(r)["validatorAddr"]) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return nil, false } return addr, true From d18d7eb4d08588e848a131a66acf7b772218258c Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 10:53:36 -0800 Subject: [PATCH 26/29] Don't write errors twice when ReadRESTReq() fails Closes: #3490 --- client/rest/types.go | 14 ++++++++------ x/auth/client/rest/sign.go | 3 +-- x/bank/client/rest/sendtx.go | 3 +-- x/distribution/client/rest/tx.go | 12 ++++-------- x/gov/client/rest/rest.go | 19 ++++++------------- x/ibc/client/rest/transfer.go | 3 +-- x/slashing/client/rest/tx.go | 3 +-- x/staking/client/rest/tx.go | 21 ++++++--------------- 8 files changed, 28 insertions(+), 50 deletions(-) diff --git a/client/rest/types.go b/client/rest/types.go index 113dc0650d02..cb409627db99 100644 --- a/client/rest/types.go +++ b/client/rest/types.go @@ -98,7 +98,7 @@ func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { /* ReadRESTReq is a simple convenience wrapper that reads the body and -unmarshals to the req interface. +unmarshals to the req interface. Returns false if errors occurred. Usage: type SomeReq struct { @@ -107,20 +107,22 @@ unmarshals to the req interface. } req := new(SomeReq) - err := ReadRESTReq(w, r, cdc, req) + if ok := ReadRESTReq(w, r, cdc, req); !ok { + return + } */ -func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) error { +func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) bool { body, err := ioutil.ReadAll(r.Body) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return err + return false } err = cdc.UnmarshalJSON(body, req) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to decode JSON payload: %s", err)) - return err + return false } - return nil + return true } diff --git a/x/auth/client/rest/sign.go b/x/auth/client/rest/sign.go index 50ec660552fa..67572eefe080 100644 --- a/x/auth/client/rest/sign.go +++ b/x/auth/client/rest/sign.go @@ -26,8 +26,7 @@ func SignTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha return func(w http.ResponseWriter, r *http.Request) { var m SignBody - if err := rest.ReadRESTReq(w, r, cdc, &m); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &m) { return } diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 70aae05fccc0..c335ebcba7c2 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -45,8 +45,7 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC } var req sendReq - err = rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { + if !rest.ReadRESTReq(w, r, cdc, &req) { return } diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index d8fa5a0dc440..8e5bff39f35d 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -60,8 +60,7 @@ func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte queryRoute string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq - if err := rest.ReadRESTReq(w, r, cdc, &req); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -96,8 +95,7 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq - if err := rest.ReadRESTReq(w, r, cdc, &req); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -137,8 +135,7 @@ func setDelegatorWithdrawalAddrHandlerFn(cdc *codec.Codec, cliCtx context.CLICon return func(w http.ResponseWriter, r *http.Request) { var req setWithdrawalAddrReq - if err := rest.ReadRESTReq(w, r, cdc, &req); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -173,8 +170,7 @@ func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq - if err := rest.ReadRESTReq(w, r, cdc, &req); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index e37f7870ecca..9d040d2060e2 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -77,9 +77,7 @@ type voteReq struct { func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req postProposalReq - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -96,8 +94,7 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han // create the message msg := gov.NewMsgSubmitProposal(req.Title, req.Description, proposalType, req.Proposer, req.InitialDeposit) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -128,8 +125,7 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF } var req depositReq - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -140,8 +136,7 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF // create the message msg := gov.NewMsgDeposit(req.Depositor, proposalID, req.Amount) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -172,8 +167,7 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc } var req voteReq - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -190,8 +184,7 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc // create the message msg := gov.NewMsgVote(req.Voter, proposalID, voteOption) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index fd35764b0dbf..f3ca528dce80 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -39,8 +39,7 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. } var req transferReq - err = rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { + if !rest.ReadRESTReq(w, r, cdc, &req) { return } diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index edbfaec92a2b..609da021c468 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -34,8 +34,7 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL bech32validator := vars["validatorAddr"] var req UnjailReq - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { + if !rest.ReadRESTReq(w, r, cdc, &req) { return } diff --git a/x/staking/client/rest/tx.go b/x/staking/client/rest/tx.go index 397e61a73370..6258d7a7e1ab 100644 --- a/x/staking/client/rest/tx.go +++ b/x/staking/client/rest/tx.go @@ -58,9 +58,7 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. return func(w http.ResponseWriter, r *http.Request) { var req msgDelegationsInput - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -70,8 +68,7 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. } msg := staking.NewMsgDelegate(req.DelegatorAddr, req.ValidatorAddr, req.Delegation) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -103,9 +100,7 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex return func(w http.ResponseWriter, r *http.Request) { var req msgBeginRedelegateInput - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -115,8 +110,7 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex } msg := staking.NewMsgBeginRedelegate(req.DelegatorAddr, req.ValidatorSrcAddr, req.ValidatorDstAddr, req.SharesAmount) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -148,9 +142,7 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx return func(w http.ResponseWriter, r *http.Request) { var req msgUndelegateInput - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -160,8 +152,7 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx } msg := staking.NewMsgUndelegate(req.DelegatorAddr, req.ValidatorAddr, req.SharesAmount) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } From 4291cbe5a9fd969e8a17915a4a9ec008b33f7f5e Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 10:55:32 -0800 Subject: [PATCH 27/29] Update PENDING.md --- PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PENDING.md b/PENDING.md index 3b8cf028ada9..f375f6c962a7 100644 --- a/PENDING.md +++ b/PENDING.md @@ -19,6 +19,7 @@ BREAKING CHANGES * SDK * [\#3487](https://github.com/cosmos/cosmos-sdk/pull/3487) Move HTTP/REST utilities out of client/utils into a new dedicated client/rest package. + * [\#3490](https://github.com/cosmos/cosmos-sdk/issues/3490) ReadRESTReq() returns bool to avoid callers to write error responses twice. * Tendermint From 045d9f4a6e38b20c7c72a65310537805f45ceb7a Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 12:34:34 -0800 Subject: [PATCH 28/29] Split up QueryRewards cc'ing @bez --- x/distribution/client/cli/query.go | 12 ++++--- x/distribution/client/common/common.go | 44 ++++++++++++++------------ x/distribution/client/rest/query.go | 28 +++++++++++----- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 4218cca51f0e..2a23a56b9879 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -130,13 +130,15 @@ func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Com RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - valAddr := "" + var resp []byte + var err error if len(args) == 2 { - // query for rewards from a particular validator - valAddr = args[1] + // 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.QueryRewards(cliCtx, cdc, queryRoute, args[0], valAddr) if err != nil { return err } diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go index 15d816c88509..de78a9bf2328 100644 --- a/x/distribution/client/common/common.go +++ b/x/distribution/client/common/common.go @@ -40,36 +40,38 @@ func QueryParams(cliCtx context.CLIContext, queryRoute string) (PrettyParams, er retBonusProposerReward, retWithdrawAddrEnabled), nil } -// QueryParams queries delegator rewards. If valAddr is empty string, -// it returns all delegations rewards for the given delegator; else -// it returns the rewards for the specific delegation. -// -// TODO refactor remove this function, we should likely not clump these two -// cases together -func QueryRewards(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute, delAddr, valAddr string) ([]byte, error) { +// QueryDelegatorTotalRewards queries delegator total rewards. +func QueryDelegatorTotalRewards(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute, delAddr string) ([]byte, error) { delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) if err != nil { return nil, err } - var route string - var bz []byte + return cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/delegator_total_rewards", queryRoute), + cdc.MustMarshalJSON(distr.NewQueryDelegatorParams(delegatorAddr)), + ) +} - if valAddr == "" { - bz = cdc.MustMarshalJSON(distr.NewQueryDelegatorParams(delegatorAddr)) - route = fmt.Sprintf("custom/%s/delegator_total_rewards", queryRoute) - } else { - validatorAddr, err := sdk.ValAddressFromBech32(valAddr) - if err != nil { - return nil, err - } - bz = cdc.MustMarshalJSON(distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr)) - route = fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) +// QueryDelegationRewards queries a delegation rewards. +func QueryDelegationRewards(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute, delAddr, valAddr string) ([]byte, error) { + + delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) + if err != nil { + return nil, err + } + validatorAddr, err := sdk.ValAddressFromBech32(valAddr) + if err != nil { + return nil, err } - return cliCtx.QueryWithData(route, bz) + return cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/delegation_rewards", queryRoute), + cdc.MustMarshalJSON(distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr)), + ) } // QueryDelegatorValidators returns delegator's list of validators diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 00d459bb9bfa..6f7730407dde 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -61,14 +61,14 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, ).Methods("GET") } -// HTTP request handler to query delegators rewards +// HTTP request handler to query the total rewards balance from all delegations func delegatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // query for rewards from a particular delegator - res, ok := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, - mux.Vars(r)["delegatorAddr"], "") + res, ok := checkResponseQueryDelegatorTotalRewards(w, cliCtx, cdc, queryRoute, + mux.Vars(r)["delegatorAddr"]) if !ok { return } @@ -83,7 +83,7 @@ func delegationRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, return func(w http.ResponseWriter, r *http.Request) { // query for rewards from a particular delegation - res, ok := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, + res, ok := checkResponseQueryDelegationRewards(w, cliCtx, cdc, queryRoute, mux.Vars(r)["delegatorAddr"], mux.Vars(r)["validatorAddr"]) if !ok { return @@ -155,7 +155,7 @@ func validatorInfoHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, // self bond rewards delAddr := sdk.AccAddress(validatorAddr) - rewardsRes, ok := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, + rewardsRes, ok := checkResponseQueryDelegationRewards(w, cliCtx, cdc, queryRoute, delAddr.String(), valAddr) if !ok { return @@ -182,7 +182,7 @@ func validatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, } delAddr := sdk.AccAddress(validatorAddr).String() - res, ok := checkResponseQueryRewards(w, cliCtx, cdc, queryRoute, delAddr, valAddr) + res, ok := checkResponseQueryDelegationRewards(w, cliCtx, cdc, queryRoute, delAddr, valAddr) if !ok { return } @@ -219,10 +219,22 @@ func outstandingRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, } } -func checkResponseQueryRewards(w http.ResponseWriter, cliCtx context.CLIContext, cdc *codec.Codec, +func checkResponseQueryDelegatorTotalRewards(w http.ResponseWriter, cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute, delAddr string) (res []byte, ok bool) { + + res, err := common.QueryDelegatorTotalRewards(cliCtx, cdc, queryRoute, delAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return nil, false + } + + return res, true +} + +func checkResponseQueryDelegationRewards(w http.ResponseWriter, cliCtx context.CLIContext, cdc *codec.Codec, queryRoute, delAddr, valAddr string) (res []byte, ok bool) { - res, err := common.QueryRewards(cliCtx, cdc, queryRoute, delAddr, valAddr) + res, err := common.QueryDelegationRewards(cliCtx, cdc, queryRoute, delAddr, valAddr) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return nil, false From 09239a9f8042619869d4d99b4c58231e5bc3edff Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 15:37:47 -0800 Subject: [PATCH 29/29] Add tests --- client/lcd/lcd_test.go | 98 +++++++++++++++++++++ client/lcd/test_helpers.go | 42 +++++++++ x/distribution/client/common/common_test.go | 4 +- 3 files changed, 142 insertions(+), 2 deletions(-) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index c531a00404ac..591984abd2cb 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -26,6 +26,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" "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" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" @@ -914,3 +916,99 @@ func TestSlashingGetParams(t *testing.T) { err := cdc.UnmarshalJSON([]byte(body), ¶ms) require.NoError(t, err) } + +func TestDistributionGetParams(t *testing.T) { + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + + res, body := Request(t, port, "GET", "/distribution/parameters", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &dclcommon.PrettyParams{})) +} + +func TestDistributionFlow(t *testing.T) { + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) + //addr2, seed2 = CreateAddr(t, name2, pw, GetKeyBase(t)) + cleanup, _, valAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + defer cleanup() + + valAddr := valAddrs[0] + operAddr := sdk.AccAddress(valAddr) + + var rewards sdk.DecCoins + res, body := Request(t, port, "GET", fmt.Sprintf("/distribution/outstanding_rewards"), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, sdk.DecCoins(nil), rewards) + + var valDistInfo distrrest.ValidatorDistInfo + res, body = Request(t, port, "GET", "/distribution/validators/"+valAddr.String(), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &valDistInfo)) + require.Equal(t, valDistInfo.OperatorAddress.String(), sdk.AccAddress(valAddr).String()) + require.Equal(t, valDistInfo.ValidatorCommission, sdk.DecCoins(nil)) + require.Equal(t, valDistInfo.SelfBondRewards, sdk.DecCoins(nil)) + + // Delegate some coins + resultTx := doDelegate(t, port, name1, pw, addr, valAddr, 60, fees) + tests.WaitForHeight(resultTx.Height+1, port) + require.Equal(t, uint32(0), resultTx.CheckTx.Code) + require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + + // send some coins + _, resultTx = doTransfer(t, port, seed, name1, memo, pw, addr, fees) + tests.WaitForHeight(resultTx.Height+5, port) + require.Equal(t, uint32(0), resultTx.CheckTx.Code) + require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + + // Query outstanding rewards changed + oustandingRewards := mustParseDecCoins("9.80stake") + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/outstanding_rewards"), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, oustandingRewards, rewards) + + // Query validator distribution info + res, body = Request(t, port, "GET", "/distribution/validators/"+valAddr.String(), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + valRewards := mustParseDecCoins("6.125stake") + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &valDistInfo)) + require.Equal(t, valRewards, valDistInfo.SelfBondRewards) + + // Query validator's rewards + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/validators/%s/rewards", valAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, valRewards, rewards) + + // Query self-delegation + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/delegators/%s/rewards/%s", operAddr, valAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, valRewards, rewards) + + // Query delegation + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/delegators/%s/rewards/%s", addr, valAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, mustParseDecCoins("3.675stake"), rewards) + + // Query delegator's rewards total + 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.Equal(t, valRewards, rewards) + + // Query delegator's withdrawal address + var withdrawAddr string + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/delegators/%s/withdraw_address", operAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &withdrawAddr)) + require.Equal(t, operAddr.String(), withdrawAddr) + + // Withdraw delegator's rewards + resultTx = doWithdrawDelegatorAllRewards(t, port, seed, name1, pw, addr, fees) + require.Equal(t, uint32(0), resultTx.CheckTx.Code) + require.Equal(t, uint32(0), resultTx.DeliverTx.Code) +} diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index b420215eb245..efef932a6025 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -60,6 +60,8 @@ import ( authRest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bankRest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + distrRest "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" govRest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" slashingRest "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" stakingRest "github.com/cosmos/cosmos-sdk/x/staking/client/rest" @@ -294,6 +296,11 @@ func InitializeTestLCD( genesisState.StakingData.Pool.NotBondedTokens = genesisState.StakingData.Pool.NotBondedTokens.Add(sdk.NewInt(100)) } + inflationMin := sdk.MustNewDecFromStr("10000.0") + genesisState.MintData.Minter.Inflation = inflationMin + genesisState.MintData.Params.InflationMax = sdk.MustNewDecFromStr("15000.0") + genesisState.MintData.Params.InflationMin = inflationMin + appState, err := codec.MarshalJSONIndent(cdc, genesisState) require.NoError(t, err) genDoc.AppState = appState @@ -390,6 +397,7 @@ func registerRoutes(rs *RestServer) { tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) authRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, auth.StoreKey) bankRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + distrRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, distr.StoreKey) stakingRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) slashingRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) govRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) @@ -1382,3 +1390,37 @@ func doUnjail(t *testing.T, port, seed, name, password string, type unjailReq struct { BaseReq rest.BaseReq `json:"base_req"` } + +// ICS24 - fee distribution + +// POST /distribution/delegators/{delgatorAddr}/rewards Withdraw delegator rewards +func doWithdrawDelegatorAllRewards(t *testing.T, port, seed, name, password string, + delegatorAddr sdk.AccAddress, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { + // get the account to get the sequence + acc := getAccount(t, port, delegatorAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + chainID := viper.GetString(client.FlagChainID) + baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) + + wr := struct { + BaseReq rest.BaseReq `json:"base_req"` + }{BaseReq: baseReq} + + req := cdc.MustMarshalJSON(wr) + res, body := Request(t, port, "POST", fmt.Sprintf("/distribution/delegators/%s/rewards", delegatorAddr), req) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results ctypes.ResultBroadcastTxCommit + cdc.MustUnmarshalJSON([]byte(body), &results) + + return results +} + +func mustParseDecCoins(dcstring string) sdk.DecCoins { + dcoins, err := sdk.ParseDecCoins(dcstring) + if err != nil { + panic(err) + } + return dcoins +} diff --git a/x/distribution/client/common/common_test.go b/x/distribution/client/common/common_test.go index 86a936443449..622326232a05 100644 --- a/x/distribution/client/common/common_test.go +++ b/x/distribution/client/common/common_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestQueryRewardsAddrValidation(t *testing.T) { +func TestQueryDelegationRewardsAddrValidation(t *testing.T) { cdc := codec.New() ctx := context.NewCLIContext().WithCodec(cdc) type args struct { @@ -28,7 +28,7 @@ func TestQueryRewardsAddrValidation(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := QueryRewards(ctx, cdc, "", tt.args.delAddr, tt.args.valAddr) + _, err := QueryDelegationRewards(ctx, cdc, "", tt.args.delAddr, tt.args.valAddr) require.True(t, err != nil, tt.wantErr) }) }