feat!: key rename cli command (#9601)
<!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> ## Description this PR adds a new function to the keyring interface, as well as a CLI command to rename a key in the keyring ref: #9407 <!-- Add a description of the changes that this PR introduces and the files that are the most critical to review. --> --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [x] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [x] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] confirmed `!` in the type prefix if API or client breaking change - [x] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [x] reviewed API design and naming - [ ] reviewed documentation is accurate - [x] reviewed tests and test coverage - [x] manually tested (if applicable)
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/input"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// RenameKeyCommand renames a key from the key store.
|
||||
func RenameKeyCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "rename <old_name> <new_name>",
|
||||
Short: "Rename an existing key",
|
||||
Long: `Rename a key from the Keybase backend.
|
||||
|
||||
Note that renaming offline or ledger keys will rename
|
||||
only the public key references stored locally, i.e.
|
||||
private keys stored in a ledger device cannot be renamed with the CLI.
|
||||
`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
buf := bufio.NewReader(cmd.InOrStdin())
|
||||
clientCtx, err := client.GetClientQueryContext(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldName, newName := args[0], args[1]
|
||||
|
||||
info, err := clientCtx.Keyring.Key(oldName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// confirm rename, unless -y is passed
|
||||
if skip, _ := cmd.Flags().GetBool(flagYes); !skip {
|
||||
prompt := fmt.Sprintf("Key reference will be renamed from %s to %s. Continue?", args[0], args[1])
|
||||
if yes, err := input.GetConfirmation(prompt, buf, cmd.ErrOrStderr()); err != nil {
|
||||
return err
|
||||
} else if !yes {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := clientCtx.Keyring.Rename(oldName, newName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.GetType() == keyring.TypeLedger || info.GetType() == keyring.TypeOffline {
|
||||
cmd.PrintErrln("Public key reference renamed")
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.PrintErrln(fmt.Sprintf("Key was successfully renamed from %s to %s", oldName, newName))
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolP(flagYes, "y", false, "Skip confirmation prompt when renaming offline or ledger key references")
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"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/testutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func Test_runRenameCmd(t *testing.T) {
|
||||
// temp keybase
|
||||
kbHome := t.TempDir()
|
||||
cmd := RenameKeyCommand()
|
||||
cmd.Flags().AddFlagSet(Commands(kbHome).PersistentFlags())
|
||||
mockIn := testutil.ApplyMockIODiscardOutErr(cmd)
|
||||
|
||||
yesF, _ := cmd.Flags().GetBool(flagYes)
|
||||
require.False(t, yesF)
|
||||
|
||||
fakeKeyName1 := "runRenameCmd_Key1"
|
||||
fakeKeyName2 := "runRenameCmd_Key2"
|
||||
|
||||
path := sdk.GetConfig().GetFullBIP44Path()
|
||||
|
||||
kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn)
|
||||
require.NoError(t, err)
|
||||
|
||||
// put fakeKeyName1 in keyring
|
||||
_, err = kb.NewAccount(fakeKeyName1, testutil.TestMnemonic, "", path, hd.Secp256k1)
|
||||
require.NoError(t, err)
|
||||
|
||||
clientCtx := client.Context{}.
|
||||
WithKeyringDir(kbHome).
|
||||
WithKeyring(kb)
|
||||
|
||||
ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx)
|
||||
|
||||
// rename a key 'blah' which doesnt exist
|
||||
cmd.SetArgs([]string{"blah", "blaah", fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome)})
|
||||
err = cmd.ExecuteContext(ctx)
|
||||
require.Error(t, err)
|
||||
require.EqualError(t, err, "blah.info: key not found")
|
||||
|
||||
// User confirmation missing
|
||||
cmd.SetArgs([]string{
|
||||
fakeKeyName1,
|
||||
"nokey",
|
||||
fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
|
||||
})
|
||||
err = cmd.Execute()
|
||||
require.Error(t, err)
|
||||
require.Equal(t, "EOF", err.Error())
|
||||
|
||||
oldKey, err := kb.Key(fakeKeyName1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add a confirmation
|
||||
cmd.SetArgs([]string{
|
||||
fakeKeyName1,
|
||||
fakeKeyName2,
|
||||
fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
|
||||
fmt.Sprintf("--%s=true", flagYes),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
|
||||
})
|
||||
require.NoError(t, cmd.Execute())
|
||||
|
||||
// key1 is gone
|
||||
_, err = kb.Key(fakeKeyName1)
|
||||
require.Error(t, err)
|
||||
|
||||
// key2 exists now
|
||||
renamedKey, err := kb.Key(fakeKeyName2)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, oldKey.GetPubKey(), renamedKey.GetPubKey())
|
||||
require.Equal(t, oldKey.GetType(), renamedKey.GetType())
|
||||
require.Equal(t, oldKey.GetAddress(), renamedKey.GetAddress())
|
||||
require.Equal(t, oldKey.GetAlgo(), renamedKey.GetAlgo())
|
||||
|
||||
// try to rename key1 but it doesnt exist anymore so error
|
||||
cmd.SetArgs([]string{
|
||||
fakeKeyName1,
|
||||
fakeKeyName2,
|
||||
fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
|
||||
fmt.Sprintf("--%s=true", flagYes),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
|
||||
})
|
||||
require.Error(t, cmd.Execute())
|
||||
}
|
||||
@@ -45,6 +45,7 @@ The pass backend requires GnuPG: https://gnupg.org/
|
||||
ListKeysCmd(),
|
||||
ShowKeysCmd(),
|
||||
DeleteKeyCommand(),
|
||||
RenameKeyCommand(),
|
||||
ParseKeyStringCommand(),
|
||||
MigrateCommand(),
|
||||
)
|
||||
|
||||
@@ -11,5 +11,5 @@ func TestCommands(t *testing.T) {
|
||||
assert.NotNil(t, rootCommands)
|
||||
|
||||
// Commands are registered
|
||||
assert.Equal(t, 9, len(rootCommands.Commands()))
|
||||
assert.Equal(t, 10, len(rootCommands.Commands()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user