Skip to content
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Features

* (keyring) [#17424](https://github.com/cosmos/cosmos-sdk/pull/17424) Allows to import private keys encoded in hex.
* (x/bank) [#16795](https://github.com/cosmos/cosmos-sdk/pull/16852) Add `DenomMetadataByQueryString` query in bank module to support metadata query by query string.
* (baseapp) [#16239](https://github.com/cosmos/cosmos-sdk/pull/16239) Add Gas Limits to allow node operators to resource bound queries.
* (baseapp) [#17393](https://github.com/cosmos/cosmos-sdk/pull/17394) Check BlockID Flag on Votes in `ValidateVoteExtensions`
Expand Down
23 changes: 23 additions & 0 deletions client/keys/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ package keys

import (
"bufio"
"fmt"
"os"

"github.com/spf13/cobra"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/input"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/version"
)

// ImportKeyCommand imports private keys from a keyfile.
Expand Down Expand Up @@ -38,3 +42,22 @@ func ImportKeyCommand() *cobra.Command {
},
}
}

func ImportKeyHexCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "import-hex <name> <hex>",
Short: "Import private keys into the local keybase",
Long: fmt.Sprintf("Import hex encoded private key into the local keybase.\nSupported key-types can be obtained with:\n%s list-key-types", version.AppName),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
keyType, _ := cmd.Flags().GetString(flags.FlagKeyType)
return clientCtx.Keyring.ImportPrivKeyHex(args[0], args[1], keyType)
},
}
cmd.Flags().String(flags.FlagKeyType, string(hd.Secp256k1Type), "private key signing algorithm kind")
return cmd
}
57 changes: 57 additions & 0 deletions client/keys/import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,60 @@ HbP+c6JmeJy9JXe2rbbF1QtCX1gLqGcDQPBXiCtFvP7/8wTZtVOPj8vREzhZ9ElO
})
}
}

func Test_runImportHexCmd(t *testing.T) {
cdc := moduletestutil.MakeTestEncodingConfig().Codec
testCases := []struct {
name string
keyringBackend string
hexKey string
keyType string
expectError bool
}{
{
name: "test backend success",
keyringBackend: keyring.BackendTest,
hexKey: "0xa3e57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf",
keyType: "secp256k1",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cmd := ImportKeyHexCommand()
cmd.Flags().AddFlagSet(Commands().PersistentFlags())
mockIn := testutil.ApplyMockIODiscardOutErr(cmd)

// Now add a temporary keybase
kbHome := t.TempDir()
kb, err := keyring.New(sdk.KeyringServiceName(), tc.keyringBackend, kbHome, nil, cdc)
require.NoError(t, err)

clientCtx := client.Context{}.
WithKeyringDir(kbHome).
WithKeyring(kb).
WithInput(mockIn).
WithCodec(cdc)
ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx)

t.Cleanup(cleanupKeys(t, kb, "keyname1"))

defer func() {
_ = os.RemoveAll(kbHome)
}()

cmd.SetArgs([]string{
"keyname1", tc.hexKey,
fmt.Sprintf("--%s=%s", flags.FlagKeyType, tc.keyType),
fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, tc.keyringBackend),
})

err = cmd.ExecuteContext(ctx)
if tc.expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
1 change: 1 addition & 0 deletions client/keys/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ The pass backend requires GnuPG: https://gnupg.org/
AddKeyCommand(),
ExportKeyCommand(),
ImportKeyCommand(),
ImportKeyHexCommand(),
ListKeysCmd(),
ListKeyTypesCmd(),
ShowKeysCmd(),
Expand Down
2 changes: 1 addition & 1 deletion client/keys/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ func TestCommands(t *testing.T) {
assert.Assert(t, rootCommands != nil)

// Commands are registered
assert.Equal(t, 11, len(rootCommands.Commands()))
assert.Equal(t, 12, len(rootCommands.Commands()))
}
28 changes: 27 additions & 1 deletion crypto/keyring/keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const (

// temporary pass phrase for exporting a key during a key rename
passPhrase = "temp"
// prefix for exported hex private keys
hexPrefix = "0x"
)

var (
Expand Down Expand Up @@ -118,7 +120,8 @@ type Signer interface {
type Importer interface {
// ImportPrivKey imports ASCII armored passphrase-encrypted private keys.
ImportPrivKey(uid, armor, passphrase string) error

// ImportPrivKeyHex imports hex encoded keys.
ImportPrivKeyHex(uid, privKey, algoStr string) error
// ImportPubKey imports ASCII armored public keys.
ImportPubKey(uid, armor string) error
}
Expand Down Expand Up @@ -338,6 +341,29 @@ func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error {
return nil
}

func (ks keystore) ImportPrivKeyHex(uid, privKey, algoStr string) error {
if _, err := ks.Key(uid); err == nil {
return errorsmod.Wrap(ErrOverwriteKey, uid)
}
if privKey[:2] == hexPrefix {
privKey = privKey[2:]
}
decodedPriv, err := hex.DecodeString(privKey)
if err != nil {
return err
}
algo, err := NewSigningAlgoFromString(algoStr, ks.options.SupportedAlgos)
if err != nil {
return err
}
priv := algo.Generate()(decodedPriv)
_, err = ks.writeLocalKey(uid, priv)
if err != nil {
return err
}
return nil
}

func (ks keystore) ImportPubKey(uid, armor string) error {
if _, err := ks.Key(uid); err == nil {
return errorsmod.Wrap(ErrOverwriteKey, uid)
Expand Down
60 changes: 59 additions & 1 deletion crypto/keyring/keyring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,65 @@ func TestImportPrivKey(t *testing.T) {
}
}

func TestExportImportPrivKey(t *testing.T) {
func TestImportPrivKeyHex(t *testing.T) {
cdc := getCodec()
tests := []struct {
name string
uid string
backend string
hexKey string
algo string
expectedErr error
}{
{
name: "correct import",
uid: "hexImport",
backend: BackendTest,
hexKey: "0xa3e57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf",
algo: "secp256k1",
expectedErr: nil,
},
{
name: "correct import without prefix",
uid: "hexImport",
backend: BackendTest,
hexKey: "a3e57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf",
algo: "secp256k1",
expectedErr: nil,
},
{
name: "wrong hex length",
uid: "hexImport",
backend: BackendTest,
hexKey: "0xae57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf",
algo: "secp256k1",
expectedErr: hex.ErrLength,
},
{
name: "unsupported algo",
uid: "hexImport",
backend: BackendTest,
hexKey: "0xa3e57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf",
algo: "notSupportedAlgo",
expectedErr: ErrUnsupportedSigningAlgo,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
kb, err := New("TestExport", tt.backend, t.TempDir(), nil, cdc)
require.NoError(t, err)
err = kb.ImportPrivKeyHex(tt.uid, tt.hexKey, tt.algo)
if tt.expectedErr == nil {
require.NoError(t, err)
} else {
require.Error(t, err)
require.True(t, errors.Is(err, tt.expectedErr))
}
})
}
}

func TestExportImportPrivKeyArmor(t *testing.T) {
cdc := getCodec()
tests := []struct {
name string
Expand Down