cosmos-sdk/x/distribution/client/cli/tx.go
Alessio Treglia b647824716
Refactor x/auth/client/utils/ (#5555)
Packages named utils, common, or misc provide clients with no
sense of what the package contains. This makes it harder for
clients to use the package and makes it harder for maintainers
to keep the package focused. Over time, they accumulate dependencies
that can make compilation significantly and unnecessarily slower,
especially in large programs. And since such package names are
generic, they are more likely to collide with other packages
imported by client code, forcing clients to invent names to
distinguish them.

 cit. https://blog.golang.org/package-names
2020-01-24 16:40:56 +00:00

301 lines
8.8 KiB
Go

// nolint
package cli
import (
"bufio"
"fmt"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"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/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/auth"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/distribution/client/common"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
var (
flagOnlyFromValidator = "only-from-validator"
flagIsValidator = "is-validator"
flagCommission = "commission"
flagMaxMessagesPerTx = "max-msgs"
)
const (
MaxMessagesPerTxDefault = 5
)
// GetTxCmd returns the transaction commands for this module
func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command {
distTxCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Distribution transactions subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
distTxCmd.AddCommand(flags.PostCommands(
GetCmdWithdrawRewards(cdc),
GetCmdSetWithdrawAddr(cdc),
GetCmdWithdrawAllRewards(cdc, storeKey),
GetCmdFundCommunityPool(cdc),
)...)
return distTxCmd
}
type generateOrBroadcastFunc func(context.CLIContext, auth.TxBuilder, []sdk.Msg) error
func splitAndApply(
generateOrBroadcast generateOrBroadcastFunc,
cliCtx context.CLIContext,
txBldr auth.TxBuilder,
msgs []sdk.Msg,
chunkSize int,
) error {
if chunkSize == 0 {
return generateOrBroadcast(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 := generateOrBroadcast(cliCtx, txBldr, msgChunk); err != nil {
return err
}
}
return nil
}
// command to withdraw rewards
func GetCmdWithdrawRewards(cdc *codec.Codec) *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())
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc))
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
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))
}
return authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, msgs)
},
}
cmd.Flags().Bool(flagCommission, false, "also withdraw validator's commission")
return cmd
}
// command to withdraw all rewards
func GetCmdWithdrawAllRewards(cdc *codec.Codec, queryRoute string) *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())
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc))
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
delAddr := cliCtx.GetFromAddress()
// The transaction cannot be generated offline since it requires a query
// to get all the validators.
if cliCtx.GenerateOnly {
return fmt.Errorf("command disabled with the provided flag: %s", flags.FlagGenerateOnly)
}
msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, queryRoute, delAddr)
if err != nil {
return err
}
chunkSize := viper.GetInt(flagMaxMessagesPerTx)
return splitAndApply(authclient.GenerateOrBroadcastMsgs, cliCtx, txBldr, msgs, chunkSize)
},
}
cmd.Flags().Int(flagMaxMessagesPerTx, MaxMessagesPerTxDefault, "Limit the number of messages per tx (0 for unlimited)")
return cmd
}
// command to replace a delegator's withdrawal address
func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command {
return &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())
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc))
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
delAddr := cliCtx.GetFromAddress()
withdrawAddr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr)
return authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
}
// GetCmdSubmitProposal implements the command to submit a community-pool-spend proposal
func GetCmdSubmitProposal(cdc *codec.Codec) *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 <path/to/proposal.json> --from=<key_or_address>
Where proposal.json contains:
{
"title": "Community Pool Spend",
"description": "Pay me some Atoms!",
"recipient": "cosmos1s5afhd6gxevu37mkqcvvsj8qeylhn0rz46zdlq",
"amount": [
{
"denom": "stake",
"amount": "10000"
}
],
"deposit": [
{
"denom": "stake",
"amount": "10000"
}
]
}
`,
version.ClientName,
),
),
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)
proposal, err := ParseCommunityPoolSpendProposalJSON(cdc, args[0])
if err != nil {
return err
}
from := cliCtx.GetFromAddress()
content := types.NewCommunityPoolSpendProposal(proposal.Title, proposal.Description, proposal.Recipient, proposal.Amount)
msg := gov.NewMsgSubmitProposal(content, proposal.Deposit, from)
if err := msg.ValidateBasic(); err != nil {
return err
}
return authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
return cmd
}
// GetCmdFundCommunityPool returns a command implementation that supports directly
// funding the community pool.
func GetCmdFundCommunityPool(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "fund-community-pool [amount]",
Args: cobra.ExactArgs(1),
Short: "Funds the community pool with the specified amount",
Long: strings.TrimSpace(
fmt.Sprintf(`Funds the community pool with the specified amount
Example:
$ %s tx distribution fund-community-pool 100uatom --from mykey
`,
version.ClientName,
),
),
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)
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 authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
}