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

cli(x/staking): add CLI and tests for rotateConsKey #18984

Closed
wants to merge 4 commits into from
Closed
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
101 changes: 100 additions & 1 deletion tests/e2e/staking/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ package testutil
import (
"context"
"errors"
"fmt"
"testing"

"github.com/cometbft/cometbft/proto/tendermint/crypto"
"github.com/cometbft/cometbft/rpc/client/http"
"github.com/stretchr/testify/suite"

"cosmossdk.io/math"
banktypes "cosmossdk.io/x/bank/types"
"cosmossdk.io/x/staking/client/cli"
stakingtypes "cosmossdk.io/x/staking/types"

"github.com/cometbft/cometbft/proto/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
Expand Down Expand Up @@ -121,3 +124,99 @@ func (s *E2ETestSuite) TestBlockResults() {
return nil
}, 10)
}

func (s *E2ETestSuite) TestConsKeyRotation() {
require := s.Require()
val := s.network.GetValidators()[0]
cmd := cli.NewRotateConsensusKeyCmd()

validPubkey := `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="}`
invalidPubkey := `{"@type":"/ed25519.PubKey","key":"wrong"}`

// Create new account in the keyring.
k, _, err := val.GetClientCtx().Keyring.NewMnemonic("NewDelegator", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
require.NoError(err)
pub, err := k.GetPubKey()
require.NoError(err)
newAddr := sdk.AccAddress(pub.Address())

msgSend := &banktypes.MsgSend{
FromAddress: val.GetAddress().String(),
ToAddress: newAddr.String(),
Amount: sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(200))),
}

// Send some funds to the new account.
_, err = clitestutil.SubmitTestTx(
val.GetClientCtx(),
msgSend,
val.GetAddress(),
clitestutil.TestTxConfig{},
)
require.NoError(err)
require.NoError(s.network.WaitForNextBlock())

testCases := []struct {
name string
args []string
expErrMsg string
code uint32
}{
{
"not a validator",
[]string{
fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr.String()),
fmt.Sprintf("--%s=%s", cli.FlagPubKey, validPubkey),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)).String()),
},
"",
3,
},
{
"valid pubkey",
[]string{
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.GetAddress()),
fmt.Sprintf("--%s=%s", cli.FlagPubKey, validPubkey),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)).String()),
},
"",
0,
},
{
"invalid pubkey",
[]string{
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.GetAddress()),
fmt.Sprintf("--%s=%s", cli.FlagPubKey, invalidPubkey),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)).String()),
},
"unable to resolve type URL",
0,
},
}

clientCtx := val.GetClientCtx()
for _, tc := range testCases {
tc := tc

s.Run(tc.name, func() {
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expErrMsg != "" {
require.Error(err)
require.Contains(err.Error(), tc.expErrMsg)
} else {
require.NoError(err, out.String())
resp := &sdk.TxResponse{}
require.NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), resp))

_, err := clitestutil.GetTxResponse(s.network, val.GetClientCtx(), resp.TxHash)
require.NoError(err)
}
})
}
}
47 changes: 47 additions & 0 deletions x/staking/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func NewTxCmd() *cobra.Command {
stakingTxCmd.AddCommand(
NewCreateValidatorCmd(),
NewEditValidatorCmd(),
NewRotateConsensusKeyCmd(),
)

return stakingTxCmd
Expand Down Expand Up @@ -174,6 +175,52 @@ func NewEditValidatorCmd() *cobra.Command {
return cmd
}

// // NewRotateConsensusKeyCmd returns a CLI command handler for creating a MsgRotateConsPubKey transaction.
// TODO: remove this once AutoCLI can flatten nested structs.
func NewRotateConsensusKeyCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "rotate-cons-pub-key",
Short: "rotate validator consensus pub key",
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

pkStr, err := cmd.Flags().GetString(FlagPubKey)
if err != nil {
return err
}

var pk cryptotypes.PubKey
if err := clientCtx.Codec.UnmarshalInterfaceJSON([]byte(pkStr), &pk); err != nil {
return err
}

valAddr, err := clientCtx.ValidatorAddressCodec.BytesToString(clientCtx.GetFromAddress())
if err != nil {
return err
}

msg, err := types.NewMsgRotateConsPubKey(valAddr, pk)
if err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

cmd.Flags().AddFlagSet(FlagSetPublicKey())

flags.AddTxFlagsToCmd(cmd)

_ = cmd.MarkFlagRequired(flags.FlagFrom)
_ = cmd.MarkFlagRequired(FlagPubKey)

return cmd
}

func newBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet, val validator, valAc address.Codec) (tx.Factory, *types.MsgCreateValidator, error) {
valAddr := clientCtx.GetFromAddress()

Expand Down
73 changes: 73 additions & 0 deletions x/staking/client/cli/tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,3 +437,76 @@ func (s *CLITestSuite) TestNewEditValidatorCmd() {
})
}
}

func (s *CLITestSuite) TestNewRotateConsensusKeyCmd() {
cmd := cli.NewRotateConsensusKeyCmd()

validPubkey := `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="}`
invalidPubkey := `{"@type":"/ed25519.PubKey","key":"wrong"}`

testCases := []struct {
name string
args []string
expErrMsg string
}{
{
"invalid tx no pubkey passed",
[]string{
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, sdkmath.NewInt(10))).String()),
},
`"pubkey" not set`,
},
{
"wrong pubkey set",
[]string{
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]),
fmt.Sprintf("--%s=%s", cli.FlagPubKey, invalidPubkey),
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, sdkmath.NewInt(10))).String()),
},
"unable to resolve type URL",
},
{
"wrong from address",
[]string{
fmt.Sprintf("--%s=%s", flags.FlagFrom, "wrongFrom"),
fmt.Sprintf("--%s=%s", cli.FlagPubKey, validPubkey),
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, sdkmath.NewInt(10))).String()),
},
"key not found",
},
{
"valid tx",
[]string{
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]),
fmt.Sprintf("--%s=%s", cli.FlagPubKey, validPubkey),
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, sdkmath.NewInt(10))).String()),
},
"",
},
}

for _, tc := range testCases {
tc := tc

s.Run(tc.name, func() {
out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args)
if tc.expErrMsg != "" {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expErrMsg)
} else {
s.Require().NoError(err, out.String())
resp := &sdk.TxResponse{}
s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), resp))
}
})
}
}
Loading