feat: create-validator now takes a json file as arg (#14864)

This commit is contained in:
Likhita Polavarapu
2023-02-22 05:43:13 +00:00
committed by GitHub
parent 77660ec452
commit 57653f8e60
7 changed files with 322 additions and 180 deletions
+2
View File
@@ -79,6 +79,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Improvements
* (x/staking) [#14864](https://github.com/cosmos/cosmos-sdk/pull/14864) `create-validator` CLI command now takes a json file as an arg instead of having a bunch of required flags to it.
* (cli) [#14659](https://github.com/cosmos/cosmos-sdk/pull/14659) Added ability to query blocks by either height/hash `simd q block --type=height|hash <height|hash>`.
* (store) [#14410](https://github.com/cosmos/cosmos-sdk/pull/14410) `rootmulti.Store.loadVersion` has validation to check if all the module stores' height is correct, it will error if any module store has incorrect height.
* (x/evidence) [#14757](https://github.com/cosmos/cosmos-sdk/pull/14757) Evidence messages do not need to implement a `.Type()` anymore.
@@ -263,6 +264,7 @@ extension interfaces. `module.Manager.Modules` is now of type `map[string]interf
### CLI Breaking Changes
* (x/staking) [#14864](https://github.com/cosmos/cosmos-sdk/pull/14864) `create-validator` CLI command now takes a json file as an arg instead of having a bunch of required flags to it.
* (cli) [#14659](https://github.com/cosmos/cosmos-sdk/pull/14659) `simd q block <height>` is removed as it just output json. The new command allows either height/hash and is `simd q block --type=height|hash <height|hash>`.
* (x/gov) [#14880](https://github.com/cosmos/cosmos-sdk/pull/14880) Remove `simd tx gov submit-legacy-proposal cancel-software-upgrade` and `software-upgrade` commands. These commands are now in the `x/upgrade` module and using gov v1. Use `tx upgrade software-upgrade` instead.
* (grpc-web) [#14652](https://github.com/cosmos/cosmos-sdk/pull/14652) Remove `grpc-web.address` flag.
+69 -44
View File
@@ -16,7 +16,6 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/testutil"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
@@ -97,11 +96,6 @@ func (s *E2ETestSuite) TestNewCreateValidatorCmd() {
require := s.Require()
val := s.network.Validators[0]
consPrivKey := ed25519.GenPrivKey()
consPubKeyBz, err := s.cfg.Codec.MarshalInterfaceJSON(consPrivKey.PubKey())
require.NoError(err)
require.NotNil(consPubKeyBz)
k, _, err := val.ClientCtx.Keyring.NewMnemonic("NewValidator", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
require.NoError(err)
@@ -120,6 +114,71 @@ func (s *E2ETestSuite) TestNewCreateValidatorCmd() {
require.NoError(err)
s.Require().NoError(s.network.WaitForNextBlock())
validJSON := fmt.Sprintf(`
{
"pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="},
"amount": "%dstake",
"moniker": "NewValidator",
"commission-rate": "0.5",
"commission-max-rate": "1.0",
"commission-max-change-rate": "0.1",
"min-self-delegation": "1"
}`, 100)
validJSONFile := testutil.WriteToNewTempFile(s.T(), validJSON)
defer func() {
if err := validJSONFile.Close(); err != nil {
val.Ctx.Logger.Info("Error closing file: %s\n", err)
}
}()
noAmountJSON := `
{
"pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="},
"moniker": "NewValidator",
"commission-rate": "0.5",
"commission-max-rate": "1.0",
"commission-max-change-rate": "0.1",
"min-self-delegation": "1"
}`
noAmountJSONFile := testutil.WriteToNewTempFile(s.T(), noAmountJSON)
defer func() {
if err := noAmountJSONFile.Close(); err != nil {
val.Ctx.Logger.Info("Error closing file: %s\n", err)
}
}()
noPubKeyJSON := fmt.Sprintf(`
{
"amount": "%dstake",
"moniker": "NewValidator",
"commission-rate": "0.5",
"commission-max-rate": "1.0",
"commission-max-change-rate": "0.1",
"min-self-delegation": "1"
}`, 100)
noPubKeyJSONFile := testutil.WriteToNewTempFile(s.T(), noPubKeyJSON)
defer func() {
if err := noPubKeyJSONFile.Close(); err != nil {
val.Ctx.Logger.Info("Error closing file: %s\n", err)
}
}()
noMonikerJSON := fmt.Sprintf(`
{
"pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="},
"amount": "%dstake",
"commission-rate": "0.5",
"commission-max-rate": "1.0",
"commission-max-change-rate": "0.1",
"min-self-delegation": "1"
}`, 100)
noMonikerJSONFile := testutil.WriteToNewTempFile(s.T(), noMonikerJSON)
defer func() {
if err := noMonikerJSONFile.Close(); err != nil {
val.Ctx.Logger.Info("Error closing file: %s\n", err)
}
}()
testCases := []struct {
name string
args []string
@@ -130,14 +189,7 @@ func (s *E2ETestSuite) TestNewCreateValidatorCmd() {
{
"invalid transaction (missing amount)",
[]string{
fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity),
fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite),
fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact),
fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails),
fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate),
fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate),
fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate),
fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation),
noAmountJSONFile.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
@@ -148,15 +200,7 @@ func (s *E2ETestSuite) TestNewCreateValidatorCmd() {
{
"invalid transaction (missing pubkey)",
[]string{
fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100),
fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity),
fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite),
fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact),
fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails),
fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate),
fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate),
fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate),
fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation),
noPubKeyJSONFile.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
@@ -167,16 +211,7 @@ func (s *E2ETestSuite) TestNewCreateValidatorCmd() {
{
"invalid transaction (missing moniker)",
[]string{
fmt.Sprintf("--%s=%s", cli.FlagPubKey, consPubKeyBz),
fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100),
fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity),
fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite),
fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact),
fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails),
fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate),
fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate),
fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate),
fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation),
noMonikerJSONFile.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
@@ -187,17 +222,7 @@ func (s *E2ETestSuite) TestNewCreateValidatorCmd() {
{
"valid transaction",
[]string{
fmt.Sprintf("--%s=%s", cli.FlagPubKey, consPubKeyBz),
fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100),
fmt.Sprintf("--%s=NewValidator", cli.FlagMoniker),
fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity),
fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite),
fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact),
fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails),
fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate),
fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate),
fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate),
fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation),
validJSONFile.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
+21 -11
View File
@@ -1565,29 +1565,39 @@ The command `create-validator` allows users to create new validator initialized
Usage:
```bash
simd tx staking create-validator [flags]
simd tx staking create-validator [path/to/validator.json] [flags]
```
Example:
```bash
simd tx staking create-validator \
--amount=1000000stake \
--pubkey=$(simd tendermint show-validator) \
--moniker="my-moniker" \
--website="https://myweb.site" \
--details="description of your validator" \
simd tx staking create-validator /path/to/validator.json \
--chain-id="name_of_chain_id" \
--commission-rate="0.10" \
--commission-max-rate="0.20" \
--commission-max-change-rate="0.01" \
--min-self-delegation="1" \
--gas="auto" \
--gas-adjustment="1.2" \
--gas-prices="0.025stake" \
--from=mykey
```
where `validator.json` contains:
```json
{
"pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"BnbwFpeONLqvWqJb3qaUbL5aoIcW3fSuAp9nT3z5f20="},
"amount": "1000000stake",
"moniker": "my-moniker",
"website": "https://myweb.site",
"security": "security-contact@gmail.com",
"details": "description of your validator",
"commission-rate": "0.10",
"commission-max-rate": "0.20",
"commission-max-change-rate": "0.01",
"min-self-delegation": "1"
}
```
and pubkey can be obtained by using `simd tendermint show-validator` command.
##### delegate
The command `delegate` allows users to delegate liquid tokens to a validator.
-12
View File
@@ -100,15 +100,3 @@ func flagSetCommissionUpdate() *flag.FlagSet {
return fs
}
func flagSetDescriptionCreate() *flag.FlagSet {
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.String(FlagMoniker, "", "The validator's name")
fs.String(FlagIdentity, "", "The optional identity signature (ex. UPort or Keybase)")
fs.String(FlagWebsite, "", "The validator's (optional) website")
fs.String(FlagSecurityContact, "", "The validator's (optional) security contact email")
fs.String(FlagDetails, "", "The validator's (optional) details")
return fs
}
+38 -56
View File
@@ -58,17 +58,46 @@ func NewTxCmd() *cobra.Command {
// NewCreateValidatorCmd returns a CLI command handler for creating a MsgCreateValidator transaction.
func NewCreateValidatorCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create-validator",
Use: "create-validator [path/to/validator.json]",
Short: "create new validator initialized with a self-delegation to it",
Args: cobra.ExactArgs(1),
Long: `Create a new validator initialized with a self-delegation by submitting a JSON file with the new validator details.`,
Example: strings.TrimSpace(
fmt.Sprintf(`
$ %s tx staking create-validator path/to/validator.json --from keyname
Where validator.json contains:
{
"pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="},
"amount": "1000000stake",
"moniker": "myvalidator",
"identity": "optional identity signature (ex. UPort or Keybase)",
"website": "validator's (optional) website",
"security": "validator's (optional) security contact email",
"details": "validator's (optional) details",
"commission-rate": "0.1",
"commission-max-rate": "0.2",
"commission-max-change-rate": "0.01",
"min-self-delegation": "1"
}
where we can get the pubkey using "%s tendermint show-validator"
`, version.AppName, version.AppName)),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
validator, err := parseAndValidateValidatorJSON(clientCtx.Codec, args[0])
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())
txf, msg, err := newBuildCreateValidatorMsg(clientCtx, txf, cmd.Flags(), validator)
if err != nil {
return err
}
@@ -77,20 +106,11 @@ func NewCreateValidatorCmd() *cobra.Command {
},
}
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
}
@@ -339,57 +359,19 @@ $ %s tx staking cancel-unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake
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
}
func newBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet, val validator) (tx.Factory, *types.MsgCreateValidator, error) {
valAddr := clientCtx.GetFromAddress()
pkStr, err := fs.GetString(FlagPubKey)
if err != nil {
return txf, nil, err
}
var pk cryptotypes.PubKey
if err := clientCtx.Codec.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,
val.Moniker,
val.Identity,
val.Website,
val.Security,
val.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, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "minimum self delegation must be a positive integer")
}
msg, err := types.NewMsgCreateValidator(
sdk.ValAddress(valAddr), pk, amount, description, commissionRates, minSelfDelegation,
sdk.ValAddress(valAddr), val.PubKey, val.Amount, description, val.CommissionRates, val.MinSelfDelegation,
)
if err != nil {
return txf, nil, err
+98 -57
View File
@@ -18,6 +18,7 @@ import (
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/testutil"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -163,112 +164,152 @@ func (s *CLITestSuite) TestNewCreateValidatorCmd() {
require := s.Require()
cmd := cli.NewCreateValidatorCmd()
consPrivKey := ed25519.GenPrivKey()
consPubKeyBz, err := s.encCfg.Codec.MarshalInterfaceJSON(consPrivKey.PubKey())
require.NoError(err)
require.NotNil(consPubKeyBz)
validJSON := fmt.Sprintf(`
{
"pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="},
"amount": "%dstake",
"moniker": "NewValidator",
"identity": "AFAF00C4",
"website": "https://newvalidator.io",
"security": "contact@newvalidator.io",
"details": "'Hey, I am a new validator. Please delegate!'",
"commission-rate": "0.5",
"commission-max-rate": "1.0",
"commission-max-change-rate": "0.1",
"min-self-delegation": "1"
}`, 100)
validJSONFile := testutil.WriteToNewTempFile(s.T(), validJSON)
defer validJSONFile.Close()
validJSONWithoutOptionalFields := fmt.Sprintf(`
{
"pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="},
"amount": "%dstake",
"moniker": "NewValidator",
"commission-rate": "0.5",
"commission-max-rate": "1.0",
"commission-max-change-rate": "0.1",
"min-self-delegation": "1"
}`, 100)
validJSONWOOptionalFile := testutil.WriteToNewTempFile(s.T(), validJSONWithoutOptionalFields)
defer validJSONWOOptionalFile.Close()
noAmountJSON := `
{
"pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="},
"moniker": "NewValidator",
"commission-rate": "0.5",
"commission-max-rate": "1.0",
"commission-max-change-rate": "0.1",
"min-self-delegation": "1"
}`
noAmountJSONFile := testutil.WriteToNewTempFile(s.T(), noAmountJSON)
defer noAmountJSONFile.Close()
noPubKeyJSON := fmt.Sprintf(`
{
"amount": "%dstake",
"moniker": "NewValidator",
"commission-rate": "0.5",
"commission-max-rate": "1.0",
"commission-max-change-rate": "0.1",
"min-self-delegation": "1"
}`, 100)
noPubKeyJSONFile := testutil.WriteToNewTempFile(s.T(), noPubKeyJSON)
defer noPubKeyJSONFile.Close()
noMonikerJSON := fmt.Sprintf(`
{
"pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="},
"amount": "%dstake",
"commission-rate": "0.5",
"commission-max-rate": "1.0",
"commission-max-change-rate": "0.1",
"min-self-delegation": "1"
}`, 100)
noMonikerJSONFile := testutil.WriteToNewTempFile(s.T(), noMonikerJSON)
defer noMonikerJSONFile.Close()
testCases := []struct {
name string
args []string
expectErr bool
expectedCode uint32
respType proto.Message
name string
args []string
expectErr bool
expErrMsg string
respType proto.Message
}{
{
"invalid transaction (missing amount)",
[]string{
fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity),
fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite),
fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact),
fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails),
fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate),
fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate),
fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate),
fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation),
noAmountJSONFile.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()),
},
true, 0, nil,
true,
"must specify amount of coins to bond",
nil,
},
{
"invalid transaction (missing pubkey)",
[]string{
fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100),
fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity),
fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite),
fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact),
fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails),
fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate),
fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate),
fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate),
fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation),
noPubKeyJSONFile.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()),
},
true, 0, nil,
true,
"must specify the JSON encoded pubkey",
nil,
},
{
"invalid transaction (missing moniker)",
[]string{
fmt.Sprintf("--%s=%s", cli.FlagPubKey, consPubKeyBz),
fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100),
fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity),
fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite),
fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact),
fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails),
fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate),
fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate),
fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate),
fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation),
noMonikerJSONFile.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()),
},
true, 0, nil,
true,
"must specify the moniker name",
nil,
},
{
"valid transaction",
"valid transaction with all fields",
[]string{
fmt.Sprintf("--%s=%s", cli.FlagPubKey, consPubKeyBz),
fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100),
fmt.Sprintf("--%s=NewValidator", cli.FlagMoniker),
fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity),
fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite),
fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact),
fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails),
fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate),
fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate),
fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate),
fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation),
validJSONFile.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()),
},
false, 0, &sdk.TxResponse{},
false, "", &sdk.TxResponse{},
},
{
"valid transaction without optional fields",
[]string{
validJSONWOOptionalFile.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()),
},
false, "", &sdk.TxResponse{},
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args)
if tc.expectErr {
require.Error(err)
require.Contains(err.Error(), tc.expErrMsg)
} else {
require.NoError(err, "test: %s\noutput: %s", tc.name, out.String())
err = s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType)
require.NoError(err, out.String(), "test: %s, output\n:", tc.name, out.String())
txResp := tc.respType.(*sdk.TxResponse)
require.Equal(tc.expectedCode, txResp.Code,
"test: %s, output\n:", tc.name, out.String())
}
})
}
+94
View File
@@ -1,12 +1,106 @@
package cli
import (
"encoding/json"
"errors"
"fmt"
"os"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
// validator struct to define the fields of the validator
type validator struct {
Amount sdk.Coin
PubKey cryptotypes.PubKey
Moniker string
Identity string
Website string
Security string
Details string
CommissionRates types.CommissionRates
MinSelfDelegation math.Int
}
func parseAndValidateValidatorJSON(cdc codec.Codec, path string) (validator, error) {
type internalVal struct {
Amount string `json:"amount"`
PubKey json.RawMessage `json:"pubkey"`
Moniker string `json:"moniker"`
Identity string `json:"identity,omitempty"`
Website string `json:"website,omitempty"`
Security string `json:"security,omitempty"`
Details string `json:"details,omitempty"`
CommissionRate string `json:"commission-rate"`
CommissionMaxRate string `json:"commission-max-rate"`
CommissionMaxChange string `json:"commission-max-change-rate"`
MinSelfDelegation string `json:"min-self-delegation"`
}
contents, err := os.ReadFile(path)
if err != nil {
return validator{}, err
}
var v internalVal
err = json.Unmarshal(contents, &v)
if err != nil {
return validator{}, err
}
if v.Amount == "" {
return validator{}, fmt.Errorf("must specify amount of coins to bond")
}
amount, err := sdk.ParseCoinNormalized(v.Amount)
if err != nil {
return validator{}, err
}
if v.PubKey == nil {
return validator{}, fmt.Errorf("must specify the JSON encoded pubkey")
}
var pk cryptotypes.PubKey
if err := cdc.UnmarshalInterfaceJSON(v.PubKey, &pk); err != nil {
return validator{}, err
}
if v.Moniker == "" {
return validator{}, fmt.Errorf("must specify the moniker name")
}
commissionRates, err := buildCommissionRates(v.CommissionRate, v.CommissionMaxRate, v.CommissionMaxChange)
if err != nil {
return validator{}, err
}
if v.MinSelfDelegation == "" {
return validator{}, fmt.Errorf("must specify minimum self delegation")
}
minSelfDelegation, ok := sdk.NewIntFromString(v.MinSelfDelegation)
if !ok {
return validator{}, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "minimum self delegation must be a positive integer")
}
return validator{
Amount: amount,
PubKey: pk,
Moniker: v.Moniker,
Identity: v.Identity,
Website: v.Website,
Security: v.Security,
Details: v.Details,
CommissionRates: commissionRates,
MinSelfDelegation: minSelfDelegation,
}, nil
}
func buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr string) (commission types.CommissionRates, err error) {
if rateStr == "" || maxRateStr == "" || maxChangeRateStr == "" {
return commission, errors.New("must specify all validator commission parameters")