crypto/keyring: deprecate old keybase (#5889)

Remove the Update method from the Keybase interface.

Remove redundant lazy keybase implementation altogether.

Created LegacyKeybase interface to restrict capabilities to
only those required by keys commands that deal with
legacy keybase such as update and migrate.

Rename keyring.New() -> keyring.NewLegacy().

Rename client/keys.NewKeyBaseFromDir -> NewLegacyKeyBaseFromDir.

crypto/keyiring.NewInMemory() now returns a in-memory keyring.
BackendMemory is added yet not exposed via command line
--keyring-backend flag. keys add uses it when --dry-run flag
is on.
This commit is contained in:
Alessio Treglia 2020-03-30 20:30:50 +02:00 committed by GitHub
parent db76afe840
commit 2a7a408d35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 975 additions and 1908 deletions

View File

@ -51,6 +51,7 @@ that parse log messages.
* (client) [\#5799](https://github.com/cosmos/cosmos-sdk/pull/5799) The `tx encode/decode` commands, due to change on encoding break compatibility with
older clients.
* (x/auth) [\#5844](https://github.com/cosmos/cosmos-sdk/pull/5844) `tx sign` command now returns an error when signing is attempted with offline/multisig keys.
* (client/keys) [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) Remove `keys update` command.
### API Breaking Changes
@ -77,6 +78,11 @@ to now accept a `codec.JSONMarshaler` for modular serialization of genesis state
* (crypto/keyring) [\#5866](https://github.com/cosmos/cosmos-sdk/pull/5866) Move `Keyring` and `Keybase` implementations and their associated types from `crypto/keys/` to `crypto/keyring/`.
* (crypto) [\#5880](https://github.com/cosmos/cosmos-sdk/pull/5880) Merge `crypto/keys/mintkey` into `crypto`.
* (crypto/keyring) [\#5858](https://github.com/cosmos/cosmos-sdk/pull/5858) Make Keyring store keys by name and address's hexbytes representation.
* (crypto/keyring) [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) Deprecate old keybase implementation:
- Remove `Update` from the `Keybase` interface.
- `NewKeyring()` now accepts a new backend: `MemoryBackend`.
- `New()` has been renamed to`NewLegacy()`, which now returns a `LegacyKeybase` type that only allows migration of keys from the legacy keybase to a new keyring.
* (client/keys) [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) Rename `NewKeyBaseFromDir()` -> `NewLegacyKeyBaseFromDir()`.
### Features

View File

@ -27,7 +27,6 @@ const (
flagInteractive = "interactive"
flagRecover = "recover"
flagNoBackup = "no-backup"
flagDryRun = "dry-run"
flagAccount = "account"
flagIndex = "index"
flagMultisig = "multisig"
@ -72,7 +71,7 @@ the flag --nosort is set.
cmd.Flags().Bool(flags.FlagUseLedger, false, "Store a local reference to a private key on a Ledger device")
cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating")
cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)")
cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore")
cmd.Flags().Bool(flags.FlagDryRun, false, "Perform action, but don't add key to local keystore")
cmd.Flags().String(flagHDPath, "", "Manual HD Path derivation (overrides BIP44 config)")
cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation")
cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation")
@ -83,7 +82,7 @@ the flag --nosort is set.
func getKeybase(transient bool, buf io.Reader) (keyring.Keybase, error) {
if transient {
return keyring.NewInMemory(), nil
return keyring.NewKeyring(sdk.KeyringServiceName(), keyring.BackendMemory, viper.GetString(flags.FlagHome), buf)
}
return keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf)
@ -91,7 +90,7 @@ func getKeybase(transient bool, buf io.Reader) (keyring.Keybase, error) {
func runAddCmd(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
kb, err := getKeybase(viper.GetBool(flagDryRun), inBuf)
kb, err := getKeybase(viper.GetBool(flags.FlagDryRun), inBuf)
if err != nil {
return err
}
@ -124,7 +123,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
return keyring.ErrUnsupportedSigningAlgo
}
if !viper.GetBool(flagDryRun) {
if !viper.GetBool(flags.FlagDryRun) {
_, err = kb.Get(name)
if err == nil {
// account exists, ask for user confirmation

View File

@ -17,7 +17,6 @@ import (
)
func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
runningUnattended := isRunningUnattended()
config := sdk.GetConfig()
bech32PrefixAccAddr := "terra"
@ -58,9 +57,6 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
kb.Delete("keyname1", "", false)
})
mockIn.Reset("test1234\n")
if runningUnattended {
mockIn.Reset("test1234\ntest1234\n")
}
key1, err := kb.Get("keyname1")
require.NoError(t, err)
require.NotNil(t, key1)
@ -79,7 +75,6 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
}
func Test_runAddCmdLedger(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := AddKeyCommand()
require.NotNil(t, cmd)
mockIn, _, _ := tests.ApplyMockIO(cmd)
@ -105,9 +100,6 @@ func Test_runAddCmdLedger(t *testing.T) {
kb.Delete("keyname1", "", false)
})
mockIn.Reset("test1234\n")
if runningUnattended {
mockIn.Reset("test1234\ntest1234\n")
}
key1, err := kb.Get("keyname1")
require.NoError(t, err)
require.NotNil(t, key1)

View File

@ -16,7 +16,6 @@ import (
)
func Test_runAddCmdBasic(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := AddKeyCommand()
assert.NotNil(t, cmd)
mockIn, _, _ := tests.ApplyMockIO(cmd)
@ -27,31 +26,27 @@ func Test_runAddCmdBasic(t *testing.T) {
viper.Set(flags.FlagHome, kbHome)
viper.Set(cli.OutputFlag, OutputFormatText)
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
} else {
mockIn.Reset("y\n")
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
require.NoError(t, err)
t.Cleanup(func() {
kb.Delete("keyname1", "", false) // nolint:errcheck
kb.Delete("keyname2", "", false) // nolint:errcheck
})
}
mockIn.Reset("y\n")
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
require.NoError(t, err)
t.Cleanup(func() {
kb.Delete("keyname1", "", false) // nolint:errcheck
kb.Delete("keyname2", "", false) // nolint:errcheck
})
assert.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
if runningUnattended {
mockIn.Reset("testpass1\nN\n")
} else {
mockIn.Reset("N\n")
}
mockIn.Reset("N\n")
assert.Error(t, runAddCmd(cmd, []string{"keyname1"}))
if runningUnattended {
mockIn.Reset("testpass1\nN\n")
} else {
mockIn.Reset("y\n")
}
err := runAddCmd(cmd, []string{"keyname2"})
assert.NoError(t, err)
assert.NoError(t, runAddCmd(cmd, []string{"keyname2"}))
assert.Error(t, runAddCmd(cmd, []string{"keyname2"}))
mockIn.Reset("y\n")
assert.NoError(t, runAddCmd(cmd, []string{"keyname2"}))
// test --dry-run
assert.NoError(t, runAddCmd(cmd, []string{"keyname4"}))
assert.Error(t, runAddCmd(cmd, []string{"keyname4"}))
viper.Set(flags.FlagDryRun, true)
assert.NoError(t, runAddCmd(cmd, []string{"keyname4"}))
}

View File

@ -15,7 +15,6 @@ import (
)
func Test_runDeleteCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
deleteKeyCommand := DeleteKeyCommand()
mockIn, _, _ := tests.ApplyMockIO(deleteKeyCommand)
@ -26,38 +25,27 @@ func Test_runDeleteCmd(t *testing.T) {
fakeKeyName1 := "runDeleteCmd_Key1"
fakeKeyName2 := "runDeleteCmd_Key2"
if !runningUnattended {
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
require.NoError(t, err)
t.Cleanup(func() {
kb.Delete("runDeleteCmd_Key1", "", false) // nolint:errcheck
kb.Delete("runDeleteCmd_Key2", "", false) // nolint:errcheck
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
require.NoError(t, err)
t.Cleanup(func() {
kb.Delete("runDeleteCmd_Key1", "", false) // nolint:errcheck
kb.Delete("runDeleteCmd_Key2", "", false) // nolint:errcheck
})
}
})
// Now add a temporary keybase
kbHome, cleanUp := tests.NewTestCaseDir(t)
t.Cleanup(cleanUp)
viper.Set(flags.FlagHome, kbHome)
// Now
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
kb, err = keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
require.NoError(t, err)
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
_, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keyring.Secp256k1)
require.NoError(t, err)
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keyring.Secp256k1)
require.NoError(t, err)
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
err = runDeleteCmd(deleteKeyCommand, []string{"blah"})
require.Error(t, err)
require.Equal(t, "The specified item could not be found in the keyring", err.Error())
@ -65,24 +53,14 @@ func Test_runDeleteCmd(t *testing.T) {
// User confirmation missing
err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1})
require.Error(t, err)
if runningUnattended {
require.Equal(t, "aborted", err.Error())
} else {
require.Equal(t, "EOF", err.Error())
}
require.Equal(t, "EOF", err.Error())
{
if runningUnattended {
mockIn.Reset("testpass1\n")
}
_, err = kb.Get(fakeKeyName1)
require.NoError(t, err)
// Now there is a confirmation
viper.Set(flagYes, true)
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
require.NoError(t, runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1}))
_, err = kb.Get(fakeKeyName1)
@ -90,14 +68,8 @@ func Test_runDeleteCmd(t *testing.T) {
}
viper.Set(flagYes, true)
if runningUnattended {
mockIn.Reset("testpass1\n")
}
_, err = kb.Get(fakeKeyName2)
require.NoError(t, err)
if runningUnattended {
mockIn.Reset("testpass1\ny\ntestpass1\n")
}
err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName2})
require.NoError(t, err)
_, err = kb.Get(fakeKeyName2)

View File

@ -13,7 +13,6 @@ import (
)
func Test_runExportCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
exportKeyCommand := ExportKeyCommand()
mockIn, _, _ := tests.ApplyMockIO(exportKeyCommand)
@ -25,23 +24,14 @@ func Test_runExportCmd(t *testing.T) {
// create a key
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
require.NoError(t, err)
if !runningUnattended {
t.Cleanup(func() {
kb.Delete("keyname1", "", false) // nolint:errcheck
})
}
t.Cleanup(func() {
kb.Delete("keyname1", "", false) // nolint:errcheck
})
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
_, err = kb.CreateAccount("keyname1", tests.TestMnemonic, "", "123456789", "", keyring.Secp256k1)
require.NoError(t, err)
// Now enter password
if runningUnattended {
mockIn.Reset("123456789\n123456789\ntestpass1\n")
} else {
mockIn.Reset("123456789\n123456789\n")
}
mockIn.Reset("123456789\n123456789\n")
require.NoError(t, runExportCmd(exportKeyCommand, []string{"keyname1"}))
}

View File

@ -15,7 +15,6 @@ import (
)
func Test_runImportCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
importKeyCommand := ImportKeyCommand()
mockIn, _, _ := tests.ApplyMockIO(importKeyCommand)
@ -24,13 +23,11 @@ func Test_runImportCmd(t *testing.T) {
t.Cleanup(cleanUp)
viper.Set(flags.FlagHome, kbHome)
if !runningUnattended {
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
require.NoError(t, err)
t.Cleanup(func() {
kb.Delete("keyname1", "", false) // nolint:errcheck
})
}
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
require.NoError(t, err)
t.Cleanup(func() {
kb.Delete("keyname1", "", false) // nolint:errcheck
})
keyfile := filepath.Join(kbHome, "key.asc")
armoredKey := `-----BEGIN TENDERMINT PRIVATE KEY-----
@ -45,10 +42,6 @@ HbP+c6JmeJy9JXe2rbbF1QtCX1gLqGcDQPBXiCtFvP7/8wTZtVOPj8vREzhZ9ElO
require.NoError(t, ioutil.WriteFile(keyfile, []byte(armoredKey), 0644))
// Now enter password
if runningUnattended {
mockIn.Reset("123456789\n12345678\n12345678\n")
} else {
mockIn.Reset("123456789\n")
}
mockIn.Reset("123456789\n")
require.NoError(t, runImportCmd(importKeyCommand, []string{"keyname1", keyfile}))
}

View File

@ -14,7 +14,6 @@ import (
)
func Test_runListCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
type args struct {
cmd *cobra.Command
args []string
@ -34,9 +33,6 @@ func Test_runListCmd(t *testing.T) {
mockIn, _, _ := tests.ApplyMockIO(cmdBasic)
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
require.NoError(t, err)
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
_, err = kb.CreateAccount("something", tests.TestMnemonic, "", "", "", keyring.Secp256k1)
require.NoError(t, err)
@ -57,18 +53,12 @@ func Test_runListCmd(t *testing.T) {
for _, tt := range testData {
tt := tt
t.Run(tt.name, func(t *testing.T) {
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
viper.Set(flagListNames, false)
viper.Set(flags.FlagHome, tt.kbDir)
if err := runListCmd(tt.args.cmd, tt.args.args); (err != nil) != tt.wantErr {
t.Errorf("runListCmd() error = %v, wantErr %v", err, tt.wantErr)
}
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
viper.Set(flagListNames, true)
if err := runListCmd(tt.args.cmd, tt.args.args); (err != nil) != tt.wantErr {
t.Errorf("runListCmd() error = %v, wantErr %v", err, tt.wantErr)

View File

@ -43,10 +43,12 @@ It is recommended to run in 'dry-run' mode first to verify all key migration mat
func runMigrateCmd(cmd *cobra.Command, args []string) error {
// instantiate legacy keybase
rootDir := viper.GetString(flags.FlagHome)
legacyKb, err := NewKeyBaseFromDir(rootDir)
var legacyKb keyring.LegacyKeybase
legacyKb, err := NewLegacyKeyBaseFromDir(rootDir)
if err != nil {
return err
}
defer legacyKb.Close()
// fetch list of keys from legacy keybase
oldKeys, err := legacyKb.List()

View File

@ -45,7 +45,6 @@ The pass backend requires GnuPG: https://gnupg.org/
ShowKeysCmd(),
flags.LineBreak,
DeleteKeyCommand(),
UpdateKeyCommand(),
ParseKeyStringCommand(),
MigrateCommand(),
)

View File

@ -16,7 +16,7 @@ func TestCommands(t *testing.T) {
assert.NotNil(t, rootCommands)
// Commands are registered
assert.Equal(t, 11, len(rootCommands.Commands()))
assert.Equal(t, 10, len(rootCommands.Commands()))
}
func TestMain(m *testing.M) {

View File

@ -35,7 +35,6 @@ func Test_showKeysCmd(t *testing.T) {
}
func Test_runShowCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := ShowKeysCmd()
mockIn, _, _ := tests.ApplyMockIO(cmd)
require.EqualError(t, runShowCmd(cmd, []string{"invalid"}), "invalid is not a valid name or address: decoding bech32 failed: invalid bech32 string length 7")
@ -55,29 +54,17 @@ func Test_runShowCmd(t *testing.T) {
kb.Delete("runShowCmd_Key1", "", false)
kb.Delete("runShowCmd_Key2", "", false)
})
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
_, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keyring.Secp256k1)
require.NoError(t, err)
if runningUnattended {
mockIn.Reset("testpass1\n")
}
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keyring.Secp256k1)
require.NoError(t, err)
// Now try single key
if runningUnattended {
mockIn.Reset("testpass1\n")
}
require.EqualError(t, runShowCmd(cmd, []string{fakeKeyName1}), "invalid Bech32 prefix encoding provided: ")
// Now try single key - set bech to acc
viper.Set(FlagBechPrefix, sdk.PrefixAccount)
if runningUnattended {
mockIn.Reset("testpass1\n")
}
// try fetch by name
require.NoError(t, runShowCmd(cmd, []string{fakeKeyName1}))
@ -88,17 +75,11 @@ func Test_runShowCmd(t *testing.T) {
// Now try multisig key - set bech to acc
viper.Set(FlagBechPrefix, sdk.PrefixAccount)
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
require.EqualError(t, runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}), "threshold must be a positive integer")
// Now try multisig key - set bech to acc + threshold=2
viper.Set(FlagBechPrefix, sdk.PrefixAccount)
viper.Set(flagMultiSigThreshold, 2)
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
require.NoError(t, err)
@ -106,23 +87,14 @@ func Test_runShowCmd(t *testing.T) {
viper.Set(FlagBechPrefix, "acc")
viper.Set(FlagDevice, true)
viper.Set(flagMultiSigThreshold, 2)
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
require.EqualError(t, err, "the device flag (-d) can only be used for accounts stored in devices")
viper.Set(FlagBechPrefix, "val")
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
require.EqualError(t, err, "the device flag (-d) can only be used for accounts")
viper.Set(FlagPublicKey, true)
if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n")
}
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
require.EqualError(t, err, "the device flag (-d) can only be used for addresses not pubkeys")

Binary file not shown.

View File

@ -0,0 +1 @@
MANIFEST-000004

View File

@ -0,0 +1 @@
MANIFEST-000000

View File

18
client/keys/testdata/keys/keys.db/LOG vendored Normal file
View File

@ -0,0 +1,18 @@
=============== Mar 30, 2020 (CEST) ===============
02:07:34.137606 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
02:07:34.144547 db@open opening
02:07:34.144770 version@stat F·[] S·0B[] Sc·[]
02:07:34.145843 db@janitor F·2 G·0
02:07:34.145875 db@open done T·1.315251ms
02:07:34.335635 db@close closing
02:07:34.335736 db@close done T·98.95µs
=============== Mar 30, 2020 (CEST) ===============
02:08:33.239115 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
02:08:33.239264 version@stat F·[] S·0B[] Sc·[]
02:08:33.239281 db@open opening
02:08:33.239310 journal@recovery F·1
02:08:33.239398 journal@recovery recovering @1
02:08:33.322008 memdb@flush created L0@2 N·4 S·391B "cos..ess,v4":"run..nfo,v3"
02:08:33.323091 version@stat F·[1] S·391B[391B] Sc·[0.25]
02:08:33.421979 db@janitor F·3 G·0
02:08:33.422153 db@open done T·182.707962ms

Binary file not shown.

View File

@ -1,55 +0,0 @@
package keys
import (
"bufio"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/input"
)
// UpdateKeyCommand changes the password of a key in the keybase.
// It takes no effect on keys managed by new the keyring-based keybase implementation.
func UpdateKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "update <name>",
Short: "Change the password used to protect private key",
Deprecated: `it takes no effect with the new keyring
based backend and is provided only for backward compatibility with the
legacy LevelDB based backend.
Refer to your operating system's manual to learn how to change your
keyring's password.
`,
RunE: runUpdateCmd,
Args: cobra.ExactArgs(1),
}
return cmd
}
func runUpdateCmd(cmd *cobra.Command, args []string) error {
name := args[0]
buf := bufio.NewReader(cmd.InOrStdin())
kb, err := NewKeyBaseFromDir(viper.GetString(flags.FlagHome))
if err != nil {
return err
}
oldpass, err := input.GetPassword("Enter the current passphrase:", buf)
if err != nil {
return err
}
getNewpass := func() (string, error) {
return input.GetCheckPassword(
"Enter the new passphrase:",
"Repeat the new passphrase:", buf)
}
if err := kb.Update(name, oldpass, getNewpass); err != nil {
return err
}
cmd.PrintErrln("Password successfully updated!")
return nil
}

View File

@ -1,58 +0,0 @@
package keys
import (
"errors"
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/tests"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
func Test_updateKeyCommand(t *testing.T) {
require.NotNil(t, UpdateKeyCommand())
// No flags or defaults to validate
}
func Test_runUpdateCmd(t *testing.T) {
fakeKeyName1 := "runUpdateCmd_Key1"
fakeKeyName2 := "runUpdateCmd_Key2"
cmd := UpdateKeyCommand()
// fails because it requests a password
err := runUpdateCmd(cmd, []string{fakeKeyName1})
require.EqualError(t, err, "EOF")
// try again
mockIn, _, _ := tests.ApplyMockIO(cmd)
mockIn.Reset("pass1234\n")
err = runUpdateCmd(cmd, []string{fakeKeyName1})
require.True(t, errors.Is(err, sdkerrors.ErrKeyNotFound))
// Prepare a key base
// Now add a temporary keybase
kbHome, cleanUp1 := tests.NewTestCaseDir(t)
t.Cleanup(cleanUp1)
viper.Set(flags.FlagHome, kbHome)
kb, err := NewKeyBaseFromDir(viper.GetString(flags.FlagHome))
require.NoError(t, err)
_, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keyring.Secp256k1)
require.NoError(t, err)
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keyring.Secp256k1)
require.NoError(t, err)
// Try again now that we have keys
// Incorrect key type
mockIn.Reset("pass1234\nNew1234\nNew1234")
err = runUpdateCmd(cmd, []string{fakeKeyName1})
require.EqualError(t, err, "locally stored key required. Received: keyring.offlineInfo")
// TODO: Check for other type types?
}

View File

@ -5,7 +5,6 @@ import (
"io"
"path/filepath"
"github.com/99designs/keyring"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
"gopkg.in/yaml.v2"
@ -25,17 +24,14 @@ const (
type bechKeyOutFn func(keyInfo cryptokeyring.Info) (cryptokeyring.KeyOutput, error)
// NewKeyBaseFromDir initializes a keybase at the rootDir directory. Keybase
// NewLegacyKeyBaseFromDir initializes a legacy keybase at the rootDir directory. Keybase
// options can be applied when generating this new Keybase.
func NewKeyBaseFromDir(rootDir string, opts ...cryptokeyring.KeybaseOption) (cryptokeyring.Keybase, error) {
return getLazyKeyBaseFromDir(rootDir, opts...)
func NewLegacyKeyBaseFromDir(rootDir string, opts ...cryptokeyring.KeybaseOption) (cryptokeyring.LegacyKeybase, error) {
return getLegacyKeyBaseFromDir(rootDir, opts...)
}
// NewInMemoryKeyBase returns a storage-less keybase.
func NewInMemoryKeyBase() cryptokeyring.Keybase { return cryptokeyring.NewInMemory() }
func getLazyKeyBaseFromDir(rootDir string, opts ...cryptokeyring.KeybaseOption) (cryptokeyring.Keybase, error) {
return cryptokeyring.New(defaultKeyDBName, filepath.Join(rootDir, "keys"), opts...), nil
func getLegacyKeyBaseFromDir(rootDir string, opts ...cryptokeyring.KeybaseOption) (cryptokeyring.LegacyKeybase, error) {
return cryptokeyring.NewLegacy(defaultKeyDBName, filepath.Join(rootDir, "keys"), opts...)
}
func printKeyInfo(w io.Writer, keyInfo cryptokeyring.Info, bechKeyOut bechKeyOutFn) {
@ -116,8 +112,3 @@ func printPubKey(w io.Writer, info cryptokeyring.Info, bechKeyOut bechKeyOutFn)
fmt.Fprintln(w, ko.PubKey)
}
func isRunningUnattended() bool {
backends := keyring.AvailableBackends()
return len(backends) == 2 && backends[1] == keyring.BackendType("file")
}

View File

@ -33,6 +33,8 @@ type (
}
)
var fundraiserPath = types.GetConfig().GetFullFundraiserPath()
// newBaseKeybase generates the base keybase with defaulting to tendermint SECP256K1 key type
func newBaseKeybase(optionsFns ...KeybaseOption) baseKeybase {
// Default options for keybase
@ -139,7 +141,7 @@ func (kb baseKeybase) CreateMnemonic(
return nil, "", err
}
info, err = kb.CreateAccount(keyWriter, name, mnemonic, DefaultBIP39Passphrase, passwd, types.GetConfig().GetFullFundraiserPath(), algo)
info, err = kb.CreateAccount(keyWriter, name, mnemonic, DefaultBIP39Passphrase, passwd, fundraiserPath, algo)
if err != nil {
return nil, "", err
}

View File

@ -1,432 +0,0 @@
package keyring
import (
"encoding/hex"
"fmt"
"reflect"
"strings"
"github.com/pkg/errors"
tmcrypto "github.com/tendermint/tendermint/crypto"
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
dbm "github.com/tendermint/tm-db"
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
var _ Keybase = dbKeybase{}
// dbKeybase combines encryption and storage implementation to provide a
// full-featured key manager.
//
// NOTE: dbKeybase will be deprecated in favor of keyringKeybase.
type dbKeybase struct {
base baseKeybase
db dbm.DB
}
// newDBKeybase creates a new dbKeybase instance using the provided DB for
// reading and writing keys.
func newDBKeybase(db dbm.DB, opts ...KeybaseOption) Keybase {
return dbKeybase{
base: newBaseKeybase(opts...),
db: db,
}
}
// NewInMemory creates a transient keybase on top of in-memory storage
// instance useful for testing purposes and on-the-fly key generation.
// Keybase options can be applied when generating this new Keybase.
func NewInMemory(opts ...KeybaseOption) Keybase { return newDBKeybase(dbm.NewMemDB(), opts...) }
// CreateMnemonic generates a new key and persists it to storage, encrypted
// using the provided password. It returns the generated mnemonic and the key Info.
// It returns an error if it fails to generate a key for the given key algorithm
// type, or if another key is already stored under the same name.
func (kb dbKeybase) CreateMnemonic(
name string, language Language, passwd string, algo SigningAlgo,
) (info Info, mnemonic string, err error) {
return kb.base.CreateMnemonic(kb, name, language, passwd, algo)
}
// CreateAccount converts a mnemonic to a private key and persists it, encrypted
// with the given password.
func (kb dbKeybase) CreateAccount(
name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo,
) (Info, error) {
return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
}
// CreateLedger creates a new locally-stored reference to a Ledger keypair.
// It returns the created key info and an error if the Ledger could not be queried.
func (kb dbKeybase) CreateLedger(
name string, algo SigningAlgo, hrp string, account, index uint32,
) (Info, error) {
return kb.base.CreateLedger(kb, name, algo, hrp, account, index)
}
// CreateOffline creates a new reference to an offline keypair. It returns the
// created key info.
func (kb dbKeybase) CreateOffline(name string, pub tmcrypto.PubKey, algo SigningAlgo) (Info, error) {
return kb.base.writeOfflineKey(kb, name, pub, algo), nil
}
// CreateMulti creates a new reference to a multisig (offline) keypair. It
// returns the created key info.
func (kb dbKeybase) CreateMulti(name string, pub tmcrypto.PubKey) (Info, error) {
return kb.base.writeMultisigKey(kb, name, pub), nil
}
// List returns the keys from storage in alphabetical order.
func (kb dbKeybase) List() ([]Info, error) {
var res []Info
iter, err := kb.db.Iterator(nil, nil)
if err != nil {
return nil, err
}
defer iter.Close()
for ; iter.Valid(); iter.Next() {
key := string(iter.Key())
// need to include only keys in storage that have an info suffix
if strings.HasSuffix(key, infoSuffix) {
info, err := unmarshalInfo(iter.Value())
if err != nil {
return nil, err
}
res = append(res, info)
}
}
return res, nil
}
// Get returns the public information about one key.
func (kb dbKeybase) Get(name string) (Info, error) {
bs, err := kb.db.Get(infoKey(name))
if err != nil {
return nil, err
}
if len(bs) == 0 {
return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, name)
}
return unmarshalInfo(bs)
}
// GetByAddress returns Info based on a provided AccAddress. An error is returned
// if the address does not exist.
func (kb dbKeybase) GetByAddress(address types.AccAddress) (Info, error) {
ik, err := kb.db.Get(addrStringKey(address))
if err != nil {
return nil, err
}
if len(ik) == 0 {
return nil, fmt.Errorf("key with address %s not found", address)
}
bs, err := kb.db.Get(ik)
if err != nil {
return nil, err
}
return unmarshalInfo(bs)
}
// Sign signs the msg with the named key. It returns an error if the key doesn't
// exist or the decryption fails.
func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) {
info, err := kb.Get(name)
if err != nil {
return
}
var priv tmcrypto.PrivKey
switch i := info.(type) {
case localInfo:
if i.PrivKeyArmor == "" {
err = fmt.Errorf("private key not available")
return
}
priv, _, err = crypto.UnarmorDecryptPrivKey(i.PrivKeyArmor, passphrase)
if err != nil {
return nil, nil, err
}
case ledgerInfo:
return SignWithLedger(info, msg)
case offlineInfo, multiInfo:
return nil, info.GetPubKey(), errors.New("cannot sign with offline keys")
}
sig, err = priv.Sign(msg)
if err != nil {
return nil, nil, err
}
return sig, priv.PubKey(), nil
}
// ExportPrivateKeyObject returns a PrivKey object given the key name and
// passphrase. An error is returned if the key does not exist or if the Info for
// the key is invalid.
func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcrypto.PrivKey, error) {
info, err := kb.Get(name)
if err != nil {
return nil, err
}
var priv tmcrypto.PrivKey
switch i := info.(type) {
case localInfo:
linfo := i
if linfo.PrivKeyArmor == "" {
err = fmt.Errorf("private key not available")
return nil, err
}
priv, _, err = crypto.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return nil, err
}
case ledgerInfo, offlineInfo, multiInfo:
return nil, errors.New("only works on local private keys")
}
return priv, nil
}
func (kb dbKeybase) Export(name string) (armor string, err error) {
bz, err := kb.db.Get(infoKey(name))
if err != nil {
return "", err
}
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
return crypto.ArmorInfoBytes(bz), nil
}
// ExportPubKey returns public keys in ASCII armored format. It retrieves a Info
// object by its name and return the public key in a portable format.
func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
bz, err := kb.db.Get(infoKey(name))
if err != nil {
return "", err
}
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
info, err := unmarshalInfo(bz)
if err != nil {
return
}
return crypto.ArmorPubKeyBytes(info.GetPubKey().Bytes(), string(info.GetAlgo())), nil
}
// ExportPrivKey returns a private key in ASCII armored format.
// It returns an error if the key does not exist or a wrong encryption passphrase
// is supplied.
func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string,
encryptPassphrase string) (armor string, err error) {
priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase)
if err != nil {
return "", err
}
info, err := kb.Get(name)
if err != nil {
return "", err
}
return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
}
// ImportPrivKey imports a private key in ASCII armor format. It returns an
// error if a key with the same name exists or a wrong encryption passphrase is
// supplied.
func (kb dbKeybase) ImportPrivKey(name string, armor string, passphrase string) error {
if _, err := kb.Get(name); err == nil {
return errors.New("Cannot overwrite key " + name)
}
privKey, algo, err := crypto.UnarmorDecryptPrivKey(armor, passphrase)
if err != nil {
return errors.Wrap(err, "couldn't import private key")
}
kb.writeLocalKey(name, privKey, passphrase, SigningAlgo(algo))
return nil
}
func (kb dbKeybase) Import(name string, armor string) (err error) {
bz, err := kb.db.Get(infoKey(name))
if err != nil {
return err
}
if len(bz) > 0 {
return errors.New("cannot overwrite data for name " + name)
}
infoBytes, err := crypto.UnarmorInfoBytes(armor)
if err != nil {
return
}
return kb.db.Set(infoKey(name), infoBytes)
}
// ImportPubKey imports ASCII-armored public keys. Store a new Info object holding
// a public key only, i.e. it will not be possible to sign with it as it lacks the
// secret key.
func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
bz, err := kb.db.Get(infoKey(name))
if err != nil {
return err
}
if len(bz) > 0 {
return errors.New("cannot overwrite data for name " + name)
}
pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armor)
if err != nil {
return
}
pubKey, err := cryptoAmino.PubKeyFromBytes(pubBytes)
if err != nil {
return
}
kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(algo))
return
}
// Delete removes key forever, but we must present the proper passphrase before
// deleting it (for security). It returns an error if the key doesn't exist or
// passphrases don't match. Passphrase is ignored when deleting references to
// offline and Ledger / HW wallet keys.
func (kb dbKeybase) Delete(name, passphrase string, skipPass bool) error {
// verify we have the proper password before deleting
info, err := kb.Get(name)
if err != nil {
return err
}
if linfo, ok := info.(localInfo); ok && !skipPass {
if _, _, err = crypto.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase); err != nil {
return err
}
}
batch := kb.db.NewBatch()
defer batch.Close()
batch.Delete(addrStringKey(info.GetAddress()))
batch.Delete(infoKey(name))
return batch.WriteSync()
}
// Update changes the passphrase with which an already stored key is
// encrypted.
//
// oldpass must be the current passphrase used for encryption,
// getNewpass is a function to get the passphrase to permanently replace
// the current passphrase
func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error {
info, err := kb.Get(name)
if err != nil {
return err
}
switch i := info.(type) {
case localInfo:
linfo := i
key, _, err := crypto.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
if err != nil {
return err
}
newpass, err := getNewpass()
if err != nil {
return err
}
kb.writeLocalKey(name, key, newpass, i.GetAlgo())
return nil
default:
return fmt.Errorf("locally stored key required. Received: %v", reflect.TypeOf(info).String())
}
}
// SupportedAlgos returns a list of supported signing algorithms.
func (kb dbKeybase) SupportedAlgos() []SigningAlgo {
return kb.base.SupportedAlgos()
}
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
func (kb dbKeybase) SupportedAlgosLedger() []SigningAlgo {
return kb.base.SupportedAlgosLedger()
}
func (kb dbKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string, algo SigningAlgo) Info {
// encrypt private key using passphrase
privArmor := crypto.EncryptArmorPrivKey(priv, passphrase, string(algo))
// make Info
pub := priv.PubKey()
info := newLocalInfo(name, pub, privArmor, algo)
kb.writeInfo(name, info)
return info
}
func (kb dbKeybase) writeInfo(name string, info Info) {
// write the info by key
key := infoKey(name)
serializedInfo := marshalInfo(info)
kb.db.SetSync(key, serializedInfo)
// store a pointer to the infokey by address for fast lookup
kb.db.SetSync(addrStringKey(info.GetAddress()), key)
}
// this is to be removed together with dbKeybase and the old Keybase interface
func addrStringKey(address types.AccAddress) []byte {
return []byte(fmt.Sprintf("%s.%s", address.String(), addressSuffix))
}
func addrHexKey(address types.AccAddress) []byte {
return []byte(fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix))
}
func infoKey(name string) []byte {
return []byte(fmt.Sprintf("%s.%s", name, infoSuffix))
}

View File

@ -6,12 +6,11 @@
// The Keybase interface defines the methods that a type needs to implement to be used
// as key storage backend. This package provides few implementations out-of-the-box.
//
// New
// NewLegacy
//
// The New constructor returns an on-disk implementation backed by LevelDB storage that has been
// The NewLegacy constructor returns an on-disk implementation backed by LevelDB storage that has been
// the default implementation used by the SDK until v0.38.0. Due to security concerns, it is
// recommended to drop it in favor of the NewKeyring or NewKeyringFile constructors as it will be
// removed in future releases.
// recommended to drop it in favor of the NewKeyring constructor as it will be removed in future releases.
//
// NewInMemory
//
@ -42,4 +41,6 @@
// https://www.passwordstore.org/
// test This backend stores keys insecurely to disk. It does not prompt for a password to
// be unlocked and it should be use only for testing purposes.
// memory Same instance as returned by NewInMemory. This backend uses a transient storage. Keys
// are discarded when the process terminates or the type instance is garbage collected.
package keyring

View File

@ -40,9 +40,6 @@ type Keybase interface {
// CreateMulti creates, stores, and returns a new multsig (offline) key reference
CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error)
// The following operations will *only* work on locally-stored keys
Update(name, oldpass string, getNewpass func() (string, error)) error
// Import imports ASCII armored Info objects.
Import(name string, armor string) (err error)

View File

@ -1,495 +0,0 @@
package keyring
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
tmcrypto "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func init() {
crypto.BcryptSecurityParameter = 1
}
const (
nums = "1234"
foobar = "foobar"
)
func TestLanguage(t *testing.T) {
kb := NewInMemory()
_, _, err := kb.CreateMnemonic("something", Japanese, "no_pass", Secp256k1)
require.Error(t, err)
require.Equal(t, "unsupported language: only english is supported", err.Error())
}
func TestCreateAccountInvalidMnemonic(t *testing.T) {
kb := NewInMemory()
_, err := kb.CreateAccount(
"some_account",
"malarkey pair crucial catch public canyon evil outer stage ten gym tornado",
"", "", CreateHDPath(0, 0).String(), Secp256k1)
require.Error(t, err)
require.Equal(t, "Invalid mnemonic", err.Error())
}
func TestCreateLedgerUnsupportedAlgo(t *testing.T) {
kb := NewInMemory()
supportedLedgerAlgos := kb.SupportedAlgosLedger()
for _, supportedAlgo := range supportedLedgerAlgos {
if Ed25519 == supportedAlgo {
require.FailNow(t, "Was not an unsupported algorithm")
}
}
_, err := kb.CreateLedger("some_account", Ed25519, "cosmos", 0, 1)
require.Error(t, err)
require.Equal(t, "unsupported signing algo", err.Error())
}
func TestCreateLedger(t *testing.T) {
kb := NewInMemory(WithSupportedAlgosLedger([]SigningAlgo{Secp256k1, Ed25519}))
// test_cover and test_unit will result in different answers
// test_cover does not compile some dependencies so ledger is disabled
// test_unit may add a ledger mock
// both cases are acceptable
supportedLedgerAlgos := kb.SupportedAlgosLedger()
secpSupported := false
edSupported := false
for _, supportedAlgo := range supportedLedgerAlgos {
secpSupported = secpSupported || (supportedAlgo == Secp256k1)
edSupported = edSupported || (supportedAlgo == Ed25519)
}
require.True(t, secpSupported)
require.True(t, edSupported)
ledger, err := kb.CreateLedger("some_account", Secp256k1, "cosmos", 3, 1)
if err != nil {
require.Error(t, err)
require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error())
require.Nil(t, ledger)
t.Skip("ledger nano S: support for ledger devices is not available in this executable")
return
}
// The mock is available, check that the address is correct
pubKey := ledger.GetPubKey()
pk, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)
require.NoError(t, err)
require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk)
// Check that restoring the key gets the same results
restoredKey, err := kb.Get("some_account")
require.NoError(t, err)
require.NotNil(t, restoredKey)
require.Equal(t, "some_account", restoredKey.GetName())
require.Equal(t, TypeLedger, restoredKey.GetType())
pubKey = restoredKey.GetPubKey()
pk, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)
require.NoError(t, err)
require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk)
path, err := restoredKey.GetPath()
require.NoError(t, err)
require.Equal(t, "44'/118'/3'/0/1", path.String())
}
// TestKeyManagement makes sure we can manipulate these keys well
func TestKeyManagement(t *testing.T) {
// make the storage with reasonable defaults
cstore := NewInMemory(WithSupportedAlgos([]SigningAlgo{Secp256k1, Sr25519}))
// Test modified supported algos
supportedAlgos := cstore.SupportedAlgos()
secpSupported := false
edSupported := false
srSupported := false
for _, supportedAlgo := range supportedAlgos {
secpSupported = secpSupported || (supportedAlgo == Secp256k1)
edSupported = edSupported || (supportedAlgo == Ed25519)
srSupported = srSupported || (supportedAlgo == Sr25519)
}
require.True(t, secpSupported)
require.False(t, edSupported)
require.True(t, srSupported)
algo := Secp256k1
n1, n2, n3 := "personal", "business", "other"
p1, p2 := nums, "really-secure!@#$"
// Check empty state
l, err := cstore.List()
require.Nil(t, err)
require.Empty(t, l)
_, _, err = cstore.CreateMnemonic(n1, English, p1, Ed25519)
require.Error(t, err, "ed25519 keys are currently not supported by keybase")
// create some keys
_, err = cstore.Get(n1)
require.Error(t, err)
i, _, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.NoError(t, err)
require.Equal(t, n1, i.GetName())
_, _, err = cstore.CreateMnemonic(n2, English, p2, algo)
require.NoError(t, err)
// we can get these keys
i2, err := cstore.Get(n2)
require.NoError(t, err)
_, err = cstore.Get(n3)
require.NotNil(t, err)
_, err = cstore.GetByAddress(accAddr(i2))
require.NoError(t, err)
addr, err := sdk.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t")
require.NoError(t, err)
_, err = cstore.GetByAddress(addr)
require.NotNil(t, err)
// list shows them in order
keyS, err := cstore.List()
require.NoError(t, err)
require.Equal(t, 2, len(keyS))
// note these are in alphabetical order
require.Equal(t, n2, keyS[0].GetName())
require.Equal(t, n1, keyS[1].GetName())
require.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey())
// deleting a key removes it
err = cstore.Delete("bad name", "foo", false)
require.NotNil(t, err)
err = cstore.Delete(n1, p1, false)
require.NoError(t, err)
keyS, err = cstore.List()
require.NoError(t, err)
require.Equal(t, 1, len(keyS))
_, err = cstore.Get(n1)
require.Error(t, err)
// create an offline key
o1 := "offline"
priv1 := ed25519.GenPrivKey()
pub1 := priv1.PubKey()
i, err = cstore.CreateOffline(o1, pub1, algo)
require.Nil(t, err)
require.Equal(t, pub1, i.GetPubKey())
require.Equal(t, o1, i.GetName())
iOffline := i.(*offlineInfo)
require.Equal(t, algo, iOffline.GetAlgo())
keyS, err = cstore.List()
require.NoError(t, err)
require.Equal(t, 2, len(keyS))
// delete the offline key
err = cstore.Delete(o1, "", false)
require.NoError(t, err)
keyS, err = cstore.List()
require.NoError(t, err)
require.Equal(t, 1, len(keyS))
// addr cache gets nuked - and test skip flag
err = cstore.Delete(n2, "", true)
require.NoError(t, err)
}
// TestSignVerify does some detailed checks on how we sign and validate
// signatures
func TestSignVerify(t *testing.T) {
cstore := NewInMemory()
algo := Secp256k1
n1, n2, n3 := "some dude", "a dudette", "dude-ish"
p1, p2, p3 := nums, foobar, foobar
// create two users and get their info
i1, _, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err)
i2, _, err := cstore.CreateMnemonic(n2, English, p2, algo)
require.Nil(t, err)
// Import a public key
armor, err := cstore.ExportPubKey(n2)
require.Nil(t, err)
err = cstore.ImportPubKey(n3, armor)
require.NoError(t, err)
i3, err := cstore.Get(n3)
require.NoError(t, err)
require.Equal(t, i3.GetName(), n3)
// let's try to sign some messages
d1 := []byte("my first message")
d2 := []byte("some other important info!")
d3 := []byte("feels like I forgot something...")
// try signing both data with both ..
s11, pub1, err := cstore.Sign(n1, p1, d1)
require.Nil(t, err)
require.Equal(t, i1.GetPubKey(), pub1)
s12, pub1, err := cstore.Sign(n1, p1, d2)
require.Nil(t, err)
require.Equal(t, i1.GetPubKey(), pub1)
s21, pub2, err := cstore.Sign(n2, p2, d1)
require.Nil(t, err)
require.Equal(t, i2.GetPubKey(), pub2)
s22, pub2, err := cstore.Sign(n2, p2, d2)
require.Nil(t, err)
require.Equal(t, i2.GetPubKey(), pub2)
// let's try to validate and make sure it only works when everything is proper
cases := []struct {
key tmcrypto.PubKey
data []byte
sig []byte
valid bool
}{
// proper matches
{i1.GetPubKey(), d1, s11, true},
// change data, pubkey, or signature leads to fail
{i1.GetPubKey(), d2, s11, false},
{i2.GetPubKey(), d1, s11, false},
{i1.GetPubKey(), d1, s21, false},
// make sure other successes
{i1.GetPubKey(), d2, s12, true},
{i2.GetPubKey(), d1, s21, true},
{i2.GetPubKey(), d2, s22, true},
}
for i, tc := range cases {
valid := tc.key.VerifyBytes(tc.data, tc.sig)
require.Equal(t, tc.valid, valid, "%d", i)
}
// Now try to sign data with a secret-less key
_, _, err = cstore.Sign(n3, p3, d3)
require.Error(t, err)
require.Equal(t, "cannot sign with offline keys", err.Error())
}
func assertPassword(t *testing.T, cstore Keybase, name, pass, badpass string) {
getNewpass := func() (string, error) { return pass, nil }
err := cstore.Update(name, badpass, getNewpass)
require.NotNil(t, err)
err = cstore.Update(name, pass, getNewpass)
require.Nil(t, err, "%+v", err)
}
// TestExportImport tests exporting and importing
func TestExportImport(t *testing.T) {
// make the storage with reasonable defaults
cstore := NewInMemory()
info, _, err := cstore.CreateMnemonic("john", English, "secretcpw", Secp256k1)
require.NoError(t, err)
require.Equal(t, info.GetName(), "john")
john, err := cstore.Get("john")
require.NoError(t, err)
require.Equal(t, info.GetName(), "john")
johnAddr := info.GetPubKey().Address()
armor, err := cstore.Export("john")
require.NoError(t, err)
err = cstore.Import("john2", armor)
require.NoError(t, err)
john2, err := cstore.Get("john2")
require.NoError(t, err)
require.Equal(t, john.GetPubKey().Address(), johnAddr)
require.Equal(t, john.GetName(), "john")
require.Equal(t, john, john2)
}
//
func TestExportImportPubKey(t *testing.T) {
// make the storage with reasonable defaults
cstore := NewInMemory()
// CreateMnemonic a private-public key pair and ensure consistency
notPasswd := "n9y25ah7"
info, _, err := cstore.CreateMnemonic("john", English, notPasswd, Secp256k1)
require.Nil(t, err)
require.NotEqual(t, info, "")
require.Equal(t, info.GetName(), "john")
addr := info.GetPubKey().Address()
john, err := cstore.Get("john")
require.NoError(t, err)
require.Equal(t, john.GetName(), "john")
require.Equal(t, john.GetPubKey().Address(), addr)
// Export the public key only
armor, err := cstore.ExportPubKey("john")
require.NoError(t, err)
// Import it under a different name
err = cstore.ImportPubKey("john-pubkey-only", armor)
require.NoError(t, err)
// Ensure consistency
john2, err := cstore.Get("john-pubkey-only")
require.NoError(t, err)
// Compare the public keys
require.True(t, john.GetPubKey().Equals(john2.GetPubKey()))
// Ensure the original key hasn't changed
john, err = cstore.Get("john")
require.NoError(t, err)
require.Equal(t, john.GetPubKey().Address(), addr)
require.Equal(t, john.GetName(), "john")
// Ensure keys cannot be overwritten
err = cstore.ImportPubKey("john-pubkey-only", armor)
require.NotNil(t, err)
}
// TestAdvancedKeyManagement verifies update, import, export functionality
func TestAdvancedKeyManagement(t *testing.T) {
// make the storage with reasonable defaults
cstore := NewInMemory()
algo := Secp256k1
n1, n2 := "old-name", "new name"
p1, p2 := nums, foobar
// make sure key works with initial password
_, _, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err, "%+v", err)
assertPassword(t, cstore, n1, p1, p2)
// update password requires the existing password
getNewpass := func() (string, error) { return p2, nil }
err = cstore.Update(n1, "jkkgkg", getNewpass)
require.NotNil(t, err)
assertPassword(t, cstore, n1, p1, p2)
// then it changes the password when correct
err = cstore.Update(n1, p1, getNewpass)
require.NoError(t, err)
// p2 is now the proper one!
assertPassword(t, cstore, n1, p2, p1)
// exporting requires the proper name and passphrase
_, err = cstore.Export(n1 + ".notreal")
require.NotNil(t, err)
_, err = cstore.Export(" " + n1)
require.NotNil(t, err)
_, err = cstore.Export(n1 + " ")
require.NotNil(t, err)
_, err = cstore.Export("")
require.NotNil(t, err)
exported, err := cstore.Export(n1)
require.Nil(t, err, "%+v", err)
// import succeeds
err = cstore.Import(n2, exported)
require.NoError(t, err)
// second import fails
err = cstore.Import(n2, exported)
require.NotNil(t, err)
}
// TestSeedPhrase verifies restoring from a seed phrase
func TestSeedPhrase(t *testing.T) {
// make the storage with reasonable defaults
cstore := NewInMemory()
algo := Secp256k1
n1, n2 := "lost-key", "found-again"
p1, p2 := nums, foobar
// make sure key works with initial password
info, mnemonic, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err, "%+v", err)
require.Equal(t, n1, info.GetName())
require.NotEmpty(t, mnemonic)
// now, let us delete this key
err = cstore.Delete(n1, p1, false)
require.Nil(t, err, "%+v", err)
_, err = cstore.Get(n1)
require.NotNil(t, err)
// let us re-create it from the mnemonic-phrase
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
hdPath := params.String()
newInfo, err := cstore.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, Secp256k1)
require.NoError(t, err)
require.Equal(t, n2, newInfo.GetName())
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
require.Equal(t, info.GetPubKey(), newInfo.GetPubKey())
}
func ExampleNew() {
// Select the encryption and storage for your cryptostore
customKeyGenFunc := func(bz []byte, algo SigningAlgo) (tmcrypto.PrivKey, error) {
var bzArr [32]byte
copy(bzArr[:], bz)
return secp256k1.PrivKeySecp256k1(bzArr), nil
}
cstore := NewInMemory(WithKeygenFunc(customKeyGenFunc))
sec := Secp256k1
// Add keys and see they return in alphabetical order
bob, _, err := cstore.CreateMnemonic("Bob", English, "friend", sec)
if err != nil {
// this should never happen
fmt.Println(err)
} else {
// return info here just like in List
fmt.Println(bob.GetName())
}
_, _, _ = cstore.CreateMnemonic("Alice", English, "secret", sec)
_, _, _ = cstore.CreateMnemonic("Carl", English, "mitm", sec)
info, _ := cstore.List()
for _, i := range info {
fmt.Println(i.GetName())
}
// We need to use passphrase to generate a signature
tx := []byte("deadbeef")
sig, pub, err := cstore.Sign("Bob", "friend", tx)
if err != nil {
fmt.Println("don't accept real passphrase")
}
// and we can validate the signature with publicly available info
binfo, _ := cstore.Get("Bob")
if !binfo.GetPubKey().Equals(bob.GetPubKey()) {
fmt.Println("Get and Create return different keys")
}
if pub.Equals(binfo.GetPubKey()) {
fmt.Println("signed by Bob")
}
if !pub.VerifyBytes(tx, sig) {
fmt.Println("invalid signature")
}
// Output:
// Bob
// Alice
// Bob
// Carl
// signed by Bob
}
func accAddr(info Info) sdk.AccAddress {
return (sdk.AccAddress)(info.GetPubKey().Address())
}

View File

@ -2,6 +2,7 @@ package keyring
import (
"bufio"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
@ -29,6 +30,7 @@ const (
BackendKWallet = "kwallet"
BackendPass = "pass"
BackendTest = "test"
BackendMemory = "memory"
)
const (
@ -66,6 +68,8 @@ func NewKeyring(
var err error
switch backend {
case BackendMemory:
return NewInMemory(opts...), nil
case BackendTest:
db, err = keyring.Open(lkbToKeyringConfig(appName, rootDir, nil, true))
case BackendFile:
@ -86,6 +90,13 @@ func NewKeyring(
return newKeyringKeybase(db, opts...), nil
}
// NewInMemory creates a transient keyring useful for testing
// purposes and on-the-fly key generation.
// Keybase options can be applied when generating this new Keybase.
func NewInMemory(opts ...KeybaseOption) Keybase {
return newKeyringKeybase(keyring.NewArrayKeyring(nil), opts...)
}
// CreateMnemonic generates a new key and persists it to storage, encrypted
// using the provided password. It returns the generated mnemonic and the key Info.
// An error is returned if it fails to generate a key for the given algo type,
@ -414,13 +425,6 @@ func (kb keyringKeybase) Delete(name, _ string, _ bool) error {
return nil
}
// Update changes the passphrase with which an already stored key is encrypted.
// The oldpass must be the current passphrase used for encryption, getNewpass is
// a function to get the passphrase to permanently replace the current passphrase.
func (kb keyringKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error {
return errors.New("unsupported operation")
}
// SupportedAlgos returns a list of supported signing algorithms.
func (kb keyringKeybase) SupportedAlgos() []SigningAlgo {
return kb.base.SupportedAlgos()
@ -581,3 +585,7 @@ func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) {
}
}
}
func addrHexKey(address types.AccAddress) []byte {
return []byte(fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix))
}

View File

@ -2,19 +2,49 @@ package keyring
import (
"bytes"
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/go-amino"
tmcrypto "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/tendermint/tendermint/crypto/multisig"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestLazyKeyManagementKeyRing(t *testing.T) {
func init() {
crypto.BcryptSecurityParameter = 1
}
const (
nums = "1234"
foobar = "foobar"
)
func TestNewKeyring(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
mockIn := strings.NewReader("")
t.Cleanup(cleanup)
kr, err := NewKeyring("cosmos", BackendFile, dir, mockIn)
require.NoError(t, err)
mockIn.Reset("password\npassword\n")
info, _, err := kr.CreateMnemonic("foo", English, "password", Secp256k1)
require.NoError(t, err)
require.Equal(t, "foo", info.GetName())
}
func TestKeyManagementKeyRing(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb, err := NewKeyring("keybasename", "test", dir, nil)
@ -99,7 +129,7 @@ func TestLazyKeyManagementKeyRing(t *testing.T) {
// TestSignVerify does some detailed checks on how we sign and validate
// signatures
func TestLazySignVerifyKeyRingWithLedger(t *testing.T) {
func TestSignVerifyKeyRingWithLedger(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb, err := NewKeyring("keybasename", "test", dir, nil)
@ -134,7 +164,7 @@ func TestLazySignVerifyKeyRingWithLedger(t *testing.T) {
require.Equal(t, "not a ledger object", err.Error())
}
func TestLazySignVerifyKeyRing(t *testing.T) {
func TestSignVerifyKeyRing(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb, err := NewKeyring("keybasename", "test", dir, nil)
@ -184,7 +214,7 @@ func TestLazySignVerifyKeyRing(t *testing.T) {
// let's try to validate and make sure it only works when everything is proper
cases := []struct {
key crypto.PubKey
key tmcrypto.PubKey
data []byte
sig []byte
valid bool
@ -212,7 +242,7 @@ func TestLazySignVerifyKeyRing(t *testing.T) {
require.Equal(t, "cannot sign with offline keys", err.Error())
}
func TestLazyExportImportKeyRing(t *testing.T) {
func TestExportImportKeyRing(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb, err := NewKeyring("keybasename", "test", dir, nil)
@ -241,7 +271,7 @@ func TestLazyExportImportKeyRing(t *testing.T) {
require.Equal(t, john, john2)
}
func TestLazyExportImportPubKeyKeyRing(t *testing.T) {
func TestExportImportPubKeyKeyRing(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb, err := NewKeyring("keybasename", "test", dir, nil)
@ -282,7 +312,7 @@ func TestLazyExportImportPubKeyKeyRing(t *testing.T) {
require.NotNil(t, err)
}
func TestLazyExportPrivateKeyObjectKeyRing(t *testing.T) {
func TestExportPrivateKeyObjectKeyRing(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb, err := NewKeyring("keybasename", "test", dir, nil)
@ -298,7 +328,7 @@ func TestLazyExportPrivateKeyObjectKeyRing(t *testing.T) {
require.True(t, exported.PubKey().Equals(info.GetPubKey()))
}
func TestLazyAdvancedKeyManagementKeyRing(t *testing.T) {
func TestAdvancedKeyManagementKeyRing(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb, err := NewKeyring("keybasename", "test", dir, nil)
@ -332,7 +362,7 @@ func TestLazyAdvancedKeyManagementKeyRing(t *testing.T) {
require.NotNil(t, err)
}
func TestLazySeedPhraseKeyRing(t *testing.T) {
func TestSeedPhraseKeyRing(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb, err := NewKeyring("keybasename", "test", dir, nil)
@ -393,15 +423,6 @@ func TestKeyringKeybaseExportImportPrivKey(t *testing.T) {
require.Equal(t, "The specified item could not be found in the keyring", err.Error())
}
func TestKeyringKeybaseUpdate(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb, err := NewKeyring("keybasename", "test", dir, nil)
require.NoError(t, err)
require.Equal(t, "unsupported operation", kb.Update("john", "oldpassword",
func() (string, error) { return "", nil }).Error())
}
func TestSupportedAlgos(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
@ -410,3 +431,575 @@ func TestSupportedAlgos(t *testing.T) {
require.Equal(t, []SigningAlgo{"secp256k1"}, kb.SupportedAlgos())
require.Equal(t, []SigningAlgo{"secp256k1"}, kb.SupportedAlgosLedger())
}
func TestInMemoryLanguage(t *testing.T) {
kb := NewInMemory()
_, _, err := kb.CreateMnemonic("something", Japanese, "no_pass", Secp256k1)
require.Error(t, err)
require.Equal(t, "unsupported language: only english is supported", err.Error())
}
func TestInMemoryCreateMultisig(t *testing.T) {
kb, err := NewKeyring("keybasename", "memory", "", nil)
require.NoError(t, err)
multi := multisig.PubKeyMultisigThreshold{
K: 1,
PubKeys: []tmcrypto.PubKey{secp256k1.GenPrivKey().PubKey()},
}
_, err = kb.CreateMulti("multi", multi)
require.NoError(t, err)
}
func TestInMemoryCreateAccountInvalidMnemonic(t *testing.T) {
kb := NewInMemory()
_, err := kb.CreateAccount(
"some_account",
"malarkey pair crucial catch public canyon evil outer stage ten gym tornado",
"", "", CreateHDPath(0, 0).String(), Secp256k1)
require.Error(t, err)
require.Equal(t, "Invalid mnemonic", err.Error())
}
func TestInMemoryCreateLedgerUnsupportedAlgo(t *testing.T) {
kb := NewInMemory()
supportedLedgerAlgos := kb.SupportedAlgosLedger()
for _, supportedAlgo := range supportedLedgerAlgos {
if Ed25519 == supportedAlgo {
require.FailNow(t, "Was not an unsupported algorithm")
}
}
_, err := kb.CreateLedger("some_account", Ed25519, "cosmos", 0, 1)
require.Error(t, err)
require.Equal(t, "unsupported signing algo", err.Error())
}
func TestInMemoryCreateLedger(t *testing.T) {
kb := NewInMemory(WithSupportedAlgosLedger([]SigningAlgo{Secp256k1, Ed25519}))
// test_cover and test_unit will result in different answers
// test_cover does not compile some dependencies so ledger is disabled
// test_unit may add a ledger mock
// both cases are acceptable
supportedLedgerAlgos := kb.SupportedAlgosLedger()
secpSupported := false
edSupported := false
for _, supportedAlgo := range supportedLedgerAlgos {
secpSupported = secpSupported || (supportedAlgo == Secp256k1)
edSupported = edSupported || (supportedAlgo == Ed25519)
}
require.True(t, secpSupported)
require.True(t, edSupported)
ledger, err := kb.CreateLedger("some_account", Secp256k1, "cosmos", 3, 1)
if err != nil {
require.Error(t, err)
require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error())
require.Nil(t, ledger)
t.Skip("ledger nano S: support for ledger devices is not available in this executable")
return
}
// The mock is available, check that the address is correct
pubKey := ledger.GetPubKey()
pk, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)
require.NoError(t, err)
require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk)
// Check that restoring the key gets the same results
restoredKey, err := kb.Get("some_account")
require.NoError(t, err)
require.NotNil(t, restoredKey)
require.Equal(t, "some_account", restoredKey.GetName())
require.Equal(t, TypeLedger, restoredKey.GetType())
pubKey = restoredKey.GetPubKey()
pk, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)
require.NoError(t, err)
require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk)
path, err := restoredKey.GetPath()
require.NoError(t, err)
require.Equal(t, "44'/118'/3'/0/1", path.String())
}
// TestInMemoryKeyManagement makes sure we can manipulate these keys well
func TestInMemoryKeyManagement(t *testing.T) {
// make the storage with reasonable defaults
cstore := NewInMemory(WithSupportedAlgos([]SigningAlgo{Secp256k1, Sr25519}))
// Test modified supported algos
supportedAlgos := cstore.SupportedAlgos()
secpSupported := false
edSupported := false
srSupported := false
for _, supportedAlgo := range supportedAlgos {
secpSupported = secpSupported || (supportedAlgo == Secp256k1)
edSupported = edSupported || (supportedAlgo == Ed25519)
srSupported = srSupported || (supportedAlgo == Sr25519)
}
require.True(t, secpSupported)
require.False(t, edSupported)
require.True(t, srSupported)
algo := Secp256k1
n1, n2, n3 := "personal", "business", "other"
p1, p2 := nums, "really-secure!@#$"
// Check empty state
l, err := cstore.List()
require.Nil(t, err)
require.Empty(t, l)
_, _, err = cstore.CreateMnemonic(n1, English, p1, Ed25519)
require.Error(t, err, "ed25519 keys are currently not supported by keybase")
// create some keys
_, err = cstore.Get(n1)
require.Error(t, err)
i, _, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.NoError(t, err)
require.Equal(t, n1, i.GetName())
_, _, err = cstore.CreateMnemonic(n2, English, p2, algo)
require.NoError(t, err)
// we can get these keys
i2, err := cstore.Get(n2)
require.NoError(t, err)
_, err = cstore.Get(n3)
require.NotNil(t, err)
_, err = cstore.GetByAddress(accAddr(i2))
require.NoError(t, err)
addr, err := sdk.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t")
require.NoError(t, err)
_, err = cstore.GetByAddress(addr)
require.NotNil(t, err)
// list shows them in order
keyS, err := cstore.List()
require.NoError(t, err)
require.Equal(t, 2, len(keyS))
// note these are in alphabetical order
require.Equal(t, n2, keyS[0].GetName())
require.Equal(t, n1, keyS[1].GetName())
require.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey())
// deleting a key removes it
err = cstore.Delete("bad name", "foo", false)
require.NotNil(t, err)
err = cstore.Delete(n1, p1, false)
require.NoError(t, err)
keyS, err = cstore.List()
require.NoError(t, err)
require.Equal(t, 1, len(keyS))
_, err = cstore.Get(n1)
require.Error(t, err)
// create an offline key
o1 := "offline"
priv1 := ed25519.GenPrivKey()
pub1 := priv1.PubKey()
i, err = cstore.CreateOffline(o1, pub1, algo)
require.Nil(t, err)
require.Equal(t, pub1, i.GetPubKey())
require.Equal(t, o1, i.GetName())
iOffline := i.(*offlineInfo)
require.Equal(t, algo, iOffline.GetAlgo())
keyS, err = cstore.List()
require.NoError(t, err)
require.Equal(t, 2, len(keyS))
// delete the offline key
err = cstore.Delete(o1, "", false)
require.NoError(t, err)
keyS, err = cstore.List()
require.NoError(t, err)
require.Equal(t, 1, len(keyS))
// addr cache gets nuked - and test skip flag
err = cstore.Delete(n2, "", true)
require.NoError(t, err)
}
// TestInMemorySignVerify does some detailed checks on how we sign and validate
// signatures
func TestInMemorySignVerify(t *testing.T) {
cstore := NewInMemory()
algo := Secp256k1
n1, n2, n3 := "some dude", "a dudette", "dude-ish"
p1, p2, p3 := nums, foobar, foobar
// create two users and get their info
i1, _, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err)
i2, _, err := cstore.CreateMnemonic(n2, English, p2, algo)
require.Nil(t, err)
// Import a public key
armor, err := cstore.ExportPubKey(n2)
require.Nil(t, err)
err = cstore.ImportPubKey(n3, armor)
require.NoError(t, err)
i3, err := cstore.Get(n3)
require.NoError(t, err)
require.Equal(t, i3.GetName(), n3)
// let's try to sign some messages
d1 := []byte("my first message")
d2 := []byte("some other important info!")
d3 := []byte("feels like I forgot something...")
// try signing both data with both ..
s11, pub1, err := cstore.Sign(n1, p1, d1)
require.Nil(t, err)
require.Equal(t, i1.GetPubKey(), pub1)
s12, pub1, err := cstore.Sign(n1, p1, d2)
require.Nil(t, err)
require.Equal(t, i1.GetPubKey(), pub1)
s21, pub2, err := cstore.Sign(n2, p2, d1)
require.Nil(t, err)
require.Equal(t, i2.GetPubKey(), pub2)
s22, pub2, err := cstore.Sign(n2, p2, d2)
require.Nil(t, err)
require.Equal(t, i2.GetPubKey(), pub2)
// let's try to validate and make sure it only works when everything is proper
cases := []struct {
key tmcrypto.PubKey
data []byte
sig []byte
valid bool
}{
// proper matches
{i1.GetPubKey(), d1, s11, true},
// change data, pubkey, or signature leads to fail
{i1.GetPubKey(), d2, s11, false},
{i2.GetPubKey(), d1, s11, false},
{i1.GetPubKey(), d1, s21, false},
// make sure other successes
{i1.GetPubKey(), d2, s12, true},
{i2.GetPubKey(), d1, s21, true},
{i2.GetPubKey(), d2, s22, true},
}
for i, tc := range cases {
valid := tc.key.VerifyBytes(tc.data, tc.sig)
require.Equal(t, tc.valid, valid, "%d", i)
}
// Now try to sign data with a secret-less key
_, _, err = cstore.Sign(n3, p3, d3)
require.Error(t, err)
require.Equal(t, "cannot sign with offline keys", err.Error())
}
// TestInMemoryExportImport tests exporting and importing
func TestInMemoryExportImport(t *testing.T) {
// make the storage with reasonable defaults
cstore := NewInMemory()
info, _, err := cstore.CreateMnemonic("john", English, "secretcpw", Secp256k1)
require.NoError(t, err)
require.Equal(t, info.GetName(), "john")
john, err := cstore.Get("john")
require.NoError(t, err)
require.Equal(t, info.GetName(), "john")
johnAddr := info.GetPubKey().Address()
armor, err := cstore.Export("john")
require.NoError(t, err)
err = cstore.Import("john2", armor)
require.NoError(t, err)
john2, err := cstore.Get("john2")
require.NoError(t, err)
require.Equal(t, john.GetPubKey().Address(), johnAddr)
require.Equal(t, john.GetName(), "john")
require.Equal(t, john, john2)
}
func TestInMemoryExportImportPrivKey(t *testing.T) {
kb := NewInMemory()
info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1)
require.NoError(t, err)
require.Equal(t, info.GetName(), "john")
priv1, err := kb.Get("john")
require.NoError(t, err)
// decrypt local private key, and produce encrypted ASCII armored output
armored, err := kb.ExportPrivKey("john", "secretcpw", "new_secretcpw")
require.NoError(t, err)
// delete exported key
require.NoError(t, kb.Delete("john", "", true))
_, err = kb.Get("john")
require.Error(t, err)
// import armored key
require.NoError(t, kb.ImportPrivKey("john", armored, "new_secretcpw"))
// ensure old and new keys match
priv2, err := kb.Get("john")
require.NoError(t, err)
require.True(t, priv1.GetPubKey().Equals(priv2.GetPubKey()))
}
func TestInMemoryExportImportPubKey(t *testing.T) {
// make the storage with reasonable defaults
cstore := NewInMemory()
// CreateMnemonic a private-public key pair and ensure consistency
notPasswd := "n9y25ah7"
info, _, err := cstore.CreateMnemonic("john", English, notPasswd, Secp256k1)
require.Nil(t, err)
require.NotEqual(t, info, "")
require.Equal(t, info.GetName(), "john")
addr := info.GetPubKey().Address()
john, err := cstore.Get("john")
require.NoError(t, err)
require.Equal(t, john.GetName(), "john")
require.Equal(t, john.GetPubKey().Address(), addr)
// Export the public key only
armor, err := cstore.ExportPubKey("john")
require.NoError(t, err)
// Import it under a different name
err = cstore.ImportPubKey("john-pubkey-only", armor)
require.NoError(t, err)
// Ensure consistency
john2, err := cstore.Get("john-pubkey-only")
require.NoError(t, err)
// Compare the public keys
require.True(t, john.GetPubKey().Equals(john2.GetPubKey()))
// Ensure the original key hasn't changed
john, err = cstore.Get("john")
require.NoError(t, err)
require.Equal(t, john.GetPubKey().Address(), addr)
require.Equal(t, john.GetName(), "john")
// Ensure keys cannot be overwritten
err = cstore.ImportPubKey("john-pubkey-only", armor)
require.NotNil(t, err)
}
func TestInMemoryExportPrivateKeyObject(t *testing.T) {
kb := NewInMemory()
info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1)
require.NoError(t, err)
require.Equal(t, info.GetName(), "john")
// export private key object
_, err = kb.ExportPrivateKeyObject("john", "invalid")
require.NoError(t, err, "%+v", err)
exported, err := kb.ExportPrivateKeyObject("john", "secretcpw")
require.Nil(t, err, "%+v", err)
require.True(t, exported.PubKey().Equals(info.GetPubKey()))
}
// TestInMemoryAdvancedKeyManagement verifies update, import, export functionality
func TestInMemoryAdvancedKeyManagement(t *testing.T) {
// make the storage with reasonable defaults
cstore := NewInMemory()
algo := Secp256k1
n1, n2 := "old-name", "new name"
p1 := nums
// make sure key works with initial password
_, _, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err, "%+v", err)
// exporting requires the proper name and passphrase
_, err = cstore.Export(n1 + ".notreal")
require.NotNil(t, err)
_, err = cstore.Export(" " + n1)
require.NotNil(t, err)
_, err = cstore.Export(n1 + " ")
require.NotNil(t, err)
_, err = cstore.Export("")
require.NotNil(t, err)
exported, err := cstore.Export(n1)
require.Nil(t, err, "%+v", err)
// import succeeds
err = cstore.Import(n2, exported)
require.NoError(t, err)
// second import fails
err = cstore.Import(n2, exported)
require.NotNil(t, err)
}
// TestInMemorySeedPhrase verifies restoring from a seed phrase
func TestInMemorySeedPhrase(t *testing.T) {
// make the storage with reasonable defaults
cstore := NewInMemory()
algo := Secp256k1
n1, n2 := "lost-key", "found-again"
p1, p2 := nums, foobar
// make sure key works with initial password
info, mnemonic, err := cstore.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err, "%+v", err)
require.Equal(t, n1, info.GetName())
require.NotEmpty(t, mnemonic)
// now, let us delete this key
err = cstore.Delete(n1, p1, false)
require.Nil(t, err, "%+v", err)
_, err = cstore.Get(n1)
require.NotNil(t, err)
// let us re-create it from the mnemonic-phrase
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
hdPath := params.String()
newInfo, err := cstore.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, Secp256k1)
require.NoError(t, err)
require.Equal(t, n2, newInfo.GetName())
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
require.Equal(t, info.GetPubKey(), newInfo.GetPubKey())
}
func ExampleNew() {
// Select the encryption and storage for your cryptostore
customKeyGenFunc := func(bz []byte, algo SigningAlgo) (tmcrypto.PrivKey, error) {
var bzArr [32]byte
copy(bzArr[:], bz)
return secp256k1.PrivKeySecp256k1(bzArr), nil
}
cstore := NewInMemory(WithKeygenFunc(customKeyGenFunc))
sec := Secp256k1
// Add keys and see they return in alphabetical order
bob, _, err := cstore.CreateMnemonic("Bob", English, "friend", sec)
if err != nil {
// this should never happen
fmt.Println(err)
} else {
// return info here just like in List
fmt.Println(bob.GetName())
}
_, _, _ = cstore.CreateMnemonic("Alice", English, "secret", sec)
_, _, _ = cstore.CreateMnemonic("Carl", English, "mitm", sec)
info, _ := cstore.List()
for _, i := range info {
fmt.Println(i.GetName())
}
// We need to use passphrase to generate a signature
tx := []byte("deadbeef")
sig, pub, err := cstore.Sign("Bob", "friend", tx)
if err != nil {
fmt.Println("don't accept real passphrase")
}
// and we can validate the signature with publicly available info
binfo, _ := cstore.Get("Bob")
if !binfo.GetPubKey().Equals(bob.GetPubKey()) {
fmt.Println("Get and Create return different keys")
}
if pub.Equals(binfo.GetPubKey()) {
fmt.Println("signed by Bob")
}
if !pub.VerifyBytes(tx, sig) {
fmt.Println("invalid signature")
}
// Output:
// Bob
// Alice
// Bob
// Carl
// signed by Bob
}
func accAddr(info Info) sdk.AccAddress {
return (sdk.AccAddress)(info.GetPubKey().Address())
}
var _ tmcrypto.PrivKey = testPriv{}
var _ tmcrypto.PubKey = testPub{}
var testCdc *amino.Codec
type testPriv []byte
func (privkey testPriv) PubKey() tmcrypto.PubKey { return testPub{} }
func (privkey testPriv) Bytes() []byte {
return testCdc.MustMarshalBinaryBare(privkey)
}
func (privkey testPriv) Sign(msg []byte) ([]byte, error) { return []byte{}, nil }
func (privkey testPriv) Equals(other tmcrypto.PrivKey) bool { return true }
type testPub []byte
func (key testPub) Address() tmcrypto.Address { return tmcrypto.Address{} }
func (key testPub) Bytes() []byte {
return testCdc.MustMarshalBinaryBare(key)
}
func (key testPub) VerifyBytes(msg []byte, sig []byte) bool { return true }
func (key testPub) Equals(other tmcrypto.PubKey) bool { return true }
func TestInMemoryKeygenOverride(t *testing.T) {
// Save existing codec and reset after test
cryptoCdc := CryptoCdc
t.Cleanup(func() {
CryptoCdc = cryptoCdc
})
// Setup testCdc encoding and decoding new key type
testCdc = codec.New()
RegisterCodec(testCdc)
tmamino.RegisterAmino(testCdc)
// Set up codecs for using new key types
privName, pubName := "test/priv_name", "test/pub_name"
tmamino.RegisterKeyType(testPriv{}, privName)
tmamino.RegisterKeyType(testPub{}, pubName)
testCdc.RegisterConcrete(testPriv{}, privName, nil)
testCdc.RegisterConcrete(testPub{}, pubName, nil)
CryptoCdc = testCdc
overrideCalled := false
dummyFunc := func(bz []byte, algo SigningAlgo) (tmcrypto.PrivKey, error) {
overrideCalled = true
return testPriv(bz), nil
}
kb := NewInMemory(WithKeygenFunc(dummyFunc))
testName, pw := "name", "testPassword"
// create new key which will generate with
info, _, err := kb.CreateMnemonic(testName, English, pw, Secp256k1)
require.NoError(t, err)
require.Equal(t, info.GetName(), testName)
// Assert overridden function was called
require.True(t, overrideCalled)
// export private key object
exported, err := kb.ExportPrivateKeyObject(testName, pw)
require.Nil(t, err, "%+v", err)
// require that the key type is the new key
_, ok := exported.(testPriv)
require.True(t, ok)
require.True(t, exported.PubKey().Equals(info.GetPubKey()))
}

View File

@ -1,221 +0,0 @@
package keyring
import (
"fmt"
"github.com/tendermint/tendermint/crypto"
tmos "github.com/tendermint/tendermint/libs/os"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var _ Keybase = lazyKeybase{}
// NOTE: lazyKeybase will be deprecated in favor of lazyKeybaseKeyring.
type lazyKeybase struct {
name string
dir string
options []KeybaseOption
}
// New creates a new instance of a lazy keybase.
func New(name, dir string, opts ...KeybaseOption) Keybase {
if err := tmos.EnsureDir(dir, 0700); err != nil {
panic(fmt.Sprintf("failed to create Keybase directory: %s", err))
}
return lazyKeybase{name: name, dir: dir, options: opts}
}
func (lkb lazyKeybase) List() ([]Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).List()
}
func (lkb lazyKeybase) Get(name string) (Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).Get(name)
}
func (lkb lazyKeybase) GetByAddress(address sdk.AccAddress) (Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).GetByAddress(address)
}
func (lkb lazyKeybase) Delete(name, passphrase string, skipPass bool) error {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).Delete(name, passphrase, skipPass)
}
func (lkb lazyKeybase) Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, nil, err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).Sign(name, passphrase, msg)
}
func (lkb lazyKeybase) CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, "", err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).CreateMnemonic(name, language, passwd, algo)
}
func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo) (Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDBKeybase(db,
lkb.options...).CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
}
func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).CreateLedger(name, algo, hrp, account, index)
}
func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey, algo SigningAlgo) (info Info, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).CreateOffline(name, pubkey, algo)
}
func (lkb lazyKeybase) CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).CreateMulti(name, pubkey)
}
func (lkb lazyKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).Update(name, oldpass, getNewpass)
}
func (lkb lazyKeybase) Import(name string, armor string) (err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).Import(name, armor)
}
func (lkb lazyKeybase) ImportPrivKey(name string, armor string, passphrase string) error {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).ImportPrivKey(name, armor, passphrase)
}
func (lkb lazyKeybase) ImportPubKey(name string, armor string) (err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).ImportPubKey(name, armor)
}
func (lkb lazyKeybase) Export(name string) (armor string, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return "", err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).Export(name)
}
func (lkb lazyKeybase) ExportPubKey(name string) (armor string, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return "", err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).ExportPubKey(name)
}
func (lkb lazyKeybase) ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).ExportPrivateKeyObject(name, passphrase)
}
func (lkb lazyKeybase) ExportPrivKey(name string, decryptPassphrase string,
encryptPassphrase string) (armor string, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return "", err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).ExportPrivKey(name, decryptPassphrase, encryptPassphrase)
}
// SupportedAlgos returns a list of supported signing algorithms.
func (lkb lazyKeybase) SupportedAlgos() []SigningAlgo {
return newBaseKeybase(lkb.options...).SupportedAlgos()
}
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
func (lkb lazyKeybase) SupportedAlgosLedger() []SigningAlgo {
return newBaseKeybase(lkb.options...).SupportedAlgosLedger()
}

View File

@ -1,453 +0,0 @@
package keyring
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestNew(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb := New("keybasename", dir)
lazykb, ok := kb.(lazyKeybase)
require.True(t, ok)
require.Equal(t, lazykb.name, "keybasename")
require.Equal(t, lazykb.dir, dir)
}
func TestLazyKeyManagement(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb := New("keybasename", dir)
algo := Secp256k1
n1, n2, n3 := "personal", "business", "other"
p1, p2 := nums, "really-secure!@#$"
// Check empty state
l, err := kb.List()
require.Nil(t, err)
assert.Empty(t, l)
_, _, err = kb.CreateMnemonic(n1, English, p1, Ed25519)
require.Error(t, err, "ed25519 keys are currently not supported by keybase")
// create some keys
_, err = kb.Get(n1)
require.Error(t, err)
i, _, err := kb.CreateMnemonic(n1, English, p1, algo)
require.NoError(t, err)
require.Equal(t, n1, i.GetName())
_, _, err = kb.CreateMnemonic(n2, English, p2, algo)
require.NoError(t, err)
// we can get these keys
i2, err := kb.Get(n2)
require.NoError(t, err)
_, err = kb.Get(n3)
require.NotNil(t, err)
_, err = kb.GetByAddress(accAddr(i2))
require.NoError(t, err)
addr, err := sdk.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t")
require.NoError(t, err)
_, err = kb.GetByAddress(addr)
require.NotNil(t, err)
// list shows them in order
keyS, err := kb.List()
require.NoError(t, err)
require.Equal(t, 2, len(keyS))
// note these are in alphabetical order
require.Equal(t, n2, keyS[0].GetName())
require.Equal(t, n1, keyS[1].GetName())
require.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey())
// deleting a key removes it
err = kb.Delete("bad name", "foo", false)
require.NotNil(t, err)
err = kb.Delete(n1, p1, false)
require.NoError(t, err)
keyS, err = kb.List()
require.NoError(t, err)
require.Equal(t, 1, len(keyS))
_, err = kb.Get(n1)
require.Error(t, err)
// create an offline key
o1 := "offline"
priv1 := ed25519.GenPrivKey()
pub1 := priv1.PubKey()
i, err = kb.CreateOffline(o1, pub1, algo)
require.Nil(t, err)
require.Equal(t, pub1, i.GetPubKey())
require.Equal(t, o1, i.GetName())
keyS, err = kb.List()
require.NoError(t, err)
require.Equal(t, 2, len(keyS))
// delete the offline key
err = kb.Delete(o1, "", false)
require.NoError(t, err)
keyS, err = kb.List()
require.NoError(t, err)
require.Equal(t, 1, len(keyS))
// addr cache gets nuked - and test skip flag
err = kb.Delete(n2, "", true)
require.NoError(t, err)
}
func TestLazySignVerify(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb := New("keybasename", dir)
algo := Secp256k1
n1, n2, n3 := "some dude", "a dudette", "dude-ish"
p1, p2, p3 := nums, foobar, foobar
// create two users and get their info
i1, _, err := kb.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err)
i2, _, err := kb.CreateMnemonic(n2, English, p2, algo)
require.Nil(t, err)
// Import a public key
armor, err := kb.ExportPubKey(n2)
require.Nil(t, err)
err = kb.ImportPubKey(n3, armor)
require.NoError(t, err)
i3, err := kb.Get(n3)
require.NoError(t, err)
require.Equal(t, i3.GetName(), n3)
// let's try to sign some messages
d1 := []byte("my first message")
d2 := []byte("some other important info!")
d3 := []byte("feels like I forgot something...")
// try signing both data with both ..
s11, pub1, err := kb.Sign(n1, p1, d1)
require.Nil(t, err)
require.Equal(t, i1.GetPubKey(), pub1)
s12, pub1, err := kb.Sign(n1, p1, d2)
require.Nil(t, err)
require.Equal(t, i1.GetPubKey(), pub1)
s21, pub2, err := kb.Sign(n2, p2, d1)
require.Nil(t, err)
require.Equal(t, i2.GetPubKey(), pub2)
s22, pub2, err := kb.Sign(n2, p2, d2)
require.Nil(t, err)
require.Equal(t, i2.GetPubKey(), pub2)
// let's try to validate and make sure it only works when everything is proper
cases := []struct {
key crypto.PubKey
data []byte
sig []byte
valid bool
}{
// proper matches
{i1.GetPubKey(), d1, s11, true},
// change data, pubkey, or signature leads to fail
{i1.GetPubKey(), d2, s11, false},
{i2.GetPubKey(), d1, s11, false},
{i1.GetPubKey(), d1, s21, false},
// make sure other successes
{i1.GetPubKey(), d2, s12, true},
{i2.GetPubKey(), d1, s21, true},
{i2.GetPubKey(), d2, s22, true},
}
for i, tc := range cases {
valid := tc.key.VerifyBytes(tc.data, tc.sig)
require.Equal(t, tc.valid, valid, "%d", i)
}
// Now try to sign data with a secret-less key
_, _, err = kb.Sign(n3, p3, d3)
require.NotNil(t, err)
}
func TestLazyExportImport(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb := New("keybasename", dir)
info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1)
require.NoError(t, err)
require.Equal(t, info.GetName(), "john")
john, err := kb.Get("john")
require.NoError(t, err)
require.Equal(t, info.GetName(), "john")
johnAddr := info.GetPubKey().Address()
armor, err := kb.Export("john")
require.NoError(t, err)
err = kb.Import("john2", armor)
require.NoError(t, err)
john2, err := kb.Get("john2")
require.NoError(t, err)
require.Equal(t, john.GetPubKey().Address(), johnAddr)
require.Equal(t, john.GetName(), "john")
require.Equal(t, john, john2)
}
func TestLazyExportImportPrivKey(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb := New("keybasename", dir)
info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1)
require.NoError(t, err)
require.Equal(t, info.GetName(), "john")
priv1, err := kb.Get("john")
require.NoError(t, err)
// decrypt local private key, and produce encrypted ASCII armored output
armored, err := kb.ExportPrivKey("john", "secretcpw", "new_secretcpw")
require.NoError(t, err)
// delete exported key
require.NoError(t, kb.Delete("john", "", true))
_, err = kb.Get("john")
require.Error(t, err)
// import armored key
require.NoError(t, kb.ImportPrivKey("john", armored, "new_secretcpw"))
// ensure old and new keys match
priv2, err := kb.Get("john")
require.NoError(t, err)
require.True(t, priv1.GetPubKey().Equals(priv2.GetPubKey()))
}
func TestLazyExportImportPubKey(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb := New("keybasename", dir)
algo := Secp256k1
// CreateMnemonic a private-public key pair and ensure consistency
notPasswd := "n9y25ah7"
info, _, err := kb.CreateMnemonic("john", English, notPasswd, algo)
require.Nil(t, err)
require.NotEqual(t, info, "")
require.Equal(t, info.GetName(), "john")
addr := info.GetPubKey().Address()
john, err := kb.Get("john")
require.NoError(t, err)
require.Equal(t, john.GetName(), "john")
require.Equal(t, john.GetPubKey().Address(), addr)
// Export the public key only
armor, err := kb.ExportPubKey("john")
require.NoError(t, err)
// Import it under a different name
err = kb.ImportPubKey("john-pubkey-only", armor)
require.NoError(t, err)
// Ensure consistency
john2, err := kb.Get("john-pubkey-only")
require.NoError(t, err)
// Compare the public keys
require.True(t, john.GetPubKey().Equals(john2.GetPubKey()))
// Ensure the original key hasn't changed
john, err = kb.Get("john")
require.NoError(t, err)
require.Equal(t, john.GetPubKey().Address(), addr)
require.Equal(t, john.GetName(), "john")
// Ensure keys cannot be overwritten
err = kb.ImportPubKey("john-pubkey-only", armor)
require.NotNil(t, err)
}
func TestLazyExportPrivateKeyObject(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb := New("keybasename", dir)
info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1)
require.NoError(t, err)
require.Equal(t, info.GetName(), "john")
// export private key object
_, err = kb.ExportPrivateKeyObject("john", "invalid")
require.NotNil(t, err, "%+v", err)
exported, err := kb.ExportPrivateKeyObject("john", "secretcpw")
require.Nil(t, err, "%+v", err)
require.True(t, exported.PubKey().Equals(info.GetPubKey()))
}
func TestLazyAdvancedKeyManagement(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb := New("keybasename", dir)
algo := Secp256k1
n1, n2 := "old-name", "new name"
p1, p2 := nums, foobar
// make sure key works with initial password
_, _, err := kb.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err, "%+v", err)
assertPassword(t, kb, n1, p1, p2)
// update password requires the existing password
getNewpass := func() (string, error) { return p2, nil }
err = kb.Update(n1, "jkkgkg", getNewpass)
require.NotNil(t, err)
assertPassword(t, kb, n1, p1, p2)
// then it changes the password when correct
err = kb.Update(n1, p1, getNewpass)
require.NoError(t, err)
// p2 is now the proper one!
assertPassword(t, kb, n1, p2, p1)
// exporting requires the proper name and passphrase
_, err = kb.Export(n1 + ".notreal")
require.NotNil(t, err)
_, err = kb.Export(" " + n1)
require.NotNil(t, err)
_, err = kb.Export(n1 + " ")
require.NotNil(t, err)
_, err = kb.Export("")
require.NotNil(t, err)
exported, err := kb.Export(n1)
require.Nil(t, err, "%+v", err)
// import succeeds
err = kb.Import(n2, exported)
require.NoError(t, err)
// second import fails
err = kb.Import(n2, exported)
require.NotNil(t, err)
}
// TestSeedPhrase verifies restoring from a seed phrase
func TestLazySeedPhrase(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb := New("keybasename", dir)
algo := Secp256k1
n1, n2 := "lost-key", "found-again"
p1, p2 := nums, foobar
// make sure key works with initial password
info, mnemonic, err := kb.CreateMnemonic(n1, English, p1, algo)
require.Nil(t, err, "%+v", err)
require.Equal(t, n1, info.GetName())
assert.NotEmpty(t, mnemonic)
// now, let us delete this key
err = kb.Delete(n1, p1, false)
require.Nil(t, err, "%+v", err)
_, err = kb.Get(n1)
require.NotNil(t, err)
// let us re-create it from the mnemonic-phrase
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
hdPath := params.String()
newInfo, err := kb.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, algo)
require.NoError(t, err)
require.Equal(t, n2, newInfo.GetName())
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
require.Equal(t, info.GetPubKey(), newInfo.GetPubKey())
}
var _ crypto.PrivKey = testPriv{}
var _ crypto.PubKey = testPub{}
var testCdc *amino.Codec
type testPriv []byte
func (privkey testPriv) PubKey() crypto.PubKey { return testPub{} }
func (privkey testPriv) Bytes() []byte {
return testCdc.MustMarshalBinaryBare(privkey)
}
func (privkey testPriv) Sign(msg []byte) ([]byte, error) { return []byte{}, nil }
func (privkey testPriv) Equals(other crypto.PrivKey) bool { return true }
type testPub []byte
func (key testPub) Address() crypto.Address { return crypto.Address{} }
func (key testPub) Bytes() []byte {
return testCdc.MustMarshalBinaryBare(key)
}
func (key testPub) VerifyBytes(msg []byte, sig []byte) bool { return true }
func (key testPub) Equals(other crypto.PubKey) bool { return true }
func TestKeygenOverride(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
// Save existing codec and reset after test
cryptoCdc := CryptoCdc
t.Cleanup(func() {
CryptoCdc = cryptoCdc
})
// Setup testCdc encoding and decoding new key type
testCdc = codec.New()
RegisterCodec(testCdc)
tmamino.RegisterAmino(testCdc)
// Set up codecs for using new key types
privName, pubName := "test/priv_name", "test/pub_name"
tmamino.RegisterKeyType(testPriv{}, privName)
tmamino.RegisterKeyType(testPub{}, pubName)
testCdc.RegisterConcrete(testPriv{}, privName, nil)
testCdc.RegisterConcrete(testPub{}, pubName, nil)
CryptoCdc = testCdc
overrideCalled := false
dummyFunc := func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error) {
overrideCalled = true
return testPriv(bz), nil
}
kb := New("keybasename", dir, WithKeygenFunc(dummyFunc))
testName, pw := "name", "testPassword"
// create new key which will generate with
info, _, err := kb.CreateMnemonic(testName, English, pw, Secp256k1)
require.NoError(t, err)
require.Equal(t, info.GetName(), testName)
// Assert overridden function was called
require.True(t, overrideCalled)
// export private key object
exported, err := kb.ExportPrivateKeyObject(testName, pw)
require.Nil(t, err, "%+v", err)
// require that the key type is the new key
_, ok := exported.(testPriv)
require.True(t, ok)
require.True(t, exported.PubKey().Equals(info.GetPubKey()))
}

188
crypto/keyring/legacy.go Normal file
View File

@ -0,0 +1,188 @@
package keyring
import (
"fmt"
"strings"
"github.com/pkg/errors"
tmcrypto "github.com/tendermint/tendermint/crypto"
tmos "github.com/tendermint/tendermint/libs/os"
dbm "github.com/tendermint/tm-db"
"github.com/cosmos/cosmos-sdk/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// LegacyKeybase is implemented by the legacy keybase implementation.
type LegacyKeybase interface {
List() ([]Info, error)
Export(name string) (armor string, err error)
ExportPrivKey(name, decryptPassphrase, encryptPassphrase string) (armor string, err error)
ExportPubKey(name string) (armor string, err error)
Close() error
}
// NewLegacy creates a new instance of a legacy keybase.
func NewLegacy(name, dir string, opts ...KeybaseOption) (LegacyKeybase, error) {
if err := tmos.EnsureDir(dir, 0700); err != nil {
return nil, fmt.Errorf("failed to create Keybase directory: %s", err)
}
db, err := sdk.NewLevelDB(name, dir)
if err != nil {
return nil, err
}
return newDBKeybase(db, opts...), nil
}
var _ LegacyKeybase = dbKeybase{}
// dbKeybase combines encryption and storage implementation to provide a
// full-featured key manager.
//
// NOTE: dbKeybase will be deprecated in favor of keyringKeybase.
type dbKeybase struct {
db dbm.DB
}
// newDBKeybase creates a new dbKeybase instance using the provided DB for
// reading and writing keys.
func newDBKeybase(db dbm.DB, opts ...KeybaseOption) dbKeybase {
return dbKeybase{
db: db,
}
}
// List returns the keys from storage in alphabetical order.
func (kb dbKeybase) List() ([]Info, error) {
var res []Info
iter, err := kb.db.Iterator(nil, nil)
if err != nil {
return nil, err
}
defer iter.Close()
for ; iter.Valid(); iter.Next() {
key := string(iter.Key())
// need to include only keys in storage that have an info suffix
if strings.HasSuffix(key, infoSuffix) {
info, err := unmarshalInfo(iter.Value())
if err != nil {
return nil, err
}
res = append(res, info)
}
}
return res, nil
}
// Get returns the public information about one key.
func (kb dbKeybase) Get(name string) (Info, error) {
bs, err := kb.db.Get(infoKey(name))
if err != nil {
return nil, err
}
if len(bs) == 0 {
return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, name)
}
return unmarshalInfo(bs)
}
// ExportPrivateKeyObject returns a PrivKey object given the key name and
// passphrase. An error is returned if the key does not exist or if the Info for
// the key is invalid.
func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcrypto.PrivKey, error) {
info, err := kb.Get(name)
if err != nil {
return nil, err
}
var priv tmcrypto.PrivKey
switch i := info.(type) {
case localInfo:
linfo := i
if linfo.PrivKeyArmor == "" {
err = fmt.Errorf("private key not available")
return nil, err
}
priv, _, err = crypto.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return nil, err
}
case ledgerInfo, offlineInfo, multiInfo:
return nil, errors.New("only works on local private keys")
}
return priv, nil
}
func (kb dbKeybase) Export(name string) (armor string, err error) {
bz, err := kb.db.Get(infoKey(name))
if err != nil {
return "", err
}
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
return crypto.ArmorInfoBytes(bz), nil
}
// ExportPubKey returns public keys in ASCII armored format. It retrieves a Info
// object by its name and return the public key in a portable format.
func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
bz, err := kb.db.Get(infoKey(name))
if err != nil {
return "", err
}
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
info, err := unmarshalInfo(bz)
if err != nil {
return
}
return crypto.ArmorPubKeyBytes(info.GetPubKey().Bytes(), string(info.GetAlgo())), nil
}
// ExportPrivKey returns a private key in ASCII armored format.
// It returns an error if the key does not exist or a wrong encryption passphrase
// is supplied.
func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string,
encryptPassphrase string) (armor string, err error) {
priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase)
if err != nil {
return "", err
}
info, err := kb.Get(name)
if err != nil {
return "", err
}
return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
}
// Close the underlying storage.
func (kb dbKeybase) Close() error {
return kb.db.Close()
}
func infoKey(name string) []byte {
return []byte(fmt.Sprintf("%s.%s", name, infoSuffix))
}

View File

@ -0,0 +1,44 @@
package keyring_test
import (
"path/filepath"
"testing"
"github.com/otiai10/copy"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/tests"
)
func TestNewLegacyKeyBase(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
kb, err := keyring.NewLegacy("keybasename", dir)
require.NoError(t, err)
require.NoError(t, kb.Close())
}
func TestLegacyKeybase(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t)
t.Cleanup(cleanup)
// Backup testdata
require.NoError(t, copy.Copy("testdata", dir))
kb, err := keyring.NewLegacy("keys", filepath.Join(dir, "keys"))
require.NoError(t, err)
t.Cleanup(func() { kb.Close() })
keys, err := kb.List()
require.NoError(t, err)
require.Equal(t, 2, len(keys))
armor, err := kb.ExportPubKey(keys[0].GetName())
require.NoError(t, err)
require.NotEmpty(t, armor)
armoredInfo, err := kb.Export(keys[0].GetName())
require.NoError(t, err)
require.NotEmpty(t, armoredInfo)
}

Binary file not shown.

View File

@ -0,0 +1 @@
MANIFEST-000004

View File

@ -0,0 +1 @@
MANIFEST-000000

View File

View File

@ -0,0 +1,18 @@
=============== Mar 30, 2020 (CEST) ===============
02:07:34.137606 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
02:07:34.144547 db@open opening
02:07:34.144770 version@stat F·[] S·0B[] Sc·[]
02:07:34.145843 db@janitor F·2 G·0
02:07:34.145875 db@open done T·1.315251ms
02:07:34.335635 db@close closing
02:07:34.335736 db@close done T·98.95µs
=============== Mar 30, 2020 (CEST) ===============
02:08:33.239115 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
02:08:33.239264 version@stat F·[] S·0B[] Sc·[]
02:08:33.239281 db@open opening
02:08:33.239310 journal@recovery F·1
02:08:33.239398 journal@recovery recovering @1
02:08:33.322008 memdb@flush created L0@2 N·4 S·391B "cos..ess,v4":"run..nfo,v3"
02:08:33.323091 version@stat F·[1] S·391B[391B] Sc·[0.25]
02:08:33.421979 db@janitor F·3 G·0
02:08:33.422153 db@open done T·182.707962ms

Binary file not shown.

1
go.mod
View File

@ -14,6 +14,7 @@ require (
github.com/gorilla/mux v1.7.4
github.com/hashicorp/golang-lru v0.5.4
github.com/mattn/go-isatty v0.0.12
github.com/otiai10/copy v1.1.1
github.com/pelletier/go-toml v1.6.0
github.com/pkg/errors v0.9.1
github.com/rakyll/statik v0.1.7

8
go.sum
View File

@ -297,6 +297,14 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/otiai10/copy v1.1.1 h1:PH7IFlRQ6Fv9vYmuXbDRLdgTHoP1w483kPNUP2bskpo=
github.com/otiai10/copy v1.1.1/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc=
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=

View File

@ -5,7 +5,6 @@ import (
"github.com/cosmos/cosmos-sdk/crypto/keyring"
clkeys "github.com/cosmos/cosmos-sdk/client/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -14,7 +13,7 @@ import (
func GenerateCoinKey() (sdk.AccAddress, string, error) {
// generate a private key, with recovery phrase
info, secret, err := clkeys.NewInMemoryKeyBase().CreateMnemonic(
info, secret, err := keyring.NewInMemory().CreateMnemonic(
"name", keyring.English, "pass", keyring.Secp256k1)
if err != nil {
return sdk.AccAddress([]byte{}), "", err

View File

@ -5,7 +5,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/tests"
@ -17,7 +16,7 @@ func TestGenerateCoinKey(t *testing.T) {
require.NoError(t, err)
// Test creation
info, err := keys.NewInMemoryKeyBase().CreateAccount("xxx", mnemonic, "", "012345678", keyring.CreateHDPath(0, 0).String(), keyring.Secp256k1)
info, err := keyring.NewInMemory().CreateAccount("xxx", mnemonic, "", "012345678", keyring.CreateHDPath(0, 0).String(), keyring.Secp256k1)
require.NoError(t, err)
require.Equal(t, addr, info.GetAddress())
}
@ -39,7 +38,7 @@ func TestGenerateSaveCoinKey(t *testing.T) {
require.Equal(t, addr, info.GetAddress())
// Test in-memory recovery
info, err = keys.NewInMemoryKeyBase().CreateAccount("xxx", mnemonic, "", "012345678", keyring.CreateHDPath(0, 0).String(), keyring.Secp256k1)
info, err = keyring.NewInMemory().CreateAccount("xxx", mnemonic, "", "012345678", keyring.CreateHDPath(0, 0).String(), keyring.Secp256k1)
require.NoError(t, err)
require.Equal(t, addr, info.GetAddress())
}