diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index 892058f6b38b..4c2a055ac9e2 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -4,6 +4,7 @@ package cli import ( "bufio" "fmt" + "github.com/cosmos/cosmos-sdk/client/tx" "strings" "github.com/spf13/cobra" @@ -34,6 +35,237 @@ const ( MaxMessagesPerTxDefault = 5 ) +// NewTxCmd returns a root CLI command handler for all x/distribution transaction commands. +func NewTxCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + distTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Distribution transactions subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + distTxCmd.AddCommand(flags.PostCommands( + NewWithdrawRewardsCmd(m, txg, ar), + NewWithdrawAllRewardsCmd(m, txg, ar), + NewSetWithdrawAddrCmd(m, txg, ar), + NewFundCommunityPoolCmd(m, txg, ar), + )...) + + return distTxCmd +} + +type newGenerateOrBroadcastFunc func(ctx context.CLIContext, txf tx.Factory, msgs ...sdk.Msg) error + +func newSplitAndApply( + newGenerateOrBroadcast newGenerateOrBroadcastFunc, + cliCtx context.CLIContext, + txBldr tx.Factory, + msgs []sdk.Msg, + chunkSize int, +) error { + if chunkSize == 0 { + return newGenerateOrBroadcast(cliCtx, txBldr, msgs...) + } + + // split messages into slices of length chunkSize + totalMessages := len(msgs) + for i := 0; i < len(msgs); i += chunkSize { + + sliceEnd := i + chunkSize + if sliceEnd > totalMessages { + sliceEnd = totalMessages + } + + msgChunk := msgs[i:sliceEnd] + if err := newGenerateOrBroadcast(cliCtx, txBldr, msgChunk...); err != nil { + return err + } + } + + return nil +} + +func NewWithdrawRewardsCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + cmd := &cobra.Command{ + Use: "withdraw-rewards [validator-addr]", + Short: "Withdraw rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator", + Long: strings.TrimSpace( + fmt.Sprintf(`Withdraw rewards from a given delegation address, +and optionally withdraw validator commission if the delegation address given is a validator operator. + +Example: +$ %s tx distribution withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey +$ %s tx distribution withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey --commission +`, + version.ClientName, version.ClientName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) + txf := tx.NewFactoryFromCLI(inBuf). + WithTxGenerator(txg). + WithAccountRetriever(ar) + cliCtx := context.NewCLIContextWithInputAndFrom(inBuf, args[0]).WithMarshaler(m) + + delAddr := cliCtx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + msgs := []sdk.Msg{types.NewMsgWithdrawDelegatorReward(delAddr, valAddr)} + if viper.GetBool(flagCommission) { + msgs = append(msgs, types.NewMsgWithdrawValidatorCommission(valAddr)) + } + + for _, msg := range msgs { + if err := msg.ValidateBasic(); err != nil { + return err + } + } + + return tx.GenerateOrBroadcastTx(cliCtx, txf, msgs...) + }, + } + cmd.Flags().Bool(flagCommission, false, "also withdraw validator's commission") + return flags.PostCommands(cmd)[0] +} + +func NewWithdrawAllRewardsCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + cmd := &cobra.Command{ + Use: "withdraw-all-rewards", + Short: "withdraw all delegations rewards for a delegator", + Long: strings.TrimSpace( + fmt.Sprintf(`Withdraw all rewards for a single delegator. + +Example: +$ %s tx distribution withdraw-all-rewards --from mykey +`, + version.ClientName, + ), + ), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) + txf := tx.NewFactoryFromCLI(inBuf). + WithTxGenerator(txg). + WithAccountRetriever(ar) + cliCtx := context.NewCLIContextWithInputAndFrom(inBuf, args[0]).WithMarshaler(m) + + delAddr := cliCtx.GetFromAddress() + + // The transaction cannot be generated offline since it requires a query + // to get all the validators. + if cliCtx.Offline { + return fmt.Errorf("cannot generate tx in offline mode") + } + + msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, types.QuerierRoute, delAddr) + if err != nil { + return err + } + + chunkSize := viper.GetInt(flagMaxMessagesPerTx) + return newSplitAndApply(tx.GenerateOrBroadcastTx, cliCtx, txf, msgs, chunkSize) + }, + } + return flags.PostCommands(cmd)[0] +} + +func NewSetWithdrawAddrCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + cmd := &cobra.Command{ + Use: "set-withdraw-addr [withdraw-addr]", + Short: "change the default withdraw address for rewards associated with an address", + Long: strings.TrimSpace( + fmt.Sprintf(`Set the withdraw address for rewards associated with a delegator address. + +Example: +$ %s tx distribution set-withdraw-addr cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --from mykey +`, + version.ClientName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) + txf := tx.NewFactoryFromCLI(inBuf). + WithTxGenerator(txg). + WithAccountRetriever(ar) + cliCtx := context.NewCLIContextWithInputAndFrom(inBuf, args[0]).WithMarshaler(m) + + delAddr := cliCtx.GetFromAddress() + withdrawAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTx(cliCtx, txf, msg) + }, + } + return flags.PostCommands(cmd)[0] +} + +func NewFundCommunityPoolCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + cmd := &cobra.Command{ + Use: "community-pool-spend [proposal-file]", + Args: cobra.ExactArgs(1), + Short: "Submit a community pool spend proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a community pool spend proposal along with an initial deposit. +The proposal details must be supplied via a JSON file. + +Example: +$ %s tx gov submit-proposal community-pool-spend --from= + +Where proposal.json contains: + +{ + "title": "Community Pool Spend", + "description": "Pay me some Atoms!", + "recipient": "cosmos1s5afhd6gxevu37mkqcvvsj8qeylhn0rz46zdlq", + "amount": "1000stake", + "deposit": "1000stake" +} +`, + version.ClientName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) + txf := tx.NewFactoryFromCLI(inBuf). + WithTxGenerator(txg). + WithAccountRetriever(ar) + cliCtx := context.NewCLIContextWithInputAndFrom(inBuf, args[0]).WithMarshaler(m) + + depositorAddr := cliCtx.GetFromAddress() + amount, err := sdk.ParseCoins(args[0]) + if err != nil { + return err + } + + msg := types.NewMsgFundCommunityPool(amount, depositorAddr) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTx(cliCtx, txf, msg) + }, + } + return flags.PostCommands(cmd)[0] +} + +// --------------------------------------------------------------------------- +// Deprecated +// +// TODO: Remove once client-side Protobuf migration has been completed. +// --------------------------------------------------------------------------- // GetTxCmd returns the transaction commands for this module func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { distTxCmd := &cobra.Command{ @@ -184,7 +416,6 @@ $ %s tx distribution set-withdraw-addr cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75 ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc)) cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 1d614c17fe6a..4f0c203ee721 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -14,59 +14,59 @@ import ( "github.com/cosmos/cosmos-sdk/types/rest" ) -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute string) { +func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { // Get the total rewards balance from all delegations r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards", - delegatorRewardsHandlerFn(cliCtx, queryRoute), + delegatorRewardsHandlerFn(cliCtx), ).Methods("GET") // Query a delegation reward r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", - delegationRewardsHandlerFn(cliCtx, queryRoute), + delegationRewardsHandlerFn(cliCtx), ).Methods("GET") // Get the rewards withdrawal address r.HandleFunc( "/distribution/delegators/{delegatorAddr}/withdraw_address", - delegatorWithdrawalAddrHandlerFn(cliCtx, queryRoute), + delegatorWithdrawalAddrHandlerFn(cliCtx), ).Methods("GET") // Validator distribution information r.HandleFunc( "/distribution/validators/{validatorAddr}", - validatorInfoHandlerFn(cliCtx, queryRoute), + validatorInfoHandlerFn(cliCtx), ).Methods("GET") // Commission and self-delegation rewards of a single a validator r.HandleFunc( "/distribution/validators/{validatorAddr}/rewards", - validatorRewardsHandlerFn(cliCtx, queryRoute), + validatorRewardsHandlerFn(cliCtx), ).Methods("GET") // Outstanding rewards of a single validator r.HandleFunc( "/distribution/validators/{validatorAddr}/outstanding_rewards", - outstandingRewardsHandlerFn(cliCtx, queryRoute), + outstandingRewardsHandlerFn(cliCtx), ).Methods("GET") // Get the current distribution parameter values r.HandleFunc( "/distribution/parameters", - paramsHandlerFn(cliCtx, queryRoute), + paramsHandlerFn(cliCtx), ).Methods("GET") // Get the amount held in the community pool r.HandleFunc( "/distribution/community_pool", - communityPoolHandler(cliCtx, queryRoute), + communityPoolHandler(cliCtx), ).Methods("GET") } // HTTP request handler to query the total rewards balance from all delegations -func delegatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func delegatorRewardsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) if !ok { @@ -85,7 +85,7 @@ func delegatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) htt return } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorTotalRewards) + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorTotalRewards) res, height, err := cliCtx.QueryWithData(route, bz) if rest.CheckInternalServerError(w, err) { return @@ -97,7 +97,7 @@ func delegatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) htt } // HTTP request handler to query a delegation rewards -func delegationRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func delegationRewardsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) if !ok { @@ -108,7 +108,7 @@ func delegationRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) ht valAddr := mux.Vars(r)["validatorAddr"] // query for rewards from a particular delegation - res, height, ok := checkResponseQueryDelegationRewards(w, cliCtx, queryRoute, delAddr, valAddr) + res, height, ok := checkResponseQueryDelegationRewards(w, cliCtx, types.QuerierRoute, delAddr, valAddr) if !ok { return } @@ -119,7 +119,7 @@ func delegationRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) ht } // HTTP request handler to query a delegation rewards -func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { delegatorAddr, ok := checkDelegatorAddressVar(w, r) if !ok { @@ -132,7 +132,7 @@ func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, queryRoute stri } bz := cliCtx.Codec.MustMarshalJSON(types.NewQueryDelegatorWithdrawAddrParams(delegatorAddr)) - res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/withdraw_addr", queryRoute), bz) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/withdraw_addr", types.QuerierRoute), bz) if rest.CheckInternalServerError(w, err) { return } @@ -161,7 +161,7 @@ func NewValidatorDistInfo(operatorAddr sdk.AccAddress, rewards sdk.DecCoins, } // HTTP request handler to query validator's distribution information -func validatorInfoHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func validatorInfoHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { valAddr, ok := checkValidatorAddressVar(w, r) if !ok { @@ -174,7 +174,7 @@ func validatorInfoHandlerFn(cliCtx context.CLIContext, queryRoute string) http.H } // query commission - bz, err := common.QueryValidatorCommission(cliCtx, queryRoute, valAddr) + bz, err := common.QueryValidatorCommission(cliCtx, types.QuerierRoute, valAddr) if rest.CheckInternalServerError(w, err) { return } @@ -186,7 +186,7 @@ func validatorInfoHandlerFn(cliCtx context.CLIContext, queryRoute string) http.H // self bond rewards delAddr := sdk.AccAddress(valAddr) - bz, height, ok := checkResponseQueryDelegationRewards(w, cliCtx, queryRoute, delAddr.String(), valAddr.String()) + bz, height, ok := checkResponseQueryDelegationRewards(w, cliCtx, types.QuerierRoute, delAddr.String(), valAddr.String()) if !ok { return } @@ -207,7 +207,7 @@ func validatorInfoHandlerFn(cliCtx context.CLIContext, queryRoute string) http.H } // HTTP request handler to query validator's commission and self-delegation rewards -func validatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func validatorRewardsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { valAddr := mux.Vars(r)["validatorAddr"] validatorAddr, ok := checkValidatorAddressVar(w, r) @@ -221,7 +221,7 @@ func validatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) htt } delAddr := sdk.AccAddress(validatorAddr).String() - bz, height, ok := checkResponseQueryDelegationRewards(w, cliCtx, queryRoute, delAddr, valAddr) + bz, height, ok := checkResponseQueryDelegationRewards(w, cliCtx, types.QuerierRoute, delAddr, valAddr) if !ok { return } @@ -232,7 +232,7 @@ func validatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) htt } // HTTP request handler to query the distribution params values -func paramsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func paramsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) if !ok { @@ -250,14 +250,14 @@ func paramsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerF } } -func communityPoolHandler(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func communityPoolHandler(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) if !ok { return } - res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/community_pool", queryRoute), nil) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/community_pool", types.QuerierRoute), nil) if rest.CheckInternalServerError(w, err) { return } @@ -273,7 +273,7 @@ func communityPoolHandler(cliCtx context.CLIContext, queryRoute string) http.Han } // HTTP request handler to query the outstanding rewards -func outstandingRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func outstandingRewardsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { validatorAddr, ok := checkValidatorAddressVar(w, r) if !ok { @@ -286,7 +286,7 @@ func outstandingRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) h } bin := cliCtx.Codec.MustMarshalJSON(types.NewQueryValidatorOutstandingRewardsParams(validatorAddr)) - res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_outstanding_rewards", queryRoute), bin) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_outstanding_rewards", types.QuerierRoute), bin) if rest.CheckInternalServerError(w, err) { return } diff --git a/x/distribution/client/rest/rest.go b/x/distribution/client/rest/rest.go index ee26ce9544f5..02fe286f9f65 100644 --- a/x/distribution/client/rest/rest.go +++ b/x/distribution/client/rest/rest.go @@ -1,6 +1,8 @@ package rest import ( + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" "net/http" "github.com/gorilla/mux" @@ -14,12 +16,18 @@ import ( govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" ) +func RegisterHandlers(cliCtx context.CLIContext, m codec.Marshaler, txg tx.Generator, r *mux.Router) { + registerQueryRoutes(cliCtx, r) + registerTxHandlers(cliCtx, m, txg, r) +} + // RegisterRoutes register distribution REST routes. func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute string) { - registerQueryRoutes(cliCtx, r, queryRoute) + registerQueryRoutes(cliCtx, r) registerTxRoutes(cliCtx, r, queryRoute) } +// TODO add proto compatible Handler after x/gov migration // ProposalRESTHandler returns a ProposalRESTHandler that exposes the community pool spend REST handler with a given sub-route. func ProposalRESTHandler(cliCtx context.CLIContext) govrest.ProposalRESTHandler { return govrest.ProposalRESTHandler{ diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index 233c92832c78..28be05d77f52 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -1,6 +1,8 @@ package rest import ( + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" "net/http" "github.com/gorilla/mux" @@ -14,54 +16,236 @@ import ( "github.com/cosmos/cosmos-sdk/types/rest" ) -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute string) { +type ( + withdrawRewardsReq struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + } + + setWithdrawalAddrReq struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + WithdrawAddress sdk.AccAddress `json:"withdraw_address" yaml:"withdraw_address"` + } + + fundCommunityPoolReq struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + Amount sdk.Coins `json:"amount" yaml:"amount"` + } +) + +func registerTxHandlers(cliCtx context.CLIContext, m codec.Marshaler, txg tx.Generator, r *mux.Router) { // Withdraw all delegator rewards r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards", - withdrawDelegatorRewardsHandlerFn(cliCtx, queryRoute), + newWithdrawDelegatorRewardsHandlerFn(cliCtx, m, txg), ).Methods("POST") // Withdraw delegation rewards r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", - withdrawDelegationRewardsHandlerFn(cliCtx), + newWithdrawDelegationRewardsHandlerFn(cliCtx, m, txg), ).Methods("POST") // Replace the rewards withdrawal address r.HandleFunc( "/distribution/delegators/{delegatorAddr}/withdraw_address", - setDelegatorWithdrawalAddrHandlerFn(cliCtx), + newSetDelegatorWithdrawalAddrHandlerFn(cliCtx, m, txg), ).Methods("POST") // Withdraw validator rewards and commission r.HandleFunc( "/distribution/validators/{validatorAddr}/rewards", - withdrawValidatorRewardsHandlerFn(cliCtx), + newWithdrawValidatorRewardsHandlerFn(cliCtx, m, txg), ).Methods("POST") // Fund the community pool r.HandleFunc( "/distribution/community_pool", - fundCommunityPoolHandlerFn(cliCtx), + newFundCommunityPoolHandlerFn(cliCtx, m, txg), ).Methods("POST") +} +func newWithdrawDelegatorRewardsHandlerFn(cliCtx context.CLIContext, m codec.Marshaler, txg tx.Generator) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cliCtx = cliCtx.WithMarshaler(m) + var req withdrawRewardsReq + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + 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 + } + + msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, types.QuerierRoute, delAddr) + if rest.CheckInternalServerError(w, err) { + return + } + + tx.WriteGeneratedTxResponse(cliCtx, w, txg, req.BaseReq, msgs...) + } } -type ( - withdrawRewardsReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` +func newWithdrawDelegationRewardsHandlerFn(cliCtx context.CLIContext, m codec.Marshaler, txg tx.Generator) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cliCtx = cliCtx.WithMarshaler(m) + var req withdrawRewardsReq + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + 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 + } + + valAddr, ok := checkValidatorAddressVar(w, r) + if !ok { + return + } + + msg := types.NewMsgWithdrawDelegatorReward(delAddr, valAddr) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { + return + } + + tx.WriteGeneratedTxResponse(cliCtx, w, txg, req.BaseReq, msg) } +} - setWithdrawalAddrReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` - WithdrawAddress sdk.AccAddress `json:"withdraw_address" yaml:"withdraw_address"` +func newSetDelegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, m codec.Marshaler, txg tx.Generator) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cliCtx = cliCtx.WithMarshaler(m) + var req setWithdrawalAddrReq + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + 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 rest.CheckBadRequestError(w, msg.ValidateBasic()) { + return + } + + tx.WriteGeneratedTxResponse(cliCtx, w, txg, req.BaseReq, msg) } +} - fundCommunityPoolReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` - Amount sdk.Coins `json:"amount" yaml:"amount"` +func newWithdrawValidatorRewardsHandlerFn(cliCtx context.CLIContext, m codec.Marshaler, txg tx.Generator) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cliCtx = cliCtx.WithMarshaler(m) + var req withdrawRewardsReq + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + // read and validate URL's variable + valAddr, ok := checkValidatorAddressVar(w, r) + if !ok { + return + } + + // prepare multi-message transaction + msgs, err := common.WithdrawValidatorRewardsAndCommission(valAddr) + if rest.CheckBadRequestError(w, err) { + return + } + + tx.WriteGeneratedTxResponse(cliCtx, w, txg, req.BaseReq, msgs...) } -) +} + +func newFundCommunityPoolHandlerFn(cliCtx context.CLIContext, m codec.Marshaler, txg tx.Generator) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cliCtx = cliCtx.WithMarshaler(m) + var req fundCommunityPoolReq + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if rest.CheckBadRequestError(w, err) { + return + } + + msg := types.NewMsgFundCommunityPool(req.Amount, fromAddr) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { + return + } + + tx.WriteGeneratedTxResponse(cliCtx, w, txg, req.BaseReq, msg) + } +} + +// --------------------------------------------------------------------------- +// Deprecated +// +// TODO: Remove once client-side Protobuf migration has been completed. +// --------------------------------------------------------------------------- +func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute string) { + // Withdraw all delegator rewards + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/rewards", + withdrawDelegatorRewardsHandlerFn(cliCtx, queryRoute), + ).Methods("POST") + + // Withdraw delegation rewards + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", + withdrawDelegationRewardsHandlerFn(cliCtx), + ).Methods("POST") + + // Replace the rewards withdrawal address + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/withdraw_address", + setDelegatorWithdrawalAddrHandlerFn(cliCtx), + ).Methods("POST") + + // Withdraw validator rewards and commission + r.HandleFunc( + "/distribution/validators/{validatorAddr}/rewards", + withdrawValidatorRewardsHandlerFn(cliCtx), + ).Methods("POST") + + // Fund the community pool + r.HandleFunc( + "/distribution/community_pool", + fundCommunityPoolHandlerFn(cliCtx), + ).Methods("POST") + +} // Withdraw delegator rewards func withdrawDelegatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc {