Skip to content

Commit

Permalink
Drop on-disk keybase in favor of keyring (#164)
Browse files Browse the repository at this point in the history
* Port to new keyring

* modify add-genesis-account to use keyring

* Adapt lcd tests to use keyring

* Attempt to fix localnet ci job

* Revert "Attempt to fix localnet ci job"

This reverts commit c299dd9.

* Try pass env var to docker container

* localnet does need COSMOS_SDK_TEST_KEYRING to function properly

* Revert tiny change

* Simplify tests

* Refresh against sdk's respective branch latest commit

* Refresh against sdk branch's latest commit

* Attempt to reduce diff size

* Refresh against latest commit

* Update against latest commit

* Refresh against sdk's latest master

* Avoid create temporary keybase

* Revert "Avoid create temporary keybase"

This reverts commit c3f6d77.

* Update docs

* Remove unnecessary arguments

* Remove unnecessary arguments

* Code cleanup

* Refresh sdk module, add upgrade module

* Refresh deps

* Fix merge

* Cleanup, AddrSeed does not need Password anymore
  • Loading branch information
Alessio Treglia authored and fedekunze committed Nov 18, 2019
1 parent 967fc43 commit 71f3b4f
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 220 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ LEDGER_ENABLED ?= true
SDK_PACK := $(shell go list -m github.com/cosmos/cosmos-sdk | sed 's/ /\@/g')

export GO111MODULE = on
export COSMOS_SDK_TEST_KEYRING = y

# process build tags

Expand Down Expand Up @@ -157,7 +158,7 @@ build-docker-gaiadnode:

# Run a 4-node testnet locally
localnet-start: build-linux localnet-stop
@if ! [ -f build/node0/gaiad/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/gaiad:Z tendermint/gaiadnode testnet --v 4 -o . --starting-ip-address 192.168.10.2 ; fi
@if ! [ -f build/node0/gaiad/config/genesis.json ]; then docker run -e COSMOS_SDK_TEST_KEYRING=y --rm -v $(CURDIR)/build:/gaiad:Z tendermint/gaiadnode testnet --v 4 -o . --starting-ip-address 192.168.10.2 ; fi
docker-compose up -d

# Stop testnet
Expand Down
3 changes: 1 addition & 2 deletions cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -828,9 +828,8 @@ func TestGaiaCLIValidateSignatures(t *testing.T) {
defer os.Remove(unsignedTxFile.Name())

// validate we can successfully sign
success, stdout, stderr = f.TxSign(keyFoo, unsignedTxFile.Name())
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name())
require.True(t, success)
require.Empty(t, stderr)
stdTx := unmarshalStdTx(t, stdout)
require.Equal(t, len(stdTx.Msgs), 1)
require.Equal(t, 1, len(stdTx.GetSignatures()))
Expand Down
10 changes: 5 additions & 5 deletions cli_test/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,13 @@ func (f *Fixtures) AddGenesisAccount(address sdk.AccAddress, coins sdk.Coins, fl
// GenTx is gaiad gentx
func (f *Fixtures) GenTx(name string, flags ...string) {
cmd := fmt.Sprintf("%s gentx --name=%s --home=%s --home-client=%s", f.GaiadBinary, name, f.GaiadHome, f.GaiacliHome)
executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass)
executeWriteCheckErr(f.T, addFlags(cmd, flags))
}

// CollectGenTxs is gaiad collect-gentxs
func (f *Fixtures) CollectGenTxs(flags ...string) {
cmd := fmt.Sprintf("%s collect-gentxs --home=%s", f.GaiadBinary, f.GaiadHome)
executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass)
executeWriteCheckErr(f.T, addFlags(cmd, flags))
}

// GDStart runs gaiad start with the appropriate flags and returns a process
Expand Down Expand Up @@ -271,19 +271,19 @@ func (f *Fixtures) KeysDelete(name string, flags ...string) {
// KeysAdd is gaiacli keys add
func (f *Fixtures) KeysAdd(name string, flags ...string) {
cmd := fmt.Sprintf("%s keys add --home=%s %s", f.GaiacliBinary, f.GaiacliHome, name)
executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass)
executeWriteCheckErr(f.T, addFlags(cmd, flags))
}

// KeysAddRecover prepares gaiacli keys add --recover
func (f *Fixtures) KeysAddRecover(name, mnemonic string, flags ...string) (exitSuccess bool, stdout, stderr string) {
cmd := fmt.Sprintf("%s keys add --home=%s --recover %s", f.GaiacliBinary, f.GaiacliHome, name)
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass, mnemonic)
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), mnemonic)
}

