From 7f78e61b93a522989b603e30871b4e9e9a779402 Mon Sep 17 00:00:00 2001 From: SaReN Date: Sat, 4 Apr 2020 01:36:37 +0530 Subject: [PATCH] Merge PR #5915: Tx Client Migration: x/staking --- x/slashing/client/cli/tx.go | 2 +- x/staking/client/cli/tx.go | 476 +++++++++++++++++++++++++++------- x/staking/client/rest/rest.go | 7 + x/staking/client/rest/tx.go | 128 ++++++++- 4 files changed, 521 insertions(+), 92 deletions(-) diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index 9d8ea841d2..e04a7e16cf 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -20,7 +20,7 @@ import ( func NewTxCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { slashingTxCmd := &cobra.Command{ Use: types.ModuleName, - Short: "Bank transaction subcommands", + Short: "Slashing transaction subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, diff --git a/x/staking/client/cli/tx.go b/x/staking/client/cli/tx.go index 3199ece93d..c9b9211e18 100644 --- a/x/staking/client/cli/tx.go +++ b/x/staking/client/cli/tx.go @@ -16,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" @@ -24,6 +25,394 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/types" ) +//__________________________________________________________ + +var ( + defaultTokens = sdk.TokensFromConsensusPower(100) + defaultAmount = defaultTokens.String() + sdk.DefaultBondDenom + defaultCommissionRate = "0.1" + defaultCommissionMaxRate = "0.2" + defaultCommissionMaxChangeRate = "0.01" + defaultMinSelfDelegation = "1" +) + +// NewTxCmd returns a root CLI command handler for all x/staking transaction commands. +func NewTxCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + stakingTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Staking transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + stakingTxCmd.AddCommand(flags.PostCommands( + NewCreateValidatorCmd(m, txg, ar), + NewEditValidatorCmd(m, txg, ar), + NewDelegateCmd(m, txg, ar), + NewRedelegateCmd(m, txg, ar), + NewUnbondCmd(m, txg, ar), + )...) + return stakingTxCmd +} + +func NewCreateValidatorCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + cmd := &cobra.Command{ + Use: "create-validator", + Short: "create new validator initialized with a self-delegation to it", + 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) + + txf, msg, err := NewBuildCreateValidatorMsg(cliCtx, txf) + if err != nil { + return err + } + return tx.GenerateOrBroadcastTx(cliCtx, txf, msg) + }, + } + cmd.Flags().AddFlagSet(FsPk) + cmd.Flags().AddFlagSet(FsAmount) + cmd.Flags().AddFlagSet(fsDescriptionCreate) + cmd.Flags().AddFlagSet(FsCommissionCreate) + cmd.Flags().AddFlagSet(FsMinSelfDelegation) + + cmd.Flags().String(FlagIP, "", fmt.Sprintf("The node's public IP. It takes effect only when used in combination with --%s", flags.FlagGenerateOnly)) + cmd.Flags().String(FlagNodeID, "", "The node's ID") + + cmd.MarkFlagRequired(flags.FlagFrom) + cmd.MarkFlagRequired(FlagAmount) + cmd.MarkFlagRequired(FlagPubKey) + cmd.MarkFlagRequired(FlagMoniker) + return flags.PostCommands(cmd)[0] +} + +func NewEditValidatorCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + cmd := &cobra.Command{ + Use: "edit-validator", + Short: "edit an existing validator account", + 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) + + valAddr := cliCtx.GetFromAddress() + description := types.NewDescription( + viper.GetString(FlagMoniker), + viper.GetString(FlagIdentity), + viper.GetString(FlagWebsite), + viper.GetString(FlagSecurityContact), + viper.GetString(FlagDetails), + ) + + var newRate *sdk.Dec + + commissionRate := viper.GetString(FlagCommissionRate) + if commissionRate != "" { + rate, err := sdk.NewDecFromStr(commissionRate) + if err != nil { + return fmt.Errorf("invalid new commission rate: %v", err) + } + + newRate = &rate + } + + var newMinSelfDelegation *sdk.Int + + minSelfDelegationString := viper.GetString(FlagMinSelfDelegation) + if minSelfDelegationString != "" { + msb, ok := sdk.NewIntFromString(minSelfDelegationString) + if !ok { + return types.ErrMinSelfDelegationInvalid + } + + newMinSelfDelegation = &msb + } + + msg := types.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate, newMinSelfDelegation) + + // build and sign the transaction, then broadcast to Tendermint + return tx.GenerateOrBroadcastTx(cliCtx, txf, msg) + }, + } + return flags.PostCommands(cmd)[0] +} + +func NewDelegateCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + cmd := &cobra.Command{ + Use: "delegate [validator-addr] [amount]", + Args: cobra.ExactArgs(2), + Short: "Delegate liquid tokens to a validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Delegate an amount of liquid coins to a validator from your wallet. + +Example: +$ %s tx staking delegate cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 1000stake --from mykey +`, + 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) + + amount, err := sdk.ParseCoin(args[1]) + if err != nil { + return err + } + + delAddr := cliCtx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + msg := types.NewMsgDelegate(delAddr, valAddr, amount) + return tx.GenerateOrBroadcastTx(cliCtx, txf, msg) + }, + } + cmd.Flags().AddFlagSet(fsDescriptionEdit) + cmd.Flags().AddFlagSet(fsCommissionUpdate) + cmd.Flags().AddFlagSet(FsMinSelfDelegation) + + return flags.PostCommands(cmd)[0] +} + +func NewRedelegateCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + cmd := &cobra.Command{ + Use: "redelegate [src-validator-addr] [dst-validator-addr] [amount]", + Short: "Redelegate illiquid tokens from one validator to another", + Args: cobra.ExactArgs(3), + Long: strings.TrimSpace( + fmt.Sprintf(`Redelegate an amount of illiquid staking tokens from one validator to another. + +Example: +$ %s tx staking redelegate cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 100stake --from mykey +`, + 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) + delAddr := cliCtx.GetFromAddress() + valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + valDstAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoin(args[2]) + if err != nil { + return err + } + + msg := types.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount) + return tx.GenerateOrBroadcastTx(cliCtx, txf, msg) + }, + } + return flags.PostCommands(cmd)[0] +} + +func NewUnbondCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + cmd := &cobra.Command{ + Use: "unbond [validator-addr] [amount]", + Short: "Unbond shares from a validator", + Args: cobra.ExactArgs(2), + Long: strings.TrimSpace( + fmt.Sprintf(`Unbond an amount of bonded shares from a validator. + +Example: +$ %s tx staking unbond cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from mykey +`, + 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) + + delAddr := cliCtx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoin(args[1]) + if err != nil { + return err + } + + msg := types.NewMsgUndelegate(delAddr, valAddr, amount) + return tx.GenerateOrBroadcastTx(cliCtx, txf, msg) + }, + } + return flags.PostCommands(cmd)[0] +} + +func NewBuildCreateValidatorMsg(cliCtx context.CLIContext, txf tx.Factory) (tx.Factory, sdk.Msg, error) { + amounstStr := viper.GetString(FlagAmount) + amount, err := sdk.ParseCoin(amounstStr) + if err != nil { + return txf, nil, err + } + + valAddr := cliCtx.GetFromAddress() + pkStr := viper.GetString(FlagPubKey) + + pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, pkStr) + if err != nil { + return txf, nil, err + } + + description := types.NewDescription( + viper.GetString(FlagMoniker), + viper.GetString(FlagIdentity), + viper.GetString(FlagWebsite), + viper.GetString(FlagSecurityContact), + viper.GetString(FlagDetails), + ) + + // get the initial validator commission parameters + rateStr := viper.GetString(FlagCommissionRate) + maxRateStr := viper.GetString(FlagCommissionMaxRate) + maxChangeRateStr := viper.GetString(FlagCommissionMaxChangeRate) + commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr) + if err != nil { + return txf, nil, err + } + + // get the initial validator min self delegation + msbStr := viper.GetString(FlagMinSelfDelegation) + minSelfDelegation, ok := sdk.NewIntFromString(msbStr) + if !ok { + return txf, nil, types.ErrMinSelfDelegationInvalid + } + + msg := types.NewMsgCreateValidator( + sdk.ValAddress(valAddr), pk, amount, description, commissionRates, minSelfDelegation, + ) + + if viper.GetBool(flags.FlagGenerateOnly) { + ip := viper.GetString(FlagIP) + nodeID := viper.GetString(FlagNodeID) + if nodeID != "" && ip != "" { + txf = txf.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) + } + } + + return txf, msg, nil +} + +// Return the flagset, particular flags, and a description of defaults +// this is anticipated to be used with the gen-tx +func CreateValidatorMsgHelpers(ipDefault string) (fs *flag.FlagSet, nodeIDFlag, pubkeyFlag, amountFlag, defaultsDesc string) { + + fsCreateValidator := flag.NewFlagSet("", flag.ContinueOnError) + fsCreateValidator.String(FlagIP, ipDefault, "The node's public IP") + fsCreateValidator.String(FlagNodeID, "", "The node's NodeID") + fsCreateValidator.String(FlagWebsite, "", "The validator's (optional) website") + fsCreateValidator.String(FlagSecurityContact, "", "The validator's (optional) security contact email") + fsCreateValidator.String(FlagDetails, "", "The validator's (optional) details") + fsCreateValidator.String(FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)") + fsCreateValidator.AddFlagSet(FsCommissionCreate) + fsCreateValidator.AddFlagSet(FsMinSelfDelegation) + fsCreateValidator.AddFlagSet(FsAmount) + fsCreateValidator.AddFlagSet(FsPk) + + defaultsDesc = fmt.Sprintf(` + delegation amount: %s + commission rate: %s + commission max rate: %s + commission max change rate: %s + minimum self delegation: %s +`, defaultAmount, defaultCommissionRate, + defaultCommissionMaxRate, defaultCommissionMaxChangeRate, + defaultMinSelfDelegation) + + return fsCreateValidator, FlagNodeID, FlagPubKey, FlagAmount, defaultsDesc +} + +// prepare flags in config +func PrepareFlagsForTxCreateValidator( + config *cfg.Config, nodeID, chainID string, valPubKey crypto.PubKey, +) { + + ip := viper.GetString(FlagIP) + if ip == "" { + fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP; "+ + "the tx's memo field will be unset") + } + + website := viper.GetString(FlagWebsite) + securityContact := viper.GetString(FlagSecurityContact) + details := viper.GetString(FlagDetails) + identity := viper.GetString(FlagIdentity) + + viper.Set(flags.FlagChainID, chainID) + viper.Set(flags.FlagFrom, viper.GetString(flags.FlagName)) + viper.Set(FlagNodeID, nodeID) + viper.Set(FlagIP, ip) + viper.Set(FlagPubKey, sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey)) + viper.Set(FlagMoniker, config.Moniker) + viper.Set(FlagWebsite, website) + viper.Set(FlagSecurityContact, securityContact) + viper.Set(FlagDetails, details) + viper.Set(FlagIdentity, identity) + + if config.Moniker == "" { + viper.Set(FlagMoniker, viper.GetString(flags.FlagName)) + } + if viper.GetString(FlagAmount) == "" { + viper.Set(FlagAmount, defaultAmount) + } + if viper.GetString(FlagCommissionRate) == "" { + viper.Set(FlagCommissionRate, defaultCommissionRate) + } + if viper.GetString(FlagCommissionMaxRate) == "" { + viper.Set(FlagCommissionMaxRate, defaultCommissionMaxRate) + } + if viper.GetString(FlagCommissionMaxChangeRate) == "" { + viper.Set(FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate) + } + if viper.GetString(FlagMinSelfDelegation) == "" { + viper.Set(FlagMinSelfDelegation, defaultMinSelfDelegation) + } +} + +// --------------------------------------------------------------------------- +// Deprecated +// +// TODO: Remove once client-side Protobuf migration has been completed. +// --------------------------------------------------------------------------- + +// GetTxCmd returns the transaction commands for this module +// +// TODO: Remove once client-side Protobuf migration has been completed. +// ref: https://github.com/cosmos/cosmos-sdk/issues/5864 // GetTxCmd returns the transaction commands for this module func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { stakingTxCmd := &cobra.Command{ @@ -255,93 +644,6 @@ $ %s tx staking unbond cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100s } } -//__________________________________________________________ - -var ( - defaultTokens = sdk.TokensFromConsensusPower(100) - defaultAmount = defaultTokens.String() + sdk.DefaultBondDenom - defaultCommissionRate = "0.1" - defaultCommissionMaxRate = "0.2" - defaultCommissionMaxChangeRate = "0.01" - defaultMinSelfDelegation = "1" -) - -// Return the flagset, particular flags, and a description of defaults -// this is anticipated to be used with the gen-tx -func CreateValidatorMsgHelpers(ipDefault string) (fs *flag.FlagSet, nodeIDFlag, pubkeyFlag, amountFlag, defaultsDesc string) { - - fsCreateValidator := flag.NewFlagSet("", flag.ContinueOnError) - fsCreateValidator.String(FlagIP, ipDefault, "The node's public IP") - fsCreateValidator.String(FlagNodeID, "", "The node's NodeID") - fsCreateValidator.String(FlagWebsite, "", "The validator's (optional) website") - fsCreateValidator.String(FlagSecurityContact, "", "The validator's (optional) security contact email") - fsCreateValidator.String(FlagDetails, "", "The validator's (optional) details") - fsCreateValidator.String(FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)") - fsCreateValidator.AddFlagSet(FsCommissionCreate) - fsCreateValidator.AddFlagSet(FsMinSelfDelegation) - fsCreateValidator.AddFlagSet(FsAmount) - fsCreateValidator.AddFlagSet(FsPk) - - defaultsDesc = fmt.Sprintf(` - delegation amount: %s - commission rate: %s - commission max rate: %s - commission max change rate: %s - minimum self delegation: %s -`, defaultAmount, defaultCommissionRate, - defaultCommissionMaxRate, defaultCommissionMaxChangeRate, - defaultMinSelfDelegation) - - return fsCreateValidator, FlagNodeID, FlagPubKey, FlagAmount, defaultsDesc -} - -// prepare flags in config -func PrepareFlagsForTxCreateValidator( - config *cfg.Config, nodeID, chainID string, valPubKey crypto.PubKey, -) { - - ip := viper.GetString(FlagIP) - if ip == "" { - fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP; "+ - "the tx's memo field will be unset") - } - - website := viper.GetString(FlagWebsite) - securityContact := viper.GetString(FlagSecurityContact) - details := viper.GetString(FlagDetails) - identity := viper.GetString(FlagIdentity) - - viper.Set(flags.FlagChainID, chainID) - viper.Set(flags.FlagFrom, viper.GetString(flags.FlagName)) - viper.Set(FlagNodeID, nodeID) - viper.Set(FlagIP, ip) - viper.Set(FlagPubKey, sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey)) - viper.Set(FlagMoniker, config.Moniker) - viper.Set(FlagWebsite, website) - viper.Set(FlagSecurityContact, securityContact) - viper.Set(FlagDetails, details) - viper.Set(FlagIdentity, identity) - - if config.Moniker == "" { - viper.Set(FlagMoniker, viper.GetString(flags.FlagName)) - } - if viper.GetString(FlagAmount) == "" { - viper.Set(FlagAmount, defaultAmount) - } - if viper.GetString(FlagCommissionRate) == "" { - viper.Set(FlagCommissionRate, defaultCommissionRate) - } - if viper.GetString(FlagCommissionMaxRate) == "" { - viper.Set(FlagCommissionMaxRate, defaultCommissionMaxRate) - } - if viper.GetString(FlagCommissionMaxChangeRate) == "" { - viper.Set(FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate) - } - if viper.GetString(FlagMinSelfDelegation) == "" { - viper.Set(FlagMinSelfDelegation, defaultMinSelfDelegation) - } -} - // BuildCreateValidatorMsg makes a new MsgCreateValidator. func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr auth.TxBuilder) (auth.TxBuilder, sdk.Msg, error) { amounstStr := viper.GetString(FlagAmount) diff --git a/x/staking/client/rest/rest.go b/x/staking/client/rest/rest.go index 4237d59e25..b43cf152c6 100644 --- a/x/staking/client/rest/rest.go +++ b/x/staking/client/rest/rest.go @@ -4,8 +4,15 @@ import ( "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" ) +func RegisterHandlers(cliCtx context.CLIContext, m codec.Marshaler, txg tx.Generator, r *mux.Router) { + registerQueryRoutes(cliCtx, r) + registerTxHandlers(cliCtx, m, txg, r) +} + // RegisterRoutes registers staking-related REST handlers to a router func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { registerQueryRoutes(cliCtx, r) diff --git a/x/staking/client/rest/tx.go b/x/staking/client/rest/tx.go index 402d52a082..01328279ea 100644 --- a/x/staking/client/rest/tx.go +++ b/x/staking/client/rest/tx.go @@ -7,24 +7,26 @@ import ( "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { +func registerTxHandlers(cliCtx context.CLIContext, m codec.Marshaler, txg tx.Generator, r *mux.Router) { r.HandleFunc( "/staking/delegators/{delegatorAddr}/delegations", - postDelegationsHandlerFn(cliCtx), + newPostDelegationsHandlerFn(cliCtx, m, txg), ).Methods("POST") r.HandleFunc( "/staking/delegators/{delegatorAddr}/unbonding_delegations", - postUnbondingDelegationsHandlerFn(cliCtx), + newPostUnbondingDelegationsHandlerFn(cliCtx, m, txg), ).Methods("POST") r.HandleFunc( "/staking/delegators/{delegatorAddr}/redelegations", - postRedelegationsHandlerFn(cliCtx), + newPostRedelegationsHandlerFn(cliCtx, m, txg), ).Methods("POST") } @@ -55,6 +57,124 @@ type ( } ) +func newPostDelegationsHandlerFn(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 DelegateRequest + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + msg := types.NewMsgDelegate(req.DelegatorAddress, req.ValidatorAddress, req.Amount) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { + return + } + + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if rest.CheckBadRequestError(w, err) { + return + } + + if !bytes.Equal(fromAddr, req.DelegatorAddress) { + rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + return + } + + tx.WriteGeneratedTxResponse(cliCtx, w, txg, req.BaseReq, msg) + } +} + +func newPostRedelegationsHandlerFn(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 RedelegateRequest + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + msg := types.NewMsgBeginRedelegate(req.DelegatorAddress, req.ValidatorSrcAddress, req.ValidatorDstAddress, req.Amount) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { + return + } + + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if rest.CheckBadRequestError(w, err) { + return + } + + if !bytes.Equal(fromAddr, req.DelegatorAddress) { + rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + return + } + + tx.WriteGeneratedTxResponse(cliCtx, w, txg, req.BaseReq, msg) + } +} + +func newPostUnbondingDelegationsHandlerFn(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 UndelegateRequest + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + msg := types.NewMsgUndelegate(req.DelegatorAddress, req.ValidatorAddress, req.Amount) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { + return + } + + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if rest.CheckBadRequestError(w, err) { + return + } + + if !bytes.Equal(fromAddr, req.DelegatorAddress) { + rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + 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) { + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/delegations", + postDelegationsHandlerFn(cliCtx), + ).Methods("POST") + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/unbonding_delegations", + postUnbondingDelegationsHandlerFn(cliCtx), + ).Methods("POST") + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/redelegations", + postRedelegationsHandlerFn(cliCtx), + ).Methods("POST") +} + func postDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req DelegateRequest