Skip to content

Commit

Permalink
Interchangable PrivKey implementations in keybase (cosmos#5278)
Browse files Browse the repository at this point in the history
Allow for the keybase to be configured to override the implementation
of the key that is saved to the keybase.

Closes: cosmos#4941
  • Loading branch information
austinabell authored and xiangjianmeng committed Dec 18, 2019
1 parent bf80cbc commit aa9b163
Show file tree
Hide file tree
Showing 32 changed files with 273 additions and 118 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ if the provided arguments are invalid.
`StdTx.Signatures` to get back the array of StdSignatures `[]StdSignature`.
* (modules) [\#5299](https://github.com/cosmos/cosmos-sdk/pull/5299) `HandleDoubleSign` along with params `MaxEvidenceAge`
and `DoubleSignJailEndTime` have moved from the `x/slashing` module to the `x/evidence` module.
* (keys) [\#4941](https://github.com/cosmos/cosmos-sdk/issues/4941) Initializing a new keybase through `NewKeyringFromHomeFlag`, `NewKeyringFromDir`, `NewKeyBaseFromHomeFlag`, `NewKeyBaseFromDir`, or `NewInMemory` functions now accept optional parameters of type `KeybaseOption`. These optional parameters are also added on the keys subcommands functions, which are now public, and allows these options to be set on the commands or ignored to default to previous behavior.
* The option introduced in this PR is `WithKeygenFunc` which allows a custom bytes to key implementation to be defined when keys are created.

### Client Breaking Changes

Expand Down Expand Up @@ -146,6 +148,7 @@ that allows for arbitrary vesting periods.
* `IncrementSequenceDecorator`: Increments the account sequence for each signer to prevent replay attacks.
* (cli) [\#5223](https://github.com/cosmos/cosmos-sdk/issues/5223) Cosmos Ledger App v2.0.0 is now supported. The changes are backwards compatible and App v1.5.x is still supported.
* (modules) [\#5249](https://github.com/cosmos/cosmos-sdk/pull/5249) Funds are now allowed to be directly sent to the community pool (via the distribution module account).
* (keys) [\#4941](https://github.com/cosmos/cosmos-sdk/issues/4941) Introduce keybase option to allow overriding the default private key implementation of a key generated through the `keys add` cli command.

### Improvements

Expand Down
41 changes: 25 additions & 16 deletions client/keys/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"errors"
"fmt"
"io"
"sort"

bip39 "github.com/bartekn/go-bip39"
Expand Down Expand Up @@ -36,7 +37,8 @@ const (
DefaultKeyPass = "12345678"
)

func addKeyCommand() *cobra.Command {
// AddKeyCommand defines a keys command to add a generated or recovered private key to keybase.
func AddKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "add <name>",
Short: "Add an encrypted private key (either newly generated or recovered), encrypt it, and save to disk",
Expand Down Expand Up @@ -75,6 +77,24 @@ the flag --nosort is set.
return cmd
}

func getKeybase(transient bool, buf io.Reader) (keys.Keybase, error) {
if transient {
return keys.NewInMemory(), nil
}

return NewKeyringFromHomeFlag(buf)
}

func runAddCmd(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
kb, err := getKeybase(viper.GetBool(flagDryRun), inBuf)
if err != nil {
return err
}

return RunAddCmd(cmd, args, kb, inBuf)
}

/*
input
- bip39 mnemonic
Expand All @@ -84,26 +104,15 @@ input
output
- armor encrypted private key (saved to file)
*/
func runAddCmd(cmd *cobra.Command, args []string) error {
var kb keys.Keybase
func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.Reader) error {
var err error

inBuf := bufio.NewReader(cmd.InOrStdin())
name := args[0]

interactive := viper.GetBool(flagInteractive)
showMnemonic := !viper.GetBool(flagNoBackup)

if viper.GetBool(flagDryRun) {
// we throw this away, so don't enforce args,
// we want to get a new random seed phrase quickly
kb = keys.NewInMemory()
} else {
kb, err = NewKeyringFromHomeFlag(cmd.InOrStdin())
if err != nil {
return err
}

if !viper.GetBool(flagDryRun) {
_, err = kb.Get(name)
if err == nil {
// account exists, ask for user confirmation
Expand Down Expand Up @@ -273,9 +282,9 @@ func printCreate(cmd *cobra.Command, info keys.Info, showMnemonic bool, mnemonic

var jsonString []byte
if viper.GetBool(flags.FlagIndentResponse) {
jsonString, err = cdc.MarshalJSONIndent(out, "", " ")
jsonString, err = KeysCdc.MarshalJSONIndent(out, "", " ")
} else {
jsonString, err = cdc.MarshalJSON(out)
jsonString, err = KeysCdc.MarshalJSON(out)
}

if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions client/keys/add_ledger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
config.SetBech32PrefixForValidator(bech32PrefixValAddr, bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(bech32PrefixConsAddr, bech32PrefixConsPub)

cmd := addKeyCommand()
cmd := AddKeyCommand()
require.NotNil(t, cmd)

// Prepare a keybase
Expand Down Expand Up @@ -80,7 +80,7 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {

func Test_runAddCmdLedger(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := addKeyCommand()
cmd := AddKeyCommand()
require.NotNil(t, cmd)
mockIn, _, _ := tests.ApplyMockIO(cmd)

Expand Down
2 changes: 1 addition & 1 deletion client/keys/add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

func Test_runAddCmdBasic(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := addKeyCommand()
cmd := AddKeyCommand()
assert.NotNil(t, cmd)
mockIn, _, _ := tests.ApplyMockIO(cmd)

Expand Down
13 changes: 7 additions & 6 deletions client/keys/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
)

var cdc *codec.Codec
// KeysCdc defines codec to be used with key operations
var KeysCdc *codec.Codec

func init() {
cdc = codec.New()
codec.RegisterCrypto(cdc)
cdc.Seal()
KeysCdc = codec.New()
codec.RegisterCrypto(KeysCdc)
KeysCdc.Seal()
}

// marshal keys
func MarshalJSON(o interface{}) ([]byte, error) {
return cdc.MarshalJSON(o)
return KeysCdc.MarshalJSON(o)
}

// unmarshal json
func UnmarshalJSON(bz []byte, ptr interface{}) error {
return cdc.UnmarshalJSON(bz, ptr)
return KeysCdc.UnmarshalJSON(bz, ptr)
}
3 changes: 2 additions & 1 deletion client/keys/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const (
flagForce = "force"
)

func deleteKeyCommand() *cobra.Command {
// DeleteKeyCommand deletes a key from the key store.
func DeleteKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "delete <name>...",
Short: "Delete the given keys",
Expand Down
2 changes: 1 addition & 1 deletion client/keys/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

func Test_runDeleteCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
deleteKeyCommand := deleteKeyCommand()
deleteKeyCommand := DeleteKeyCommand()
mockIn, _, _ := tests.ApplyMockIO(deleteKeyCommand)

yesF, _ := deleteKeyCommand.Flags().GetBool(flagYes)
Expand Down
3 changes: 2 additions & 1 deletion client/keys/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
"github.com/cosmos/cosmos-sdk/client/input"
)

func exportKeyCommand() *cobra.Command {
// ExportKeyCommand exports private keys from the key store.
func ExportKeyCommand() *cobra.Command {
return &cobra.Command{
Use: "export <name>",
Short: "Export private keys",
Expand Down
2 changes: 1 addition & 1 deletion client/keys/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

func Test_runExportCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
exportKeyCommand := exportKeyCommand()
exportKeyCommand := ExportKeyCommand()
mockIn, _, _ := tests.ApplyMockIO(exportKeyCommand)

// Now add a temporary keybase
Expand Down
3 changes: 2 additions & 1 deletion client/keys/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
"github.com/cosmos/cosmos-sdk/client/input"
)

func importKeyCommand() *cobra.Command {
// ImportKeyCommand imports private keys from a keyfile.
func ImportKeyCommand() *cobra.Command {
return &cobra.Command{
Use: "import <name> <keyfile>",
Short: "Import private keys into the local keybase",
Expand Down
2 changes: 1 addition & 1 deletion client/keys/import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

func Test_runImportCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
importKeyCommand := importKeyCommand()
importKeyCommand := ImportKeyCommand()
mockIn, _, _ := tests.ApplyMockIO(importKeyCommand)

// Now add a temporary keybase
Expand Down
3 changes: 2 additions & 1 deletion client/keys/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (

const flagListNames = "list-names"

func listKeysCmd() *cobra.Command {
// ListKeysCmd lists all keys in the key store.
func ListKeysCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List all keys",
Expand Down
2 changes: 1 addition & 1 deletion client/keys/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func Test_runListCmd(t *testing.T) {
args []string
}

cmdBasic := listKeysCmd()
cmdBasic := ListKeysCmd()

// Prepare some keybases
kbHome1, cleanUp1 := tests.NewTestCaseDir(t)
Expand Down
3 changes: 2 additions & 1 deletion client/keys/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import (
// is not needed for importing into the Keyring keystore.
const migratePassphrase = "NOOP_PASSPHRASE"

func migrateCommand() *cobra.Command {
// MigrateCommand migrates key information from legacy keybase to OS secret store.
func MigrateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "migrate",
Short: "Migrate key information from the lagacy key database to the OS secret store, or encrypted file store as a fall-back and save it",
Expand Down
4 changes: 2 additions & 2 deletions client/keys/migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

func Test_runMigrateCmd(t *testing.T) {
cmd := addKeyCommand()
cmd := AddKeyCommand()
assert.NotNil(t, cmd)
mockIn, _, _ := tests.ApplyMockIO(cmd)

Expand All @@ -29,7 +29,7 @@ func Test_runMigrateCmd(t *testing.T) {
assert.NoError(t, err)

viper.Set(flags.FlagDryRun, true)
cmd = migrateCommand()
cmd = MigrateCommand()
mockIn, _, _ = tests.ApplyMockIO(cmd)
mockIn.Reset("test1234\n")
assert.NoError(t, runMigrateCmd(cmd, []string{}))
Expand Down
3 changes: 2 additions & 1 deletion client/keys/mnemonic.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ const (
mnemonicEntropySize = 256
)

func mnemonicKeyCommand() *cobra.Command {
// MnemonicKeyCommand computes the bip39 memonic for input entropy.
func MnemonicKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "mnemonic",
Short: "Compute the bip39 mnemonic for some input entropy",
Expand Down
4 changes: 2 additions & 2 deletions client/keys/mnemonic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import (
)

func Test_RunMnemonicCmdNormal(t *testing.T) {
cmdBasic := mnemonicKeyCommand()
cmdBasic := MnemonicKeyCommand()
err := runMnemonicCmd(cmdBasic, []string{})
require.NoError(t, err)
}

func Test_RunMnemonicCmdUser(t *testing.T) {
cmdUser := mnemonicKeyCommand()
cmdUser := MnemonicKeyCommand()
err := cmdUser.Flags().Set(flagUserEntropy, "1")
assert.NoError(t, err)

Expand Down
7 changes: 4 additions & 3 deletions client/keys/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ func (bo bech32Output) String() string {
return fmt.Sprintf("Bech32 Formats:\n%s", strings.Join(out, "\n"))
}

func parseKeyStringCommand() *cobra.Command {
// ParseKeyStringCommand parses an address from hex to bech32 and vice versa.
func ParseKeyStringCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "parse <hex-or-bech32-address>",
Short: "Parse address from hex to bech32 and vice versa",
Expand Down Expand Up @@ -124,9 +125,9 @@ func displayParseKeyInfo(stringer fmt.Stringer) {
case OutputFormatJSON:

if viper.GetBool(flags.FlagIndentResponse) {
out, err = cdc.MarshalJSONIndent(stringer, "", " ")
out, err = KeysCdc.MarshalJSONIndent(stringer, "", " ")
} else {
out = cdc.MustMarshalJSON(stringer)
out = KeysCdc.MustMarshalJSON(stringer)
}

}
Expand Down
20 changes: 10 additions & 10 deletions client/keys/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ func Commands() *cobra.Command {
needs to sign with a private key.`,
}
cmd.AddCommand(
mnemonicKeyCommand(),
addKeyCommand(),
exportKeyCommand(),
importKeyCommand(),
listKeysCmd(),
showKeysCmd(),
MnemonicKeyCommand(),
AddKeyCommand(),
ExportKeyCommand(),
ImportKeyCommand(),
ListKeysCmd(),
ShowKeysCmd(),
flags.LineBreak,
deleteKeyCommand(),
updateKeyCommand(),
parseKeyStringCommand(),
migrateCommand(),
DeleteKeyCommand(),
UpdateKeyCommand(),
ParseKeyStringCommand(),
MigrateCommand(),
)
cmd.PersistentFlags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)")
viper.BindPFlag(flags.FlagKeyringBackend, cmd.Flags().Lookup(flags.FlagKeyringBackend))
Expand Down
3 changes: 2 additions & 1 deletion client/keys/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ const (
defaultMultiSigKeyName = "multi"
)

func showKeysCmd() *cobra.Command {
// ShowKeysCmd shows key information for a given key name.
func ShowKeysCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "show [name [name...]]",
Short: "Show key info for the given name",
Expand Down
4 changes: 2 additions & 2 deletions client/keys/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ func Test_multiSigKey_Properties(t *testing.T) {
}

func Test_showKeysCmd(t *testing.T) {
cmd := showKeysCmd()
cmd := ShowKeysCmd()
require.NotNil(t, cmd)
require.Equal(t, "false", cmd.Flag(FlagAddress).DefValue)
require.Equal(t, "false", cmd.Flag(FlagPublicKey).DefValue)
}

func Test_runShowCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := showKeysCmd()
cmd := ShowKeysCmd()
mockIn, _, _ := tests.ApplyMockIO(cmd)
require.EqualError(t, runShowCmd(cmd, []string{"invalid"}), "The specified item could not be found in the keyring")
require.EqualError(t, runShowCmd(cmd, []string{"invalid1", "invalid2"}), "The specified item could not be found in the keyring")
Expand Down
4 changes: 3 additions & 1 deletion client/keys/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
"github.com/cosmos/cosmos-sdk/client/input"
)

func updateKeyCommand() *cobra.Command {
// 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",
Expand Down
4 changes: 2 additions & 2 deletions client/keys/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

func Test_updateKeyCommand(t *testing.T) {
cmd := updateKeyCommand()
cmd := UpdateKeyCommand()
assert.NotNil(t, cmd)
// No flags or defaults to validate
}
Expand All @@ -20,7 +20,7 @@ func Test_runUpdateCmd(t *testing.T) {
fakeKeyName1 := "runUpdateCmd_Key1"
fakeKeyName2 := "runUpdateCmd_Key2"

cmd := updateKeyCommand()
cmd := UpdateKeyCommand()

// fails because it requests a password
assert.EqualError(t, runUpdateCmd(cmd, []string{fakeKeyName1}), "EOF")
Expand Down
Loading

0 comments on commit aa9b163

Please sign in to comment.