From d4c831e63ad3e78eefe3a3832d92f2b8373b5395 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 14 Nov 2019 15:17:21 +0100 Subject: [PATCH] Drop on-disk keybase in favor of keyring (#5180) * Switch keys commands to keyring * Replace NewKeybase with NewKeyring * Fix delete test * Purge dead code * Override COSMOS_SDK_TEST_KEYRING envvar to switch to a test keyring * s/unningOnServer/unningUnattended/ C'ing @tnachen * Add deprecated warning, output looks like the following: ``` $ gaiacli keys update --help Command "update" is 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. Change the password used to protect private key Usage: gaiacli keys update [flags] Flags: -h, --help help for update Global Flags: --chain-id string Chain ID of tendermint node -e, --encoding string Binary encoding (hex|b64|btc) (default "hex") --home string directory for config and data (default "/home/alessio/.gaiacli") -o, --output string Output format (text|json) (default "text") --trace print out full stack trace on errors ``` * Update multisign command * Modify server.GenerateSaveCoinKey() * GenerateSaveCoinKey more modifications * Update docs * Update upgrade module --- client/alias.go | 9 ++- client/context/context.go | 44 ++++++++++---- client/keys/add.go | 16 +----- client/keys/add_ledger_test.go | 64 +++++++++++++-------- client/keys/add_test.go | 45 ++++++++------- client/keys/delete.go | 23 +++----- client/keys/delete_test.go | 60 +++++++++++++++----- client/keys/export.go | 4 +- client/keys/export_test.go | 30 +++++++--- client/keys/import.go | 4 +- client/keys/import_test.go | 20 +++++-- client/keys/list.go | 2 +- client/keys/list_test.go | 23 ++++++-- client/keys/show.go | 8 ++- client/keys/show_test.go | 85 ++++++++++++++++++---------- client/keys/update.go | 10 +++- client/keys/utils.go | 74 +++++++----------------- crypto/keys/keyring.go | 2 +- docs/interfaces/service-providers.md | 5 +- server/init.go | 12 +--- server/init_test.go | 34 +++++------ x/auth/client/cli/tx_multisign.go | 8 ++- x/auth/client/cli/tx_sign.go | 6 +- x/auth/client/utils/tx.go | 23 ++------ x/auth/types/txbuilder.go | 8 ++- x/bank/client/cli/tx.go | 7 ++- x/crisis/client/cli/tx.go | 7 ++- x/distribution/client/cli/tx.go | 21 ++++--- x/genutil/client/cli/gentx.go | 8 ++- x/gov/client/cli/tx.go | 16 ++++-- x/params/client/cli/tx.go | 6 +- x/slashing/client/cli/tx.go | 7 ++- x/staking/client/cli/tx.go | 26 +++++---- x/upgrade/client/cli/tx.go | 11 ++-- 34 files changed, 419 insertions(+), 309 deletions(-) diff --git a/client/alias.go b/client/alias.go index 15f6f87290..e2a1caf167 100644 --- a/client/alias.go +++ b/client/alias.go @@ -65,6 +65,8 @@ const ( var ( // functions aliases NewCLIContextWithFrom = context.NewCLIContextWithFrom + NewCLIContextWithInput = context.NewCLIContextWithInput + NewCLIContextWithInputAndFrom = context.NewCLIContextWithInputAndFrom NewCLIContext = context.NewCLIContext GetFromFields = context.GetFromFields ErrInvalidAccount = context.ErrInvalidAccount @@ -81,11 +83,8 @@ var ( NewRecoverKey = keys.NewRecoverKey NewUpdateKeyReq = keys.NewUpdateKeyReq NewDeleteKeyReq = keys.NewDeleteKeyReq - GetKeyInfo = keys.GetKeyInfo - GetPassphrase = keys.GetPassphrase - ReadPassphraseFromStdin = keys.ReadPassphraseFromStdin - NewKeyBaseFromHomeFlag = keys.NewKeyBaseFromHomeFlag - NewKeyBaseFromDir = keys.NewKeyBaseFromDir + NewKeyringFromDir = keys.NewKeyringFromDir + NewKeyringFromHomeFlag = keys.NewKeyringFromHomeFlag NewInMemoryKeyBase = keys.NewInMemoryKeyBase NewRestServer = lcd.NewRestServer ServeCommand = lcd.ServeCommand diff --git a/client/context/context.go b/client/context/context.go index 2fd6ff66f8..c778a4345c 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -27,6 +27,7 @@ type CLIContext struct { Client rpcclient.Client ChainID string Keybase cryptokeys.Keybase + Input io.Reader Output io.Writer OutputFormat string Height int64 @@ -45,18 +46,18 @@ type CLIContext struct { SkipConfirm bool } -// NewCLIContextWithFrom returns a new initialized CLIContext with parameters from the -// command line using Viper. It takes a key name or address and populates the FromName and -// FromAddress field accordingly. It will also create Tendermint verifier using -// the chain ID, home directory and RPC URI provided by the command line. If using -// a CLIContext in tests or any non CLI-based environment, the verifier will not -// be created and will be set as nil because FlagTrustNode must be set. -func NewCLIContextWithFrom(from string) CLIContext { +// NewCLIContextWithInputAndFrom returns a new initialized CLIContext with parameters from the +// command line using Viper. It takes a io.Reader and and key name or address and populates +// the FromName and FromAddress field accordingly. It will also create Tendermint verifier +// using the chain ID, home directory and RPC URI provided by the command line. If using +// a CLIContext in tests or any non CLI-based environment, the verifier will not be created +// and will be set as nil because FlagTrustNode must be set. +func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext { var nodeURI string var rpc rpcclient.Client genOnly := viper.GetBool(flags.FlagGenerateOnly) - fromAddress, fromName, err := GetFromFields(from, genOnly) + fromAddress, fromName, err := GetFromFields(input, from, genOnly) if err != nil { fmt.Printf("failed to get from fields: %v", err) os.Exit(1) @@ -72,6 +73,7 @@ func NewCLIContextWithFrom(from string) CLIContext { ctx := CLIContext{ Client: rpc, ChainID: viper.GetString(flags.FlagChainID), + Input: input, Output: os.Stdout, NodeURI: nodeURI, From: viper.GetString(flags.FlagFrom), @@ -99,10 +101,32 @@ func NewCLIContextWithFrom(from string) CLIContext { return ctx.WithVerifier(verifier) } +// NewCLIContextWithFrom returns a new initialized CLIContext with parameters from the +// command line using Viper. It takes a key name or address and populates the FromName and +// FromAddress field accordingly. It will also create Tendermint verifier using +// the chain ID, home directory and RPC URI provided by the command line. If using +// a CLIContext in tests or any non CLI-based environment, the verifier will not +// be created and will be set as nil because FlagTrustNode must be set. +func NewCLIContextWithFrom(from string) CLIContext { + return NewCLIContextWithInputAndFrom(os.Stdin, from) +} + // NewCLIContext returns a new initialized CLIContext with parameters from the // command line using Viper. func NewCLIContext() CLIContext { return NewCLIContextWithFrom(viper.GetString(flags.FlagFrom)) } +// NewCLIContextWithInput returns a new initialized CLIContext with a io.Reader and parameters +// from the command line using Viper. +func NewCLIContextWithInput(input io.Reader) CLIContext { + return NewCLIContextWithInputAndFrom(input, viper.GetString(flags.FlagFrom)) +} + +// WithInput returns a copy of the context with an updated input. +func (ctx CLIContext) WithInput(r io.Reader) CLIContext { + ctx.Input = r + return ctx +} + // WithCodec returns a copy of the context with an updated codec. func (ctx CLIContext) WithCodec(cdc *codec.Codec) CLIContext { ctx.Codec = cdc @@ -229,7 +253,7 @@ func (ctx CLIContext) PrintOutput(toPrint interface{}) error { // GetFromFields returns a from account address and Keybase name given either // an address or key name. If genOnly is true, only a valid Bech32 cosmos // address is returned. -func GetFromFields(from string, genOnly bool) (sdk.AccAddress, string, error) { +func GetFromFields(input io.Reader, from string, genOnly bool) (sdk.AccAddress, string, error) { if from == "" { return nil, "", nil } @@ -243,7 +267,7 @@ func GetFromFields(from string, genOnly bool) (sdk.AccAddress, string, error) { return addr, "", nil } - keybase, err := keys.NewKeyBaseFromHomeFlag() + keybase, err := keys.NewKeyringFromHomeFlag(input) if err != nil { return nil, "", err } diff --git a/client/keys/add.go b/client/keys/add.go index 8ff444010a..f55572887b 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -87,7 +87,6 @@ output func runAddCmd(cmd *cobra.Command, args []string) error { var kb keys.Keybase var err error - var encryptPassword string inBuf := bufio.NewReader(cmd.InOrStdin()) name := args[0] @@ -99,9 +98,8 @@ func runAddCmd(cmd *cobra.Command, args []string) error { // we throw this away, so don't enforce args, // we want to get a new random seed phrase quickly kb = keys.NewInMemory() - encryptPassword = DefaultKeyPass } else { - kb, err = NewKeyBaseFromHomeFlag() + kb, err = NewKeyringFromHomeFlag(cmd.InOrStdin()) if err != nil { return err } @@ -150,16 +148,6 @@ func runAddCmd(cmd *cobra.Command, args []string) error { cmd.PrintErrf("Key %q saved to disk.\n", name) return nil } - - // ask for a password when generating a local key - if viper.GetString(FlagPublicKey) == "" && !viper.GetBool(flags.FlagUseLedger) { - encryptPassword, err = input.GetCheckPassword( - "Enter a passphrase to encrypt your key to disk:", - "Repeat the passphrase:", inBuf) - if err != nil { - return err - } - } } if viper.GetString(FlagPublicKey) != "" { @@ -243,7 +231,7 @@ func runAddCmd(cmd *cobra.Command, args []string) error { } } - info, err := kb.CreateAccount(name, mnemonic, bip39Passphrase, encryptPassword, account, index) + info, err := kb.CreateAccount(name, mnemonic, bip39Passphrase, DefaultKeyPass, account, index) if err != nil { return err } diff --git a/client/keys/add_ledger_test.go b/client/keys/add_ledger_test.go index 8602820918..d33fd4a814 100644 --- a/client/keys/add_ledger_test.go +++ b/client/keys/add_ledger_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/spf13/viper" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/cli" @@ -17,6 +17,7 @@ import ( ) func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) { + runningUnattended := isRunningUnattended() config := sdk.GetConfig() bech32PrefixAccAddr := "terra" @@ -33,11 +34,11 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) { config.SetBech32PrefixForConsensusNode(bech32PrefixConsAddr, bech32PrefixConsPub) cmd := addKeyCommand() - assert.NotNil(t, cmd) + require.NotNil(t, cmd) // Prepare a keybase kbHome, kbCleanUp := tests.NewTestCaseDir(t) - assert.NotNil(t, kbHome) + require.NotNil(t, kbHome) defer kbCleanUp() viper.Set(flags.FlagHome, kbHome) viper.Set(flags.FlagUseLedger, true) @@ -47,19 +48,26 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) { // Now enter password mockIn, _, _ := tests.ApplyMockIO(cmd) mockIn.Reset("test1234\ntest1234\n") - assert.NoError(t, runAddCmd(cmd, []string{"keyname1"})) + require.NoError(t, runAddCmd(cmd, []string{"keyname1"})) // Now check that it has been stored properly - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) - assert.NotNil(t, kb) + kb, err := NewKeyringFromHomeFlag(mockIn) + require.NoError(t, err) + require.NotNil(t, kb) + defer func() { + kb.Delete("keyname1", "", false) + }() + mockIn.Reset("test1234\n") + if runningUnattended { + mockIn.Reset("test1234\ntest1234\n") + } key1, err := kb.Get("keyname1") - assert.NoError(t, err) - assert.NotNil(t, key1) + require.NoError(t, err) + require.NotNil(t, key1) - assert.Equal(t, "keyname1", key1.GetName()) - assert.Equal(t, keys.TypeLedger, key1.GetType()) - assert.Equal(t, + require.Equal(t, "keyname1", key1.GetName()) + require.Equal(t, keys.TypeLedger, key1.GetType()) + require.Equal(t, "terrapub1addwnpepqvpg7r26nl2pvqqern00m6s9uaax3hauu2rzg8qpjzq9hy6xve7sw0d84m6", sdk.MustBech32ifyAccPub(key1.GetPubKey())) @@ -71,12 +79,14 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) { } func Test_runAddCmdLedger(t *testing.T) { + runningUnattended := isRunningUnattended() cmd := addKeyCommand() - assert.NotNil(t, cmd) + require.NotNil(t, cmd) + mockIn, _, _ := tests.ApplyMockIO(cmd) // Prepare a keybase kbHome, kbCleanUp := tests.NewTestCaseDir(t) - assert.NotNil(t, kbHome) + require.NotNil(t, kbHome) defer kbCleanUp() viper.Set(flags.FlagHome, kbHome) viper.Set(flags.FlagUseLedger, true) @@ -84,21 +94,27 @@ func Test_runAddCmdLedger(t *testing.T) { /// Test Text viper.Set(cli.OutputFlag, OutputFormatText) // Now enter password - mockIn, _, _ := tests.ApplyMockIO(cmd) mockIn.Reset("test1234\ntest1234\n") - assert.NoError(t, runAddCmd(cmd, []string{"keyname1"})) + require.NoError(t, runAddCmd(cmd, []string{"keyname1"})) // Now check that it has been stored properly - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) - assert.NotNil(t, kb) + kb, err := NewKeyringFromHomeFlag(mockIn) + require.NoError(t, err) + require.NotNil(t, kb) + defer func() { + kb.Delete("keyname1", "", false) + }() + mockIn.Reset("test1234\n") + if runningUnattended { + mockIn.Reset("test1234\ntest1234\n") + } key1, err := kb.Get("keyname1") - assert.NoError(t, err) - assert.NotNil(t, key1) + require.NoError(t, err) + require.NotNil(t, key1) - assert.Equal(t, "keyname1", key1.GetName()) - assert.Equal(t, keys.TypeLedger, key1.GetType()) - assert.Equal(t, + require.Equal(t, "keyname1", key1.GetName()) + require.Equal(t, keys.TypeLedger, key1.GetType()) + require.Equal(t, "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0", sdk.MustBech32ifyAccPub(key1.GetPubKey())) } diff --git a/client/keys/add_test.go b/client/keys/add_test.go index 86f93f2bc5..3f332a393e 100644 --- a/client/keys/add_test.go +++ b/client/keys/add_test.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/cli" @@ -13,6 +14,7 @@ import ( ) func Test_runAddCmdBasic(t *testing.T) { + runningUnattended := isRunningUnattended() cmd := addKeyCommand() assert.NotNil(t, cmd) mockIn, _, _ := tests.ApplyMockIO(cmd) @@ -21,28 +23,33 @@ func Test_runAddCmdBasic(t *testing.T) { assert.NotNil(t, kbHome) defer kbCleanUp() viper.Set(flags.FlagHome, kbHome) - viper.Set(cli.OutputFlag, OutputFormatText) - mockIn.Reset("test1234\ntest1234\n") - err := runAddCmd(cmd, []string{"keyname1"}) - assert.NoError(t, err) + if runningUnattended { + mockIn.Reset("testpass1\ntestpass1\n") + } else { + mockIn.Reset("y\n") + kb, err := NewKeyringFromHomeFlag(mockIn) + require.NoError(t, err) + defer func() { + kb.Delete("keyname1", "", false) + kb.Delete("keyname2", "", false) + }() + } + assert.NoError(t, runAddCmd(cmd, []string{"keyname1"})) - viper.Set(cli.OutputFlag, OutputFormatText) + if runningUnattended { + mockIn.Reset("testpass1\nN\n") + } else { + mockIn.Reset("N\n") + } + assert.Error(t, runAddCmd(cmd, []string{"keyname1"})) - mockIn.Reset("test1234\ntest1234\n") - err = runAddCmd(cmd, []string{"keyname1"}) - assert.Error(t, err) - - viper.Set(cli.OutputFlag, OutputFormatText) - - mockIn.Reset("y\ntest1234\ntest1234\n") - err = runAddCmd(cmd, []string{"keyname1"}) - assert.NoError(t, err) - - viper.Set(cli.OutputFlag, OutputFormatJSON) - - mockIn.Reset("test1234\ntest1234\n") - err = runAddCmd(cmd, []string{"keyname2"}) + if runningUnattended { + mockIn.Reset("testpass1\nN\n") + } else { + mockIn.Reset("y\n") + } + err := runAddCmd(cmd, []string{"keyname2"}) assert.NoError(t, err) } diff --git a/client/keys/delete.go b/client/keys/delete.go index 6f8d2837fa..d2e535105a 100644 --- a/client/keys/delete.go +++ b/client/keys/delete.go @@ -4,12 +4,11 @@ import ( "bufio" "errors" - "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client/input" "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/spf13/cobra" + "github.com/spf13/viper" ) const ( @@ -40,8 +39,9 @@ private keys stored in a ledger device cannot be deleted with the CLI. func runDeleteCmd(cmd *cobra.Command, args []string) error { name := args[0] + buf := bufio.NewReader(cmd.InOrStdin()) - kb, err := NewKeyBaseFromHomeFlag() + kb, err := NewKeyringFromHomeFlag(buf) if err != nil { return err } @@ -51,13 +51,14 @@ func runDeleteCmd(cmd *cobra.Command, args []string) error { return err } - buf := bufio.NewReader(cmd.InOrStdin()) if info.GetType() == keys.TypeLedger || info.GetType() == keys.TypeOffline { + // confirm deletion, unless -y is passed if !viper.GetBool(flagYes) { if err := confirmDeletion(buf); err != nil { return err } } + if err := kb.Delete(name, "", true); err != nil { return err } @@ -65,18 +66,8 @@ func runDeleteCmd(cmd *cobra.Command, args []string) error { return nil } - // skip passphrase check if run with --force - skipPass := viper.GetBool(flagForce) - var oldpass string - if !skipPass { - if oldpass, err = input.GetPassword( - "DANGER - enter password to permanently delete key:", buf); err != nil { - return err - } - } - - err = kb.Delete(name, oldpass, skipPass) - if err != nil { + // old password and skip flag arguments are ignored + if err := kb.Delete(name, "", true); err != nil { return err } cmd.PrintErrln("Key deleted forever (uh oh!)") diff --git a/client/keys/delete_test.go b/client/keys/delete_test.go index 184f1dfb9e..537c8073f8 100644 --- a/client/keys/delete_test.go +++ b/client/keys/delete_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/spf13/viper" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client/flags" @@ -14,45 +13,74 @@ import ( ) func Test_runDeleteCmd(t *testing.T) { + runningUnattended := isRunningUnattended() deleteKeyCommand := deleteKeyCommand() + mockIn, _, _ := tests.ApplyMockIO(deleteKeyCommand) yesF, _ := deleteKeyCommand.Flags().GetBool(flagYes) forceF, _ := deleteKeyCommand.Flags().GetBool(flagForce) - assert.False(t, yesF) - assert.False(t, forceF) + require.False(t, yesF) + require.False(t, forceF) fakeKeyName1 := "runDeleteCmd_Key1" fakeKeyName2 := "runDeleteCmd_Key2" + if !runningUnattended { + kb, err := NewKeyringFromHomeFlag(mockIn) + require.NoError(t, err) + defer func() { + kb.Delete("runDeleteCmd_Key1", "", false) + kb.Delete("runDeleteCmd_Key2", "", false) + }() + } // Now add a temporary keybase kbHome, cleanUp := tests.NewTestCaseDir(t) defer cleanUp() viper.Set(flags.FlagHome, kbHome) // Now - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) + kb, err := NewKeyringFromHomeFlag(mockIn) + require.NoError(t, err) + if runningUnattended { + mockIn.Reset("testpass1\ntestpass1\n") + } _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", 0, 0) - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1) - assert.NoError(t, err) + require.NoError(t, err) + if runningUnattended { + mockIn.Reset("testpass1\ntestpass1\n") + } + _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1) + require.NoError(t, err) + + if runningUnattended { + mockIn.Reset("testpass1\ntestpass1\n") + } err = runDeleteCmd(deleteKeyCommand, []string{"blah"}) require.Error(t, err) - require.Equal(t, "Key blah not found", err.Error()) + require.Equal(t, "The specified item could not be found in the keyring", err.Error()) // User confirmation missing err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1}) require.Error(t, err) - require.Equal(t, "EOF", err.Error()) + if runningUnattended { + require.Equal(t, "aborted", err.Error()) + } else { + 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 - mockIn, _, _ := tests.ApplyMockIO(deleteKeyCommand) - mockIn.Reset("y\n") + viper.Set(flagYes, true) + if runningUnattended { + mockIn.Reset("testpass1\ntestpass1\n") + } require.NoError(t, runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1})) _, err = kb.Get(fakeKeyName1) @@ -60,14 +88,18 @@ 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) require.Error(t, err) // Key2 is gone - - // TODO: Write another case for !keys.Local } func Test_confirmDeletion(t *testing.T) { diff --git a/client/keys/export.go b/client/keys/export.go index 93e1085f97..393785501b 100644 --- a/client/keys/export.go +++ b/client/keys/export.go @@ -20,12 +20,12 @@ func exportKeyCommand() *cobra.Command { } func runExportCmd(cmd *cobra.Command, args []string) error { - kb, err := NewKeyBaseFromHomeFlag() + buf := bufio.NewReader(cmd.InOrStdin()) + kb, err := NewKeyringFromHomeFlag(buf) if err != nil { return err } - buf := bufio.NewReader(cmd.InOrStdin()) decryptPassword, err := input.GetPassword("Enter passphrase to decrypt your key:", buf) if err != nil { return err diff --git a/client/keys/export_test.go b/client/keys/export_test.go index bfb04dc624..8132f06a6d 100644 --- a/client/keys/export_test.go +++ b/client/keys/export_test.go @@ -4,14 +4,16 @@ import ( "testing" "github.com/spf13/viper" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/tests" ) func Test_runExportCmd(t *testing.T) { + runningUnattended := isRunningUnattended() exportKeyCommand := exportKeyCommand() + mockIn, _, _ := tests.ApplyMockIO(exportKeyCommand) // Now add a temporary keybase kbHome, cleanUp := tests.NewTestCaseDir(t) @@ -19,13 +21,25 @@ func Test_runExportCmd(t *testing.T) { viper.Set(flags.FlagHome, kbHome) // create a key - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) - _, err = kb.CreateAccount("keyname1", tests.TestMnemonic, "", "123456789", 0, 0) - assert.NoError(t, err) + kb, err := NewKeyringFromHomeFlag(mockIn) + require.NoError(t, err) + if !runningUnattended { + defer func() { + kb.Delete("keyname1", "", false) + }() + } + + if runningUnattended { + mockIn.Reset("testpass1\ntestpass1\n") + } + _, err = kb.CreateAccount("keyname1", tests.TestMnemonic, "", "123456789", 0, 0) + require.NoError(t, err) - mockIn, _, _ := tests.ApplyMockIO(exportKeyCommand) - mockIn.Reset("123456789\n123456789\n") // Now enter password - assert.NoError(t, runExportCmd(exportKeyCommand, []string{"keyname1"})) + if runningUnattended { + mockIn.Reset("123456789\n123456789\ntestpass1\n") + } else { + mockIn.Reset("123456789\n123456789\n") + } + require.NoError(t, runExportCmd(exportKeyCommand, []string{"keyname1"})) } diff --git a/client/keys/import.go b/client/keys/import.go index 55375090b8..127479ce99 100644 --- a/client/keys/import.go +++ b/client/keys/import.go @@ -21,7 +21,8 @@ func importKeyCommand() *cobra.Command { } func runImportCmd(cmd *cobra.Command, args []string) error { - kb, err := NewKeyBaseFromHomeFlag() + buf := bufio.NewReader(cmd.InOrStdin()) + kb, err := NewKeyringFromHomeFlag(buf) if err != nil { return err } @@ -31,7 +32,6 @@ func runImportCmd(cmd *cobra.Command, args []string) error { return err } - buf := bufio.NewReader(cmd.InOrStdin()) passphrase, err := input.GetPassword("Enter passphrase to decrypt your key:", buf) if err != nil { return err diff --git a/client/keys/import_test.go b/client/keys/import_test.go index 95d9abfe46..3579f96863 100644 --- a/client/keys/import_test.go +++ b/client/keys/import_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/spf13/viper" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client/flags" @@ -14,13 +13,23 @@ import ( ) func Test_runImportCmd(t *testing.T) { + runningUnattended := isRunningUnattended() importKeyCommand := importKeyCommand() + mockIn, _, _ := tests.ApplyMockIO(importKeyCommand) // Now add a temporary keybase kbHome, cleanUp := tests.NewTestCaseDir(t) defer cleanUp() viper.Set(flags.FlagHome, kbHome) + if !runningUnattended { + kb, err := NewKeyringFromHomeFlag(mockIn) + require.NoError(t, err) + defer func() { + kb.Delete("keyname1", "", false) + }() + } + keyfile := filepath.Join(kbHome, "key.asc") armoredKey := `-----BEGIN TENDERMINT PRIVATE KEY----- salt: A790BB721D1C094260EA84F5E5B72289 @@ -34,7 +43,10 @@ HbP+c6JmeJy9JXe2rbbF1QtCX1gLqGcDQPBXiCtFvP7/8wTZtVOPj8vREzhZ9ElO require.NoError(t, ioutil.WriteFile(keyfile, []byte(armoredKey), 0644)) // Now enter password - mockIn, _, _ := tests.ApplyMockIO(importKeyCommand) - mockIn.Reset("123456789\n") - assert.NoError(t, runImportCmd(importKeyCommand, []string{"keyname1", keyfile})) + if runningUnattended { + mockIn.Reset("123456789\n12345678\n12345678\n") + } else { + mockIn.Reset("123456789\n") + } + require.NoError(t, runImportCmd(importKeyCommand, []string{"keyname1", keyfile})) } diff --git a/client/keys/list.go b/client/keys/list.go index 7edec85415..da4d4b2224 100644 --- a/client/keys/list.go +++ b/client/keys/list.go @@ -19,7 +19,7 @@ along with their associated name and address.`, } func runListCmd(cmd *cobra.Command, args []string) error { - kb, err := NewKeyBaseFromHomeFlag() + kb, err := NewKeyringFromHomeFlag(cmd.InOrStdin()) if err != nil { return err } diff --git a/client/keys/list_test.go b/client/keys/list_test.go index 214e846ce2..b8cfb223ca 100644 --- a/client/keys/list_test.go +++ b/client/keys/list_test.go @@ -5,13 +5,14 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/tests" ) func Test_runListCmd(t *testing.T) { + runningUnattended := isRunningUnattended() type args struct { cmd *cobra.Command args []string @@ -28,24 +29,34 @@ func Test_runListCmd(t *testing.T) { defer cleanUp2() viper.Set(flags.FlagHome, kbHome2) - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) - _, err = kb.CreateAccount("something", tests.TestMnemonic, "", "", 0, 0) - assert.NoError(t, err) + mockIn, _, _ := tests.ApplyMockIO(cmdBasic) + kb, err := NewKeyringFromHomeFlag(mockIn) + require.NoError(t, err) + if runningUnattended { + mockIn.Reset("testpass1\ntestpass1\n") + } + _, err = kb.CreateAccount("something", tests.TestMnemonic, "", "", 0, 0) + require.NoError(t, err) + + defer func() { + kb.Delete("something", "", false) + }() testData := []struct { name string kbDir string args args wantErr bool }{ - {"invalid keybase", "/dev/null", args{cmdBasic, []string{}}, true}, {"keybase: empty", kbHome1, args{cmdBasic, []string{}}, false}, {"keybase: w/key", kbHome2, args{cmdBasic, []string{}}, false}, } for _, tt := range testData { tt := tt t.Run(tt.name, func(t *testing.T) { + if runningUnattended { + mockIn.Reset("testpass1\ntestpass1\n") + } 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) diff --git a/client/keys/show.go b/client/keys/show.go index cf5043c2ab..fa37f372f9 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -56,15 +56,19 @@ consisting of all the keys provided by name and multisig threshold.`, func runShowCmd(cmd *cobra.Command, args []string) (err error) { var info keys.Info + kb, err := NewKeyringFromHomeFlag(cmd.InOrStdin()) + if err != nil { + return err + } if len(args) == 1 { - info, err = GetKeyInfo(args[0]) + info, err = kb.Get(args[0]) if err != nil { return err } } else { pks := make([]tmcrypto.PubKey, len(args)) for i, keyName := range args { - info, err := GetKeyInfo(keyName) + info, err := kb.Get(keyName) if err != nil { return err } diff --git a/client/keys/show_test.go b/client/keys/show_test.go index dfbee66f7b..6fb97d97f4 100644 --- a/client/keys/show_test.go +++ b/client/keys/show_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/spf13/viper" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/multisig" @@ -21,27 +21,25 @@ func Test_multiSigKey_Properties(t *testing.T) { pk := multisig.NewPubKeyMultisigThreshold(1, []crypto.PubKey{tmpKey1.PubKey()}) tmp := keys.NewMultiInfo("myMultisig", pk) - assert.Equal(t, "myMultisig", tmp.GetName()) - assert.Equal(t, keys.TypeMulti, tmp.GetType()) - assert.Equal(t, "D3923267FA8A3DD367BB768FA8BDC8FF7F89DA3F", tmp.GetPubKey().Address().String()) - assert.Equal(t, "cosmos16wfryel63g7axeamw68630wglalcnk3l0zuadc", tmp.GetAddress().String()) + require.Equal(t, "myMultisig", tmp.GetName()) + require.Equal(t, keys.TypeMulti, tmp.GetType()) + require.Equal(t, "D3923267FA8A3DD367BB768FA8BDC8FF7F89DA3F", tmp.GetPubKey().Address().String()) + require.Equal(t, "cosmos16wfryel63g7axeamw68630wglalcnk3l0zuadc", tmp.GetAddress().String()) } func Test_showKeysCmd(t *testing.T) { cmd := showKeysCmd() - assert.NotNil(t, cmd) - assert.Equal(t, "false", cmd.Flag(FlagAddress).DefValue) - assert.Equal(t, "false", cmd.Flag(FlagPublicKey).DefValue) + 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() - - err := runShowCmd(cmd, []string{"invalid"}) - assert.EqualError(t, err, "Key invalid not found") - - err = runShowCmd(cmd, []string{"invalid1", "invalid2"}) - assert.EqualError(t, err, "Key invalid1 not found") + 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") // Prepare a key base // Now add a temporary keybase @@ -51,47 +49,76 @@ func Test_runShowCmd(t *testing.T) { fakeKeyName1 := "runShowCmd_Key1" fakeKeyName2 := "runShowCmd_Key2" - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) + kb, err := NewKeyringFromHomeFlag(mockIn) + require.NoError(t, err) + defer func() { + kb.Delete("runShowCmd_Key1", "", false) + kb.Delete("runShowCmd_Key2", "", false) + }() + if runningUnattended { + mockIn.Reset("testpass1\ntestpass1\n") + } _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", 0, 0) - assert.NoError(t, err) + require.NoError(t, err) + + if runningUnattended { + mockIn.Reset("testpass1\n") + } _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1) - assert.NoError(t, err) + require.NoError(t, err) // Now try single key - err = runShowCmd(cmd, []string{fakeKeyName1}) - assert.EqualError(t, err, "invalid Bech32 prefix encoding provided: ") + 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) - err = runShowCmd(cmd, []string{fakeKeyName1}) - assert.NoError(t, err) + if runningUnattended { + mockIn.Reset("testpass1\n") + } + require.NoError(t, runShowCmd(cmd, []string{fakeKeyName1})) // Now try multisig key - set bech to acc viper.Set(FlagBechPrefix, sdk.PrefixAccount) - err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}) - assert.EqualError(t, err, "threshold must be a positive integer") + 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}) - assert.NoError(t, err) + require.NoError(t, err) // Now try multisig key - set bech to acc + threshold=2 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}) - assert.EqualError(t, err, "the device flag (-d) can only be used for accounts stored in devices") + 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}) - assert.EqualError(t, err, "the device flag (-d) can only be used for accounts") + 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}) - assert.EqualError(t, err, "the device flag (-d) can only be used for addresses not pubkeys") + require.EqualError(t, err, "the device flag (-d) can only be used for addresses not pubkeys") // TODO: Capture stdout and compare } @@ -148,7 +175,7 @@ func Test_getBechKeyOut(t *testing.T) { } if !tt.wantErr { - assert.NotNil(t, got) + require.NotNil(t, got) } // TODO: Still not possible to compare functions diff --git a/client/keys/update.go b/client/keys/update.go index 5e220ef2f1..64fdbe7e5f 100644 --- a/client/keys/update.go +++ b/client/keys/update.go @@ -12,8 +12,14 @@ func updateKeyCommand() *cobra.Command { cmd := &cobra.Command{ Use: "update ", Short: "Change the password used to protect private key", - RunE: runUpdateCmd, - Args: cobra.ExactArgs(1), + 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 } diff --git a/client/keys/utils.go b/client/keys/utils.go index b708bdefef..95beb8ffe0 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -1,18 +1,19 @@ package keys import ( - "bufio" "fmt" + "io" "os" "path/filepath" + "github.com/99designs/keyring" "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/cli" - yaml "gopkg.in/yaml.v2" + "gopkg.in/yaml.v2" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/input" "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" ) // available output formats. @@ -26,55 +27,6 @@ const ( type bechKeyOutFn func(keyInfo keys.Info) (keys.KeyOutput, error) -// GetKeyInfo returns key info for a given name. An error is returned if the -// keybase cannot be retrieved or getting the info fails. -func GetKeyInfo(name string) (keys.Info, error) { - keybase, err := NewKeyBaseFromHomeFlag() - if err != nil { - return nil, err - } - - return keybase.Get(name) -} - -// GetPassphrase returns a passphrase for a given name. It will first retrieve -// the key info for that name if the type is local, it'll fetch input from -// STDIN. Otherwise, an empty passphrase is returned. An error is returned if -// the key info cannot be fetched or reading from STDIN fails. -func GetPassphrase(name string) (string, error) { - var passphrase string - - keyInfo, err := GetKeyInfo(name) - if err != nil { - return passphrase, err - } - - // we only need a passphrase for locally stored keys - // TODO: (ref: #864) address security concerns - if keyInfo.GetType() == keys.TypeLocal { - passphrase, err = ReadPassphraseFromStdin(name) - if err != nil { - return passphrase, err - } - } - - return passphrase, nil -} - -// ReadPassphraseFromStdin attempts to read a passphrase from STDIN return an -// error upon failure. -func ReadPassphraseFromStdin(name string) (string, error) { - buf := bufio.NewReader(os.Stdin) - prompt := fmt.Sprintf("Password to sign with '%s':", name) - - passphrase, err := input.GetPassword(prompt, buf) - if err != nil { - return passphrase, fmt.Errorf("error reading passphrase: %v", err) - } - - return passphrase, nil -} - // NewKeyBaseFromHomeFlag initializes a Keybase based on the configuration. func NewKeyBaseFromHomeFlag() (keys.Keybase, error) { rootDir := viper.GetString(flags.FlagHome) @@ -89,6 +41,19 @@ func NewKeyBaseFromDir(rootDir string) (keys.Keybase, error) { // NewInMemoryKeyBase returns a storage-less keybase. func NewInMemoryKeyBase() keys.Keybase { return keys.NewInMemory() } +// NewKeyBaseFromHomeFlag initializes a keyring based on configuration. +func NewKeyringFromHomeFlag(input io.Reader) (keys.Keybase, error) { + return NewKeyringFromDir(viper.GetString(flags.FlagHome), input) +} + +// NewKeyBaseFromDir initializes a keybase at a particular dir. +func NewKeyringFromDir(rootDir string, input io.Reader) (keys.Keybase, error) { + if os.Getenv("COSMOS_SDK_TEST_KEYRING") != "" { + return keys.NewTestKeyring(sdk.GetConfig().GetKeyringServiceName(), rootDir) + } + return keys.NewKeyring(sdk.GetConfig().GetKeyringServiceName(), rootDir, input) +} + func getLazyKeyBaseFromDir(rootDir string) (keys.Keybase, error) { return keys.New(defaultKeyDBName, filepath.Join(rootDir, "keys")), nil } @@ -171,3 +136,8 @@ func printPubKey(info keys.Info, bechKeyOut bechKeyOutFn) { fmt.Println(ko.PubKey) } + +func isRunningUnattended() bool { + backends := keyring.AvailableBackends() + return len(backends) == 2 && backends[1] == keyring.BackendType("file") +} diff --git a/crypto/keys/keyring.go b/crypto/keys/keyring.go index 3a6161f4f9..d2673a68b3 100644 --- a/crypto/keys/keyring.go +++ b/crypto/keys/keyring.go @@ -370,7 +370,7 @@ func (kb keyringKeybase) ImportPubKey(name string, armor string) error { // deleting it (for security). It returns an error if the key doesn't exist or // passphrases don't match. The passphrase is ignored when deleting references to // offline and Ledger / HW wallet keys. -func (kb keyringKeybase) Delete(name, passphrase string, skipPass bool) error { +func (kb keyringKeybase) Delete(name, _ string, _ bool) error { // verify we have the proper password before deleting info, err := kb.Get(name) if err != nil { diff --git a/docs/interfaces/service-providers.md b/docs/interfaces/service-providers.md index 39d29daf4d..ad8019930d 100644 --- a/docs/interfaces/service-providers.md +++ b/docs/interfaces/service-providers.md @@ -34,13 +34,14 @@ To generate a new key (default secp256k1 elliptic curve): gaiacli keys add ``` -You will be asked to create a password (at least 8 characters) for this key-pair. This will return the information listed below: +The key-pair will be created in your system's password store. This will return the information listed below: - `NAME`: Name of your key - `TYPE`: Type of your key, always `local`. - `ADDRESS`: Your address. Used to receive funds. - `PUBKEY`: Your public key. Useful for validators. -- `MNEMONIC`: 24-words phrase. **Save this mnemonic somewhere safe**. It is used to recover your private key in case you forget the password. +- `MNEMONIC`: 24-words phrase. **Save this mnemonic somewhere safe**. It is used to recover your +private key in case you forget the password to unlock your system's credentials store. You can see all your available keys by typing: diff --git a/server/init.go b/server/init.go index 58cf98d0ab..c9a95d7ab7 100644 --- a/server/init.go +++ b/server/init.go @@ -25,21 +25,13 @@ func GenerateCoinKey() (sdk.AccAddress, string, error) { // GenerateSaveCoinKey returns the address of a public key, along with the secret // phrase to recover the private key. -func GenerateSaveCoinKey(clientRoot, keyName, keyPass string, - overwrite bool) (sdk.AccAddress, string, error) { - - // get the keystore from the client - keybase, err := clkeys.NewKeyBaseFromDir(clientRoot) - if err != nil { - return sdk.AccAddress([]byte{}), "", err - } - +func GenerateSaveCoinKey(keybase keys.Keybase, keyName, keyPass string, overwrite bool) (sdk.AccAddress, string, error) { // ensure no overwrite if !overwrite { _, err := keybase.Get(keyName) if err == nil { return sdk.AccAddress([]byte{}), "", fmt.Errorf( - "key already exists, overwrite is disabled (clientRoot: %s)", clientRoot) + "key already exists, overwrite is disabled") } } diff --git a/server/init_test.go b/server/init_test.go index 38b8877790..752540825a 100644 --- a/server/init_test.go +++ b/server/init_test.go @@ -1,14 +1,14 @@ package server_test import ( - "io/ioutil" - "os" "testing" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client/keys" + crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/tests" ) func TestGenerateCoinKey(t *testing.T) { @@ -24,17 +24,16 @@ func TestGenerateCoinKey(t *testing.T) { func TestGenerateSaveCoinKey(t *testing.T) { t.Parallel() - dir, cleanup := tempdir(t) + dir, cleanup := tests.NewTestCaseDir(t) defer cleanup() // clean after itself - // Remove the dir to that GenerateSaveCoinKey creates it automatically - os.RemoveAll(dir) - addr, mnemonic, err := server.GenerateSaveCoinKey(dir, "keyname", "012345678", false) + kb, err := crkeys.NewTestKeyring(t.Name(), dir) + require.NoError(t, err) + + addr, mnemonic, err := server.GenerateSaveCoinKey(kb, "keyname", "012345678", false) require.NoError(t, err) // Test key was actually saved - kb, err := keys.NewKeyBaseFromDir(dir) - require.NoError(t, err) info, err := kb.Get("keyname") require.NoError(t, err) require.Equal(t, addr, info.GetAddress()) @@ -47,28 +46,23 @@ func TestGenerateSaveCoinKey(t *testing.T) { func TestGenerateSaveCoinKeyOverwriteFlag(t *testing.T) { t.Parallel() - dir, cleanup := tempdir(t) + dir, cleanup := tests.NewTestCaseDir(t) defer cleanup() // clean after itself - // Remove the dir to that GenerateSaveCoinKey creates it automatically - os.RemoveAll(dir) + + kb, err := crkeys.NewTestKeyring(t.Name(), dir) + require.NoError(t, err) keyname := "justakey" - addr1, _, err := server.GenerateSaveCoinKey(dir, keyname, "012345678", false) + addr1, _, err := server.GenerateSaveCoinKey(kb, keyname, "012345678", false) require.NoError(t, err) // Test overwrite with overwrite=false - _, _, err = server.GenerateSaveCoinKey(dir, keyname, "012345678", false) + _, _, err = server.GenerateSaveCoinKey(kb, keyname, "012345678", false) require.Error(t, err) // Test overwrite with overwrite=true - addr2, _, err := server.GenerateSaveCoinKey(dir, keyname, "012345678", true) + addr2, _, err := server.GenerateSaveCoinKey(kb, keyname, "012345678", true) require.NoError(t, err) require.NotEqual(t, addr1, addr2) } - -func tempdir(t *testing.T) (string, func()) { - dir, err := ioutil.TempDir("", t.Name()+"_") - require.NoError(t, err) - return dir, func() { os.RemoveAll(dir) } -} diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index e6d958a5d9..49e9b68607 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -1,6 +1,7 @@ package cli import ( + "bufio" "fmt" "io/ioutil" "os" @@ -65,7 +66,8 @@ func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) return } - keybase, err := keys.NewKeyBaseFromDir(viper.GetString(cli.HomeFlag)) + inBuf := bufio.NewReader(cmd.InOrStdin()) + keybase, err := keys.NewKeyringFromDir(viper.GetString(cli.HomeFlag), inBuf) if err != nil { return } @@ -80,8 +82,8 @@ func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) multisigPub := multisigInfo.GetPubKey().(multisig.PubKeyMultisigThreshold) multisigSig := multisig.NewMultisig(len(multisigPub.PubKeys)) - cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := types.NewTxBuilderFromCLI() + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) + txBldr := types.NewTxBuilderFromCLI(inBuf) if !viper.GetBool(flagOffline) { accnum, seq, err := types.NewAccountRetriever(cliCtx).GetAccountNumberSequence(multisigInfo.GetAddress()) diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index d4af880d3e..f2e72cb4e3 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -1,6 +1,7 @@ package cli import ( + "bufio" "fmt" "os" "strings" @@ -98,9 +99,10 @@ func makeSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) error return err } + inBuf := bufio.NewReader(cmd.InOrStdin()) offline := viper.GetBool(flagOffline) - cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := types.NewTxBuilderFromCLI() + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) + txBldr := types.NewTxBuilderFromCLI(inBuf) if viper.GetBool(flagValidateSigs) { if !printAndValidateSigs(cliCtx, txBldr.ChainID(), stdTx, offline) { diff --git a/x/auth/client/utils/tx.go b/x/auth/client/utils/tx.go index d2212a039a..4837639e45 100644 --- a/x/auth/client/utils/tx.go +++ b/x/auth/client/utils/tx.go @@ -10,10 +10,10 @@ import ( "github.com/pkg/errors" "github.com/spf13/viper" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -93,13 +93,8 @@ func CompleteAndBroadcastTxCLI(txBldr authtypes.TxBuilder, cliCtx context.CLICon } } - passphrase, err := keys.GetPassphrase(fromName) - if err != nil { - return err - } - // build and sign the transaction - txBytes, err := txBldr.BuildAndSign(fromName, passphrase, msgs) + txBytes, err := txBldr.BuildAndSign(fromName, client.DefaultKeyPass, msgs) if err != nil { return err } @@ -197,12 +192,7 @@ func SignStdTx( } } - passphrase, err := keys.GetPassphrase(name) - if err != nil { - return signedStdTx, err - } - - return txBldr.SignStdTx(name, passphrase, stdTx, appendSig) + return txBldr.SignStdTx(name, client.DefaultKeyPass, stdTx, appendSig) } // SignStdTxWithSignerAddress attaches a signature to a StdTx and returns a copy of a it. @@ -224,12 +214,7 @@ func SignStdTxWithSignerAddress(txBldr authtypes.TxBuilder, cliCtx context.CLICo } } - passphrase, err := keys.GetPassphrase(name) - if err != nil { - return signedStdTx, err - } - - return txBldr.SignStdTx(name, passphrase, stdTx, false) + return txBldr.SignStdTx(name, client.DefaultKeyPass, stdTx, false) } // Read and decode a StdTx from the given filename. Can pass "-" to read from stdin. diff --git a/x/auth/types/txbuilder.go b/x/auth/types/txbuilder.go index 7d60086a13..b5f7eee8c6 100644 --- a/x/auth/types/txbuilder.go +++ b/x/auth/types/txbuilder.go @@ -3,6 +3,8 @@ package types import ( "errors" "fmt" + "io" + "os" "strings" "github.com/spf13/viper" @@ -51,8 +53,8 @@ func NewTxBuilder( // NewTxBuilderFromCLI returns a new initialized TxBuilder with parameters from // the command line using Viper. -func NewTxBuilderFromCLI() TxBuilder { - kb, err := keys.NewKeyBaseFromHomeFlag() +func NewTxBuilderFromCLI(input io.Reader) TxBuilder { + kb, err := keys.NewKeyringFromHomeFlag(input) if err != nil { panic(err) } @@ -274,7 +276,7 @@ func (bldr TxBuilder) SignStdTx(name, passphrase string, stdTx StdTx, appendSig func MakeSignature(keybase crkeys.Keybase, name, passphrase string, msg StdSignMsg) (sig StdSignature, err error) { if keybase == nil { - keybase, err = keys.NewKeyBaseFromHomeFlag() + keybase, err = keys.NewKeyringFromHomeFlag(os.Stdin) if err != nil { return } diff --git a/x/bank/client/cli/tx.go b/x/bank/client/cli/tx.go index fbcdc5ba4a..71e1392b96 100644 --- a/x/bank/client/cli/tx.go +++ b/x/bank/client/cli/tx.go @@ -1,6 +1,8 @@ package cli import ( + "bufio" + "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -34,8 +36,9 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { Short: "Create and sign a send tx", Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithFrom(args[0]).WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInputAndFrom(inBuf, args[0]).WithCodec(cdc) to, err := sdk.AccAddressFromBech32(args[1]) if err != nil { diff --git a/x/crisis/client/cli/tx.go b/x/crisis/client/cli/tx.go index 1b79eac87f..cdea9206fb 100644 --- a/x/crisis/client/cli/tx.go +++ b/x/crisis/client/cli/tx.go @@ -2,6 +2,8 @@ package cli import ( + "bufio" + "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -21,8 +23,9 @@ func GetCmdInvariantBroken(cdc *codec.Codec) *cobra.Command { Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) senderAddr := cliCtx.GetFromAddress() moduleName, route := args[0], args[1] diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index f41edf3e13..22140eccd2 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -2,6 +2,7 @@ package cli import ( + "bufio" "fmt" "strings" @@ -101,8 +102,9 @@ $ %s tx distr withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqh ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) delAddr := cliCtx.GetFromAddress() valAddr, err := sdk.ValAddressFromBech32(args[0]) @@ -138,8 +140,9 @@ $ %s tx distr withdraw-all-rewards --from mykey ), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) delAddr := cliCtx.GetFromAddress() @@ -180,8 +183,9 @@ $ %s tx set-withdraw-addr cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --from m Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) delAddr := cliCtx.GetFromAddress() withdrawAddr, err := sdk.AccAddressFromBech32(args[0]) @@ -232,8 +236,9 @@ Where proposal.json contains: ), ), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) proposal, err := ParseCommunityPoolSpendProposalJSON(cdc, args[0]) if err != nil { diff --git a/x/genutil/client/cli/gentx.go b/x/genutil/client/cli/gentx.go index bed11b88e1..f6a2a334c7 100644 --- a/x/genutil/client/cli/gentx.go +++ b/x/genutil/client/cli/gentx.go @@ -1,6 +1,7 @@ package cli import ( + "bufio" "bytes" "encoding/json" "fmt" @@ -94,7 +95,8 @@ func GenTxCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, sm return errors.Wrap(err, "failed to validate genesis state") } - kb, err := client.NewKeyBaseFromDir(viper.GetString(flagClientHome)) + inBuf := bufio.NewReader(cmd.InOrStdin()) + kb, err := client.NewKeyringFromDir(viper.GetString(flagClientHome), inBuf) if err != nil { return errors.Wrap(err, "failed to initialize keybase") } @@ -121,8 +123,8 @@ func GenTxCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, sm return errors.Wrap(err, "failed to validate account in genesis") } - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := client.NewCLIContext().WithCodec(cdc) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := client.NewCLIContextWithInput(inBuf).WithCodec(cdc) // Set the generate-only flag here after the CLI context has // been created. This allows the from name/key to be correctly populated. diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 8b6deecb8a..4bd49a610e 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -1,6 +1,7 @@ package cli import ( + "bufio" "fmt" "strconv" "strings" @@ -106,8 +107,9 @@ $ %s tx gov submit-proposal --title="Test Proposal" --description="My awesome pr ), ), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) proposal, err := parseSubmitProposalFlags() if err != nil { @@ -156,8 +158,9 @@ $ %s tx gov deposit 1 10stake --from mykey ), ), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -203,8 +206,9 @@ $ %s tx gov vote 1 yes --from mykey ), ), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) // Get voting address from := cliCtx.GetFromAddress() diff --git a/x/params/client/cli/tx.go b/x/params/client/cli/tx.go index 79d6bb1257..08dc1c985e 100644 --- a/x/params/client/cli/tx.go +++ b/x/params/client/cli/tx.go @@ -1,6 +1,7 @@ package cli import ( + "bufio" "fmt" "strings" @@ -64,8 +65,9 @@ Where proposal.json contains: ), ), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) proposal, err := paramscutils.ParseParamChangeProposalJSON(cdc, args[0]) if err != nil { diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index 32ed991316..e6bb0b44e9 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -1,6 +1,8 @@ package cli import ( + "bufio" + "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -40,8 +42,9 @@ func GetCmdUnjail(cdc *codec.Codec) *cobra.Command { $ tx slashing unjail --from mykey `, RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) valAddr := cliCtx.GetFromAddress() diff --git a/x/staking/client/cli/tx.go b/x/staking/client/cli/tx.go index dd86aa7b84..5898a25933 100644 --- a/x/staking/client/cli/tx.go +++ b/x/staking/client/cli/tx.go @@ -1,6 +1,7 @@ package cli import ( + "bufio" "fmt" "os" "strings" @@ -49,8 +50,9 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { Use: "create-validator", Short: "create new validator initialized with a self-delegation to it", RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) txBldr, msg, err := BuildCreateValidatorMsg(cliCtx, txBldr) if err != nil { @@ -85,8 +87,9 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { Use: "edit-validator", Short: "edit an existing validator account", RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(auth.DefaultTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) valAddr := cliCtx.GetFromAddress() description := types.NewDescription( @@ -150,8 +153,9 @@ $ %s tx staking delegate cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 10 ), ), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(auth.DefaultTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) amount, err := sdk.ParseCoin(args[1]) if err != nil { @@ -186,8 +190,9 @@ $ %s tx staking redelegate cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj ), ), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(auth.DefaultTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) delAddr := cliCtx.GetFromAddress() valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) @@ -227,8 +232,9 @@ $ %s tx staking unbond cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100s ), ), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(auth.DefaultTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) delAddr := cliCtx.GetFromAddress() valAddr, err := sdk.ValAddressFromBech32(args[0]) diff --git a/x/upgrade/client/cli/tx.go b/x/upgrade/client/cli/tx.go index b4c34e2312..26aa0848c2 100644 --- a/x/upgrade/client/cli/tx.go +++ b/x/upgrade/client/cli/tx.go @@ -1,6 +1,7 @@ package cli import ( + "bufio" "fmt" "time" @@ -85,8 +86,9 @@ func GetCmdSubmitUpgradeProposal(cdc *codec.Codec) *cobra.Command { return err } - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) from := cliCtx.GetFromAddress() depositStr, err := cmd.Flags().GetString(cli.FlagDeposit) @@ -124,8 +126,9 @@ func GetCmdSubmitCancelUpgradeProposal(cdc *codec.Codec) *cobra.Command { Short: "Submit a software upgrade proposal", Long: "Cancel a software upgrade along with an initial deposit.", RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) from := cliCtx.GetFromAddress() depositStr, err := cmd.Flags().GetString(cli.FlagDeposit)