feat: create-validator now takes a json file as arg (#14864)
This commit is contained in:
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user