// KeysAddRecoverHDPath prepares gaiacli keys add --recover --account --index
func (f *Fixtures) KeysAddRecoverHDPath(name, mnemonic string, account uint32, index uint32, flags ...string) {
cmd := fmt.Sprintf("%s keys add --home=%s --recover %s --account %d --index %d", f.GaiacliBinary, f.GaiacliHome, name, account, index)
executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass, mnemonic)
executeWriteCheckErr(f.T, addFlags(cmd, flags), mnemonic)
}

// KeysShow is gaiacli keys show
Expand Down
1 change: 1 addition & 0 deletions cmd/gaiacli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func main() {
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
config.SetKeyringServiceName("gaia")
config.Seal()

// TODO: setup keybase, viper object, etc. to be passed into
Expand Down
6 changes: 4 additions & 2 deletions cmd/gaiad/genaccounts.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bufio"
"errors"
"fmt"

Expand Down Expand Up @@ -40,14 +41,15 @@ the address will be looked up in the local Keybase. The list of initial tokens m
contain valid denominations. Accounts may optionally be supplied with vesting parameters.
`,
Args: cobra.ExactArgs(2),
RunE: func(_ *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, args []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))

addr, err := sdk.AccAddressFromBech32(args[0])
inBuf := bufio.NewReader(cmd.InOrStdin())
if err != nil {
// attempt to lookup address from Keybase if no address was provided
kb, err := keys.NewKeyBaseFromDir(viper.GetString(flagClientHome))
kb, err := keys.NewKeyringFromDir(viper.GetString(flagClientHome), inBuf)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions cmd/gaiad/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func main() {
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
config.SetKeyringServiceName("gaia")
config.Seal()

ctx := server.NewDefaultContext()
Expand Down
27 changes: 6 additions & 21 deletions cmd/gaiad/testnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func InitTestnet(cmd *cobra.Command, config *tmconfig.Config, cdc *codec.Codec,
genFiles []string
)

inBuf := bufio.NewReader(cmd.InOrStdin())
// generate private keys, node IDs, and initial transactions
for i := 0; i < numValidators; i++ {
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
Expand Down Expand Up @@ -157,24 +158,13 @@ func InitTestnet(cmd *cobra.Command, config *tmconfig.Config, cdc *codec.Codec,
memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip)
genFiles = append(genFiles, config.GenesisFile())

buf := bufio.NewReader(cmd.InOrStdin())
prompt := fmt.Sprintf(
"Password for account '%s' (default %s):", nodeDirName, client.DefaultKeyPass,
)

keyPass, err := client.GetPassword(prompt, buf)
if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from
// STDIN or the given password is not empty but failed to meet minimum
// length requirements.
kb, err := keys.NewKeyringFromDir(clientDir, inBuf)
if err != nil {
return err
}

if keyPass == "" {
keyPass = client.DefaultKeyPass
}

addr, secret, err := server.GenerateSaveCoinKey(clientDir, nodeDirName, keyPass, true)
keyPass := client.DefaultKeyPass
addr, secret, err := server.GenerateSaveCoinKey(kb, nodeDirName, keyPass, true)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
Expand Down Expand Up @@ -210,13 +200,8 @@ func InitTestnet(cmd *cobra.Command, config *tmconfig.Config, cdc *codec.Codec,
sdk.OneInt(),
)

kb, err := keys.NewKeyBaseFromDir(clientDir)
if err != nil {
return err
}

tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo)
txBldr := auth.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo).WithKeybase(kb)
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithChainID(chainID).WithMemo(memo).WithKeybase(kb)

signedTx, err := txBldr.SignStdTx(nodeDirName, client.DefaultKeyPass, tx, false)
if err != nil {
Expand Down
25 changes: 22 additions & 3 deletions docs/delegator-guide-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,20 @@ To restore an account using a fundraiser mnemonic and store the associated encry
gaiacli keys add <yourKeyName> --recover
```

You will be prompted to input a passphrase that is used to encrypt the private key of account `0` on disk. Each time you want to send a transaction, this password will be required. If you lose the password, you can always recover the private key with the mnemonic.

- `<yourKeyName>` is the name of the account. It is a reference to the account number used to derive the key pair from the mnemonic. You will use this name to identify your account when you want to send a transaction.
- You can add the optional `--account` flag to specify the path (`0`, `1`, `2`, ...) you want to use to generate your account. By default, account `0` is generated.

The private key of account `0` will be saved in your operating system's credentials storage.
Each time you want to send a transaction, you will need to unlock your system's credentials store.
If you lose access to your credentials storage, you can always recover the private key with the
mnemonic.

::: tip
**You may not be prompted for password each time you send a transaction since most operating systems
unlock user's credentials store upon login by default. If you want to change your credentials
store security policies please refer to your operating system manual.**
:::

### Creating an Account

To create an account, you just need to have `gaiacli` installed. Before creating it, you need to know where you intend to store and interact with your private keys. The best options are to store them in an offline dedicated computer or a ledger device. Storing them on your regular online computer involves more risk, since anyone who infiltrates your computer through the internet could exfiltrate your private keys and steal your funds.
Expand Down Expand Up @@ -197,7 +206,17 @@ To generate an account, just use the following command:
gaiacli keys add <yourKeyName>
```

The command will generate a 24-words mnemonic and save the private and public keys for account `0` at the same time. You will be prompted to input a passphrase that is used to encrypt the private key of account `0` on disk. Each time you want to send a transaction, this password will be required. If you lose the password, you can always recover the private key with the mnemonic.
The command will generate a 24-words mnemonic and save the private and public keys for account `0`
at the same time.
Each time you want to send a transaction, you will need to unlock your system's credentials store.
If you lose access to your credentials storage, you can always recover the private key with the
mnemonic.

::: tip
**You may not be prompted for password each time you send a transaction since most operating systems
unlock user's credentials store upon login by default. If you want to change your credentials
store security policies please refer to your operating system manual.**
:::

::: danger
**Do not lose or share your 12 words with anyone. To prevent theft or loss of funds, it is best to ensure that you keep multiple copies of your mnemonic, and store it in a safe, secure place and that only you know how to access. If someone is able to gain access to your mnemonic, they will be able to gain access to your private keys and control the accounts associated with them.**
Expand Down
21 changes: 17 additions & 4 deletions docs/gaiacli.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ There are three types of key representations that are used:
- Get this value with `gaiad tendermint show-validator`
- e.g. `cosmosvalconspub1zcjduepq0ms2738680y72v44tfyqm3c9ppduku8fs6sr73fx7m666sjztznqzp2emf`

#### Migrate Keys From Legacy On-Disk Keybase To OS Built-in Secret Store

Older versions of `gaiacli` used store keys in the user's home directory. If you are migrating
from an old version of `gaiacli` you will need to migrate your old keys into your operating system's
credentials storage by running the following command:

```bash
gaiacli keys migrate
```

The command will prompt for every passphrase. If a passphrase is incorrect, it will skip the
respective key.

#### Generate Keys

You'll need an account private and public key pair \(a.k.a. `sk, pk` respectively\) to be able to receive funds, send txs, bond tx, etc.
Expand All @@ -72,10 +85,10 @@ To generate a new _secp256k1_ key:
gaiacli keys add <account_name>
```

Next, you will have to create a passphrase to protect the key on disk. The output of the above
command will contain a _seed phrase_. It is recommended to save the _seed phrase_ in a safe
place so that in case you forget the password, you could eventually regenerate the key from
the seed phrase with the following command:
The output of the above command will contain a _seed phrase_. It is recommended to save the _seed
phrase_ in a safe place so that in case you forget the password of the operating system's
credentials store, you could eventually regenerate the key from the seed phrase with the
following command:

```bash
gaiacli keys add --recover
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.13

require (
github.com/btcsuite/btcd v0.0.0-20190807005414-4063feeff79a // indirect
github.com/cosmos/cosmos-sdk v0.34.4-0.20191108144056-d81d46192a0c
github.com/cosmos/cosmos-sdk v0.34.4-0.20191114141721-d4c831e63ad3
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d // indirect
github.com/golang/mock v1.3.1 // indirect
github.com/onsi/ginkgo v1.8.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cosmos/cosmos-sdk v0.34.4-0.20191108144056-d81d46192a0c h1:XYIUjgiFabdbRTvhqeY9Dc7k95bvFbgC47mv/Dk13S8=
github.com/cosmos/cosmos-sdk v0.34.4-0.20191108144056-d81d46192a0c/go.mod h1:0sLtjU+qex1OfpGQHjWhJByx74IoH78R742PEAdoQJk=
github.com/cosmos/cosmos-sdk v0.34.4-0.20191114141721-d4c831e63ad3 h1:vjzTX8arh3cEAHSbxazdRRLS67Gl4JoNY6tcNyjqmms=
github.com/cosmos/cosmos-sdk v0.34.4-0.20191114141721-d4c831e63ad3/go.mod h1:0sLtjU+qex1OfpGQHjWhJByx74IoH78R742PEAdoQJk=
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU=
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
Expand Down
15 changes: 6 additions & 9 deletions lcd_test/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,19 +371,19 @@ func init() {

// CreateAddr adds an address to the key store and returns an address and seed.
// It also requires that the key could be created.
func CreateAddr(name, password string, kb crkeys.Keybase) (sdk.AccAddress, string, error) {
func CreateAddr(name string, kb crkeys.Keybase) (sdk.AccAddress, string, error) {
var (
err error
info crkeys.Info
seed string
)
info, seed, err = kb.CreateMnemonic(name, crkeys.English, password, crkeys.Secp256k1)
info, seed, err = kb.CreateMnemonic(name, crkeys.English, client.DefaultKeyPass, crkeys.Secp256k1)
return sdk.AccAddress(info.GetPubKey().Address()), seed, err
}

// CreateAddr adds multiple address to the key store and returns the addresses and associated seeds in lexographical order by address.
// It also requires that the keys could be created.
func CreateAddrs(kb crkeys.Keybase, numAddrs int) (addrs []sdk.AccAddress, seeds, names, passwords []string, errs []error) {
func CreateAddrs(kb crkeys.Keybase, numAddrs int) (addrs []sdk.AccAddress, seeds, names []string, errs []error) {
var (
err error
info crkeys.Info
Expand All @@ -394,12 +394,11 @@ func CreateAddrs(kb crkeys.Keybase, numAddrs int) (addrs []sdk.AccAddress, seeds

for i := 0; i < numAddrs; i++ {
name := fmt.Sprintf("test%d", i)
password := "1234567890"
info, seed, err = kb.CreateMnemonic(name, crkeys.English, password, crkeys.Secp256k1)
info, seed, err = kb.CreateMnemonic(name, crkeys.English, client.DefaultKeyPass, crkeys.Secp256k1)
if err != nil {
errs = append(errs, err)
}
addrSeeds = append(addrSeeds, AddrSeed{Address: sdk.AccAddress(info.GetPubKey().Address()), Seed: seed, Name: name, Password: password})
addrSeeds = append(addrSeeds, AddrSeed{Address: sdk.AccAddress(info.GetPubKey().Address()), Seed: seed, Name: name})
}
if len(errs) > 0 {
return
Expand All @@ -411,18 +410,16 @@ func CreateAddrs(kb crkeys.Keybase, numAddrs int) (addrs []sdk.AccAddress, seeds
addrs = append(addrs, addrSeeds[i].Address)
seeds = append(seeds, addrSeeds[i].Seed)
names = append(names, addrSeeds[i].Name)
passwords = append(passwords, addrSeeds[i].Password)
}

return addrs, seeds, names, passwords, errs
return addrs, seeds, names, errs
}

// AddrSeed combines an Address with the mnemonic of the private key to that address
type AddrSeed struct {
Address sdk.AccAddress
Seed string
Name string
Password string
}

// AddrSeedSlice implements `Interface` in sort package.
Expand Down
Loading

0 comments on commit 71f3b4f

Please sign in to comment.