cosmos-sdk/x/staking/client/cli/tx.go
Robert Zaremba 7568b6680a
Remove bech32 PubKey support (#7477)
* Move PubKey bech32 to legacy package and migrate the usage where possible

* update /server

* wip

* move proto json encoding helper functions to internal

* update internal/marshal

* wip

* update sections which needs legacybech32

* update validators output

* fix conflicts

* slashing update

* add more tests and helper function for ANY JSON serialization

* update slashing

* Update function documentation

* Rename code any-marshal helper functions

* Update pubkey unpacking test

* Update test comments

* solve TestDecodeStore

* solve legacytx issues

* all code compiles

* keyring tests

* keyring cleanup

* remove AssertMsg

* fix some tests

* fix add_ledger_test.go

* update cli tests

* debug cli test

* rename clashed bech32 names

* linter fixes

* update tmservice tests

* linter: update legacy deprecated checks

* fix names

* linting

* legacybech32 pubkey type rename

* fix staking client

* fix test compilation

* fix TestGetCmdQuerySigningInfo

* rename NewIfcJSONAnyMarshaler

* keyring: remove duplicated information from multinfo structure

* todo cleanups

* Update Changelog

* remove some legacybech32 from tests

* remove todos

* remove printlnJSON from /server CLI and amino encoding

* remove protocdc.MarshalJSON

* client/show remove duplicated function

* remove protocdc package

* comment update

* remove legacybech32.MustMarshalPubKey from a test

* add todo

* fix TestPublicKeyUnsafe test

* review update

* fix bech32 UnmarshalPubKey

* Use codec.MarshalIfcJSON

* fix linter issues

* merging conflict: fix codec.Unmarshal calls

* cleanups

* Update CHANGELOG.md

Co-authored-by: Aaron Craelius <aaron@regen.network>

* Reword changelog updates

* use pubkey.String for comparison in Test_runAddCmdLedgerWithCustomCoinType

* Update GetCmdQuerySigningInfo example

* cli: update keys add docs

* Add errors AsOf and errors.ErrIO type

* restore multisigPubKeyInfo structure bring it back to multiInfo struct

* Update codec/proto_codec.go

Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>

* Update crypto/keys/ed25519/ed25519_test.go

Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>

* Update codec/proto_codec.go

Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>

* move pubkey any marshaling tests

* Apply suggestions from code review

Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>

* review updates

* adding missing return

* errors: use IsOf instead of AsOf

* keyring: add a correct check for key not found in keyring.Get

* add checkKeyNotFound

* fix linter issues

* fix: keyring key not found check

* fix keyring tests

* fix linting issues

* cli tests

* fix: 'simd keys show <key> -p'

* fix: TestVerifyMultisignature

* rename keyring Bech32... functions to Mk...

* fix RunAddCmd

* Update pubkey display

* wip

* add more tests

* udate keyring output tests

* remove todo from ledger tests

* rename MkKeyOutput

* Changelog update

* solve liner issues

* add link to github issue

Co-authored-by: Aaron Craelius <aaron@regen.network>
Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>
2021-03-25 14:53:22 +00:00

580 lines
16 KiB
Go

package cli
import (
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/msgservice"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
// default values
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() *cobra.Command {
stakingTxCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Staking transaction subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
stakingTxCmd.AddCommand(
NewCreateValidatorCmd(),
NewEditValidatorCmd(),
NewDelegateCmd(),
NewRedelegateCmd(),
NewUnbondCmd(),
)
return stakingTxCmd
}
func NewCreateValidatorCmd() *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 {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).
WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever)
txf, msg, err := newBuildCreateValidatorMsg(clientCtx, txf, cmd.Flags())
if err != nil {
return err
}
svcMsgClientConn := &msgservice.ServiceMsgClientConn{}
msgClient := types.NewMsgClient(svcMsgClientConn)
_, err = msgClient.CreateValidator(cmd.Context(), msg)
if err != nil {
return err
}
return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, svcMsgClientConn.GetMsgs()...)
},
}
cmd.Flags().AddFlagSet(FlagSetPublicKey())
cmd.Flags().AddFlagSet(FlagSetAmount())
cmd.Flags().AddFlagSet(flagSetDescriptionCreate())
cmd.Flags().AddFlagSet(FlagSetCommissionCreate())
cmd.Flags().AddFlagSet(FlagSetMinSelfDelegation())
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")
flags.AddTxFlagsToCmd(cmd)
_ = cmd.MarkFlagRequired(flags.FlagFrom)
_ = cmd.MarkFlagRequired(FlagAmount)
_ = cmd.MarkFlagRequired(FlagPubKey)
_ = cmd.MarkFlagRequired(FlagMoniker)
return cmd
}
func NewEditValidatorCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "edit-validator",
Short: "edit an existing validator account",
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
valAddr := clientCtx.GetFromAddress()
moniker, _ := cmd.Flags().GetString(FlagMoniker)
identity, _ := cmd.Flags().GetString(FlagIdentity)
website, _ := cmd.Flags().GetString(FlagWebsite)
security, _ := cmd.Flags().GetString(FlagSecurityContact)
details, _ := cmd.Flags().GetString(FlagDetails)
description := types.NewDescription(moniker, identity, website, security, details)
var newRate *sdk.Dec
commissionRate, _ := cmd.Flags().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, _ := cmd.Flags().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)
svcMsgClientConn := &msgservice.ServiceMsgClientConn{}
msgClient := types.NewMsgClient(svcMsgClientConn)
_, err = msgClient.EditValidator(cmd.Context(), msg)
if err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...)
},
}
cmd.Flags().AddFlagSet(flagSetDescriptionEdit())
cmd.Flags().AddFlagSet(flagSetCommissionUpdate())
cmd.Flags().AddFlagSet(FlagSetMinSelfDelegation())
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func NewDelegateCmd() *cobra.Command {
bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix()
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 %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 1000stake --from mykey
`,
version.AppName, bech32PrefixValAddr,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
amount, err := sdk.ParseCoinNormalized(args[1])
if err != nil {
return err
}
delAddr := clientCtx.GetFromAddress()
valAddr, err := sdk.ValAddressFromBech32(args[0])
if err != nil {
return err
}
msg := types.NewMsgDelegate(delAddr, valAddr, amount)
svcMsgClientConn := &msgservice.ServiceMsgClientConn{}
msgClient := types.NewMsgClient(svcMsgClientConn)
_, err = msgClient.Delegate(cmd.Context(), msg)
if err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func NewRedelegateCmd() *cobra.Command {
bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix()
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 %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 100stake --from mykey
`,
version.AppName, bech32PrefixValAddr, bech32PrefixValAddr,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
delAddr := clientCtx.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.ParseCoinNormalized(args[2])
if err != nil {
return err
}
msg := types.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount)
svcMsgClientConn := &msgservice.ServiceMsgClientConn{}
msgClient := types.NewMsgClient(svcMsgClientConn)
_, err = msgClient.BeginRedelegate(cmd.Context(), msg)
if err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func NewUnbondCmd() *cobra.Command {
bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix()
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 %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from mykey
`,
version.AppName, bech32PrefixValAddr,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
delAddr := clientCtx.GetFromAddress()
valAddr, err := sdk.ValAddressFromBech32(args[0])
if err != nil {
return err
}
amount, err := sdk.ParseCoinNormalized(args[1])
if err != nil {
return err
}
msg := types.NewMsgUndelegate(delAddr, valAddr, amount)
svcMsgClientConn := &msgservice.ServiceMsgClientConn{}
msgClient := types.NewMsgClient(svcMsgClientConn)
_, err = msgClient.Undelegate(cmd.Context(), msg)
if err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func newBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet) (tx.Factory, *types.MsgCreateValidator, error) {
fAmount, _ := fs.GetString(FlagAmount)
amount, err := sdk.ParseCoinNormalized(fAmount)
if err != nil {
return txf, nil, err
}
valAddr := clientCtx.GetFromAddress()
pkStr, err := fs.GetString(FlagPubKey)
if err != nil {
return txf, nil, err
}
var pk cryptotypes.PubKey
if err := clientCtx.JSONMarshaler.UnmarshalInterfaceJSON([]byte(pkStr), &pk); err != nil {
return txf, nil, err
}
moniker, _ := fs.GetString(FlagMoniker)
identity, _ := fs.GetString(FlagIdentity)
website, _ := fs.GetString(FlagWebsite)
security, _ := fs.GetString(FlagSecurityContact)
details, _ := fs.GetString(FlagDetails)
description := types.NewDescription(
moniker,
identity,
website,
security,
details,
)
// get the initial validator commission parameters
rateStr, _ := fs.GetString(FlagCommissionRate)
maxRateStr, _ := fs.GetString(FlagCommissionMaxRate)
maxChangeRateStr, _ := fs.GetString(FlagCommissionMaxChangeRate)
commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr)
if err != nil {
return txf, nil, err
}
// get the initial validator min self delegation
msbStr, _ := fs.GetString(FlagMinSelfDelegation)
minSelfDelegation, ok := sdk.NewIntFromString(msbStr)
if !ok {
return txf, nil, types.ErrMinSelfDelegationInvalid
}
msg, err := types.NewMsgCreateValidator(
sdk.ValAddress(valAddr), pk, amount, description, commissionRates, minSelfDelegation,
)
if err != nil {
return txf, nil, err
}
if err := msg.ValidateBasic(); err != nil {
return txf, nil, err
}
genOnly, _ := fs.GetBool(flags.FlagGenerateOnly)
if genOnly {
ip, _ := fs.GetString(FlagIP)
nodeID, _ := fs.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 CreateValidatorMsgFlagSet(ipDefault string) (fs *flag.FlagSet, 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(FlagMoniker, "", "The validator's (optional) moniker")
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(FlagSetCommissionCreate())
fsCreateValidator.AddFlagSet(FlagSetMinSelfDelegation())
fsCreateValidator.AddFlagSet(FlagSetAmount())
fsCreateValidator.AddFlagSet(FlagSetPublicKey())
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, defaultsDesc
}
type TxCreateValidatorConfig struct {
ChainID string
NodeID string
Moniker string
Amount string
CommissionRate string
CommissionMaxRate string
CommissionMaxChangeRate string
MinSelfDelegation string
PubKey cryptotypes.PubKey
IP string
Website string
SecurityContact string
Details string
Identity string
}
func PrepareConfigForTxCreateValidator(flagSet *flag.FlagSet, moniker, nodeID, chainID string, valPubKey cryptotypes.PubKey) (TxCreateValidatorConfig, error) {
c := TxCreateValidatorConfig{}
ip, err := flagSet.GetString(FlagIP)
if err != nil {
return c, err
}
if ip == "" {
_, _ = fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP; "+
"the tx's memo field will be unset")
}
c.IP = ip
website, err := flagSet.GetString(FlagWebsite)
if err != nil {
return c, err
}
c.Website = website
securityContact, err := flagSet.GetString(FlagSecurityContact)
if err != nil {
return c, err
}
c.SecurityContact = securityContact
details, err := flagSet.GetString(FlagDetails)
if err != nil {
return c, err
}
c.SecurityContact = details
identity, err := flagSet.GetString(FlagIdentity)
if err != nil {
return c, err
}
c.Identity = identity
c.Amount, err = flagSet.GetString(FlagAmount)
if err != nil {
return c, err
}
c.CommissionRate, err = flagSet.GetString(FlagCommissionRate)
if err != nil {
return c, err
}
c.CommissionMaxRate, err = flagSet.GetString(FlagCommissionMaxRate)
if err != nil {
return c, err
}
c.CommissionMaxChangeRate, err = flagSet.GetString(FlagCommissionMaxChangeRate)
if err != nil {
return c, err
}
c.MinSelfDelegation, err = flagSet.GetString(FlagMinSelfDelegation)
if err != nil {
return c, err
}
c.NodeID = nodeID
c.PubKey = valPubKey
c.Website = website
c.SecurityContact = securityContact
c.Details = details
c.Identity = identity
c.ChainID = chainID
c.Moniker = moniker
if c.Amount == "" {
c.Amount = defaultAmount
}
if c.CommissionRate == "" {
c.CommissionRate = defaultCommissionRate
}
if c.CommissionMaxRate == "" {
c.CommissionMaxRate = defaultCommissionMaxRate
}
if c.CommissionMaxChangeRate == "" {
c.CommissionMaxChangeRate = defaultCommissionMaxChangeRate
}
if c.MinSelfDelegation == "" {
c.MinSelfDelegation = defaultMinSelfDelegation
}
return c, nil
}
// BuildCreateValidatorMsg makes a new MsgCreateValidator.
func BuildCreateValidatorMsg(clientCtx client.Context, config TxCreateValidatorConfig, txBldr tx.Factory, generateOnly bool) (tx.Factory, sdk.Msg, error) {
amounstStr := config.Amount
amount, err := sdk.ParseCoinNormalized(amounstStr)
if err != nil {
return txBldr, nil, err
}
valAddr := clientCtx.GetFromAddress()
description := types.NewDescription(
config.Moniker,
config.Identity,
config.Website,
config.SecurityContact,
config.Details,
)
// get the initial validator commission parameters
rateStr := config.CommissionRate
maxRateStr := config.CommissionMaxRate
maxChangeRateStr := config.CommissionMaxChangeRate
commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr)
if err != nil {
return txBldr, nil, err
}
// get the initial validator min self delegation
msbStr := config.MinSelfDelegation
minSelfDelegation, ok := sdk.NewIntFromString(msbStr)
if !ok {
return txBldr, nil, types.ErrMinSelfDelegationInvalid
}
msg, err := types.NewMsgCreateValidator(
sdk.ValAddress(valAddr), config.PubKey, amount, description, commissionRates, minSelfDelegation,
)
if err != nil {
return txBldr, msg, err
}
if generateOnly {
ip := config.IP
nodeID := config.NodeID
if nodeID != "" && ip != "" {
txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip))
}
}
return txBldr, msg, nil
}