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)