Skip to content

Commit

Permalink
Drop on-disk keybase in favor of keyring (#5180)
Browse files Browse the repository at this point in the history
* 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 <name> [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
  • Loading branch information
Alessio Treglia authored and fedekunze committed Nov 14, 2019
1 parent 1285782 commit d4c831e
Show file tree
Hide file tree
Showing 34 changed files with 418 additions and 308 deletions.
9 changes: 4 additions & 5 deletions client/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
44 changes: 34 additions & 10 deletions client/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
16 changes: 2 additions & 14 deletions client/keys/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
}
Expand Down Expand Up @@ -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) != "" {
Expand Down Expand Up @@ -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
}
Expand Down
64 changes: 40 additions & 24 deletions client/keys/add_ledger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -17,6 +17,7 @@ import (
)

func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
runningUnattended := isRunningUnattended()
config := sdk.GetConfig()

bech32PrefixAccAddr := "terra"
Expand All @@ -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)
Expand All @@ -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()))

Expand All @@ -71,34 +79,42 @@ 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)

/// 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()))
}
49 changes: 28 additions & 21 deletions client/keys/add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -13,6 +14,7 @@ import (
)

func Test_runAddCmdBasic(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := addKeyCommand()
assert.NotNil(t, cmd)
mockIn, _, _ := tests.ApplyMockIO(cmd)
Expand All @@ -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)

viper.Set(cli.OutputFlag, OutputFormatText)

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\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"}))

if runningUnattended {
mockIn.Reset("testpass1\nN\n")
} else {
mockIn.Reset("N\n")
}
assert.Error(t, runAddCmd(cmd, []string{"keyname1"}))

if runningUnattended {
mockIn.Reset("testpass1\nN\n")
} else {
mockIn.Reset("y\n")
}
err := runAddCmd(cmd, []string{"keyname2"})
assert.NoError(t, err)
}
Loading

0 comments on commit d4c831e

Please sign in to comment.