Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

staking: create validator in one transaction #234

Merged
merged 1 commit into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion x/gov/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ metadata example:
return err
}

msgs, metadata, title, summary, deposit, err := parseSubmitProposal(clientCtx.Codec, args[0])
msgs, metadata, title, summary, deposit, err := ParseSubmitProposal(clientCtx.Codec, args[0])
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions x/gov/client/cli/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ type proposal struct {
Summary string `json:"summary"`
}

// parseSubmitProposal reads and parses the proposal.
func parseSubmitProposal(cdc codec.Codec, path string) ([]sdk.Msg, string, string, string, sdk.Coins, error) {
// ParseSubmitProposal reads and parses the proposal.
func ParseSubmitProposal(cdc codec.Codec, path string) ([]sdk.Msg, string, string, string, sdk.Coins, error) {
var proposal proposal

contents, err := os.ReadFile(path)
Expand Down
6 changes: 3 additions & 3 deletions x/gov/client/cli/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,15 @@ func TestParseSubmitProposal(t *testing.T) {
badJSON := testutil.WriteToNewTempFile(t, "bad json")

// nonexistent json
_, _, _, _, _, err := parseSubmitProposal(cdc, "fileDoesNotExist") //nolint: dogsled
_, _, _, _, _, err := ParseSubmitProposal(cdc, "fileDoesNotExist") //nolint: dogsled
require.Error(t, err)

// invalid json
_, _, _, _, _, err = parseSubmitProposal(cdc, badJSON.Name()) //nolint: dogsled
_, _, _, _, _, err = ParseSubmitProposal(cdc, badJSON.Name()) //nolint: dogsled
require.Error(t, err)

// ok json
msgs, metadata, title, summary, deposit, err := parseSubmitProposal(cdc, okJSON.Name())
msgs, metadata, title, summary, deposit, err := ParseSubmitProposal(cdc, okJSON.Name())
require.NoError(t, err, "unexpected error")
require.Equal(t, sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(1000))), deposit)
require.Equal(t, base64.StdEncoding.EncodeToString(expectedMetadata), metadata)
Expand Down
116 changes: 116 additions & 0 deletions x/staking/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/version"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/authz"
govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)

Expand All @@ -42,6 +47,7 @@ func NewTxCmd() *cobra.Command {
}

stakingTxCmd.AddCommand(
NewCreateValidatorCmd(),
NewEditValidatorCmd(),
NewDelegateCmd(),

Expand All @@ -52,6 +58,116 @@ func NewTxCmd() *cobra.Command {
return stakingTxCmd
}

// NewCreateValidatorCmd returns a CLI command handler for creating a MsgCreateValidator transaction.
func NewCreateValidatorCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create-validator [path/to/create_validator_proposal.json]",
Short: "submit a create new validator proposal",
Args: cobra.ExactArgs(1),
Long: `Submit a create new validator proposal by submitting a JSON file with the new validator details, once the proposal has been passed, create a new validator initialized with a self-delegation.`,
Example: strings.TrimSpace(
fmt.Sprintf(`
$ %s tx staking create-validator path/to/create_validator_proposal.json --from keyname

Where create_validator_proposal.json contains:

{
"messages": [
{
"@type": "/cosmos.staking.v1beta1.MsgCreateValidator",
"description": {
"moniker": "${NODE_NAME}",
"identity": "",
"website": "",
"security_contact": "",
"details": ""
},
"commission": {
"rate": "0.070000000000000000",
"max_rate": "1.000000000000000000",
"max_change_rate": "0.010000000000000000"
},
"min_self_delegation": "1000000000000000000000",
"delegator_address": "${VALIDATOR_ADDR}",
"validator_address": "${VALIDATOR_ADDR}",
"pubkey": {
"@type": "/cosmos.crypto.ed25519.PubKey",
"key": "${VALIDATOR_NODE_PUB_KEY}"
},
"value": {
"denom": "BNB",
"amount": "1000000000000000000000"
},
"from": "0x7b5Fe22B5446f7C62Ea27B8BD71CeF94e03f3dF2",
"relayer_address": "${RELAYER_ADDR}",
"challenger_address": "${CHALLENGER_ADDR}",
"bls_key": "${VALIDATOR_BLS}"
}
],
"metadata": "",
"title": "Create ${NODE_NAME} Validator",
"summary": "create ${NODE_NAME} validator",
"deposit": "1000000000000000000BNB"
}

modify the related configrations as you need, where you 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
}

msgs, metadata, title, summary, deposit, err := govcli.ParseSubmitProposal(clientCtx.Codec, args[0])
if err != nil {
return err
}

govMsg, err := v1.NewMsgSubmitProposal(msgs, deposit, clientCtx.GetFromAddress().String(), metadata, title, summary)
if err != nil {
return fmt.Errorf("invalid message: %w", err)
}

if len(msgs) != 1 {
return fmt.Errorf("invalid message length: %d", len(msgs))
}

valMsg, ok := msgs[0].(*types.MsgCreateValidator)
if !ok || valMsg.ValidateBasic() != nil {
return fmt.Errorf("invalid create validator message")
}

delAddr, err := sdk.AccAddressFromHexUnsafe(valMsg.DelegatorAddress)
if err != nil {
return err
}
if !delAddr.Equals(clientCtx.GetFromAddress()) {
return fmt.Errorf("the from address should be the self delegator address: %s", delAddr.String())
}

valAddr, err := sdk.AccAddressFromHexUnsafe(valMsg.ValidatorAddress)
if err != nil {
return err
}

grantee := authtypes.NewModuleAddress(govtypes.ModuleName)
authorization, err := types.NewStakeAuthorization([]sdk.AccAddress{valAddr}, nil, types.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &valMsg.Value)
authzMsg, err := authz.NewMsgGrant(clientCtx.GetFromAddress(), grantee, authorization, nil)
if err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), authzMsg, govMsg)
},
}

flags.AddTxFlagsToCmd(cmd)

_ = cmd.MarkFlagRequired(flags.FlagFrom)

return cmd
}

// NewEditValidatorCmd returns a CLI command handler for creating a MsgEditValidator transaction.
func NewEditValidatorCmd() *cobra.Command {
cmd := &cobra.Command{
Expand Down
7 changes: 2 additions & 5 deletions x/staking/types/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,14 @@ func (msg MsgCreateValidator) GetSignBytes() []byte {
// ValidateBasic implements the sdk.Msg interface.
func (msg MsgCreateValidator) ValidateBasic() error {
// note that unmarshaling from bech32 ensures both non-empty and valid
delAddr, err := sdk.AccAddressFromHexUnsafe(msg.DelegatorAddress)
_, err := sdk.AccAddressFromHexUnsafe(msg.DelegatorAddress)
if err != nil {
return sdkerrors.ErrInvalidAddress.Wrapf("invalid delegator address: %s", err)
}
valAddr, err := sdk.AccAddressFromHexUnsafe(msg.ValidatorAddress)
_, err = sdk.AccAddressFromHexUnsafe(msg.ValidatorAddress)
if err != nil {
return sdkerrors.ErrInvalidAddress.Wrapf("invalid validator address: %s", err)
}
if !valAddr.Equals(delAddr) {
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "validator address is invalid")
}

if msg.Pubkey == nil {
return ErrEmptyValidatorPubKey
Expand Down