Enter the new keyring interface (#5904)
crypto/keyring: `Keybase` interface gives way to its successor: `Keyring`. `LegacyKeybase` interface is added in order to guarantee limited backward compatibility with the old `Keybase` interface for the sole purpose of migrating keys across the new keyring backends. The package no longer depends on the `github.com/types.Config` singleton. `SupportedAlgos` and `SupportedLedgerAlgos` methods have been removed. The keyring just fails when trying to perform an action with an unsupported algorithm. crypto/ subdirs reorganization: `crypto/keys/hd` was moved to `crypto/hd`, which now groups together all HD wallets related types and utilities. client/input: * Removal of unnecessary `GetCheckPassword`, `PrintPrefixed` functions. * `GetConfirmation`'s signature changed to take in a io.Writer for better integration with `cobra.Command` types. client/context: * In-memory keyring is allocated in the context when `--gen-only` flag is passed in. `GetFromFields` does no longer silently allocate a keyring, it takes one as argument. Co-authored with @jgimeno Co-authored-by: Jonathan Gimeno <jgimeno@gmail.com>
This commit is contained in:
parent
7325692550
commit
a1feca39c2
23
CHANGELOG.md
23
CHANGELOG.md
|
@ -70,19 +70,22 @@ and provided directly the IAVL store.
|
||||||
* (modules) [\#5572](https://github.com/cosmos/cosmos-sdk/pull/5572) Move account balance logic and APIs from `x/auth` to `x/bank`.
|
* (modules) [\#5572](https://github.com/cosmos/cosmos-sdk/pull/5572) Move account balance logic and APIs from `x/auth` to `x/bank`.
|
||||||
* (types) [\#5533](https://github.com/cosmos/cosmos-sdk/pull/5533) Refactored `AppModuleBasic` and `AppModuleGenesis`
|
* (types) [\#5533](https://github.com/cosmos/cosmos-sdk/pull/5533) Refactored `AppModuleBasic` and `AppModuleGenesis`
|
||||||
to now accept a `codec.JSONMarshaler` for modular serialization of genesis state.
|
to now accept a `codec.JSONMarshaler` for modular serialization of genesis state.
|
||||||
* (crypto/keyring) [\#5735](https://github.com/cosmos/cosmos-sdk/pull/5735) Keyring's `Update()` function is now no-op.
|
|
||||||
* (types/rest) [\#5779](https://github.com/cosmos/cosmos-sdk/pull/5779) Drop unused Parse{Int64OrReturnBadRequest,QueryParamBool}() functions.
|
* (types/rest) [\#5779](https://github.com/cosmos/cosmos-sdk/pull/5779) Drop unused Parse{Int64OrReturnBadRequest,QueryParamBool}() functions.
|
||||||
* (keys) [\#5820](https://github.com/cosmos/cosmos-sdk/pull/5820/) Removed method CloseDB from Keybase interface.
|
* (keys) [\#5820](https://github.com/cosmos/cosmos-sdk/pull/5820/) Removed method CloseDB from Keybase interface.
|
||||||
* (baseapp) [\#5837](https://github.com/cosmos/cosmos-sdk/issues/5837) Transaction simulation now returns a `SimulationResponse` which contains the `GasInfo` and
|
* (baseapp) [\#5837](https://github.com/cosmos/cosmos-sdk/issues/5837) Transaction simulation now returns a `SimulationResponse` which contains the `GasInfo` and
|
||||||
`Result` from the execution.
|
`Result` from the execution.
|
||||||
* (crypto/keyring) [\#5866](https://github.com/cosmos/cosmos-sdk/pull/5866) Move `Keyring` and `Keybase` implementations and their associated types from `crypto/keys/` to `crypto/keyring/`.
|
* (client/input) [\#5904](https://github.com/cosmos/cosmos-sdk/pull/5904) Removal of unnecessary `GetCheckPassword`, `PrintPrefixed` functions.
|
||||||
* (crypto) [\#5880](https://github.com/cosmos/cosmos-sdk/pull/5880) Merge `crypto/keys/mintkey` into `crypto`.
|
|
||||||
* (crypto/keyring) [\#5858](https://github.com/cosmos/cosmos-sdk/pull/5858) Make Keyring store keys by name and address's hexbytes representation.
|
|
||||||
* (crypto/keyring) [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) Deprecate old keybase implementation:
|
|
||||||
- Remove `Update` from the `Keybase` interface.
|
|
||||||
- `NewKeyring()` now accepts a new backend: `MemoryBackend`.
|
|
||||||
- `New()` has been renamed to`NewLegacy()`, which now returns a `LegacyKeybase` type that only allows migration of keys from the legacy keybase to a new keyring.
|
|
||||||
* (client/keys) [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) Rename `NewKeyBaseFromDir()` -> `NewLegacyKeyBaseFromDir()`.
|
* (client/keys) [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) Rename `NewKeyBaseFromDir()` -> `NewLegacyKeyBaseFromDir()`.
|
||||||
|
* (crypto) [\#5880](https://github.com/cosmos/cosmos-sdk/pull/5880) Merge `crypto/keys/mintkey` into `crypto`.
|
||||||
|
* (crypto/hd) [\#5904](https://github.com/cosmos/cosmos-sdk/pull/5904) `crypto/keys/hd` moved to `crypto/hd`.
|
||||||
|
* (crypto/keyring):
|
||||||
|
- [\#5866](https://github.com/cosmos/cosmos-sdk/pull/5866) Rename `crypto/keys/` to `crypto/keyring/`.
|
||||||
|
- [\#5904](https://github.com/cosmos/cosmos-sdk/pull/5904) `Keybase` -> `Keyring` interfaces migration. `LegacyKeybase` interface is added in order
|
||||||
|
to guarantee limited backward compatibility with the old Keybase interface for the sole purpose of migrating keys across the new keyring backends. `NewLegacy`
|
||||||
|
constructor is provided [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) to allow for smooth migration of keys from the legacy LevelDB based implementation
|
||||||
|
to new keyring backends. Plus, the package and the new keyring no longer depends on the sdk.Config singleton. Please consult the package documentation for more
|
||||||
|
information on how to implement the new `Keyring` interface.
|
||||||
|
- [\#5858](https://github.com/cosmos/cosmos-sdk/pull/5858) Make Keyring store keys by name and address's hexbytes representation.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
@ -92,8 +95,8 @@ to now accept a `codec.JSONMarshaler` for modular serialization of genesis state
|
||||||
|
|
||||||
* (types) [\#5741](https://github.com/cosmos/cosmos-sdk/issues/5741) Prevent ChainAnteDecorators() from panicking when empty AnteDecorator slice is supplied.
|
* (types) [\#5741](https://github.com/cosmos/cosmos-sdk/issues/5741) Prevent ChainAnteDecorators() from panicking when empty AnteDecorator slice is supplied.
|
||||||
* (modules) [\#5569](https://github.com/cosmos/cosmos-sdk/issues/5569) `InitGenesis`, for the relevant modules, now ensures module accounts exist.
|
* (modules) [\#5569](https://github.com/cosmos/cosmos-sdk/issues/5569) `InitGenesis`, for the relevant modules, now ensures module accounts exist.
|
||||||
* (crypto/keyring) [\#5844](https://github.com/cosmos/cosmos-sdk/pull/5844) Keybase/Keyring `Sign()` methods no longer decode amino signatures
|
* (crypto/keyring) [\#5844](https://github.com/cosmos/cosmos-sdk/pull/5844) `Keyring.Sign()` methods no longer decode amino signatures when method receivers
|
||||||
when method receivers are offline/multisig keys.
|
are offline/multisig keys.
|
||||||
* (x/auth) [\#5892](https://github.com/cosmos/cosmos-sdk/pull/5892) Add `RegisterKeyTypeCodec` to register new
|
* (x/auth) [\#5892](https://github.com/cosmos/cosmos-sdk/pull/5892) Add `RegisterKeyTypeCodec` to register new
|
||||||
types (eg. keys) to the `auth` module internal amino codec.
|
types (eg. keys) to the `auth` module internal amino codec.
|
||||||
* (rest) [\#5906](https://github.com/cosmos/cosmos-sdk/pull/5906) Fix an issue that make some REST calls panic when sending
|
* (rest) [\#5906](https://github.com/cosmos/cosmos-sdk/pull/5906) Fix an issue that make some REST calls panic when sending
|
||||||
|
|
|
@ -25,8 +25,8 @@ type CLIContext struct {
|
||||||
Client rpcclient.Client
|
Client rpcclient.Client
|
||||||
ChainID string
|
ChainID string
|
||||||
Marshaler codec.Marshaler
|
Marshaler codec.Marshaler
|
||||||
Keybase keyring.Keybase
|
|
||||||
Input io.Reader
|
Input io.Reader
|
||||||
|
Keyring keyring.Keyring
|
||||||
Output io.Writer
|
Output io.Writer
|
||||||
OutputFormat string
|
OutputFormat string
|
||||||
Height int64
|
Height int64
|
||||||
|
@ -58,8 +58,19 @@ func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext {
|
||||||
var nodeURI string
|
var nodeURI string
|
||||||
var rpc rpcclient.Client
|
var rpc rpcclient.Client
|
||||||
|
|
||||||
|
homedir := viper.GetString(flags.FlagHome)
|
||||||
genOnly := viper.GetBool(flags.FlagGenerateOnly)
|
genOnly := viper.GetBool(flags.FlagGenerateOnly)
|
||||||
fromAddress, fromName, err := GetFromFields(input, from, genOnly)
|
backend := viper.GetString(flags.FlagKeyringBackend)
|
||||||
|
if len(backend) == 0 {
|
||||||
|
backend = keyring.BackendMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
keyring, err := newKeyringFromFlags(backend, homedir, input, genOnly)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("couldn't acquire keyring: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
fromAddress, fromName, err := GetFromFields(keyring, from, genOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to get from fields: %v\n", err)
|
fmt.Printf("failed to get from fields: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -84,9 +95,10 @@ func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext {
|
||||||
Output: os.Stdout,
|
Output: os.Stdout,
|
||||||
NodeURI: nodeURI,
|
NodeURI: nodeURI,
|
||||||
From: viper.GetString(flags.FlagFrom),
|
From: viper.GetString(flags.FlagFrom),
|
||||||
|
Keyring: keyring,
|
||||||
OutputFormat: viper.GetString(cli.OutputFlag),
|
OutputFormat: viper.GetString(cli.OutputFlag),
|
||||||
Height: viper.GetInt64(flags.FlagHeight),
|
Height: viper.GetInt64(flags.FlagHeight),
|
||||||
HomeDir: viper.GetString(flags.FlagHome),
|
HomeDir: homedir,
|
||||||
TrustNode: viper.GetBool(flags.FlagTrustNode),
|
TrustNode: viper.GetBool(flags.FlagTrustNode),
|
||||||
UseLedger: viper.GetBool(flags.FlagUseLedger),
|
UseLedger: viper.GetBool(flags.FlagUseLedger),
|
||||||
BroadcastMode: viper.GetString(flags.FlagBroadcastMode),
|
BroadcastMode: viper.GetString(flags.FlagBroadcastMode),
|
||||||
|
@ -129,6 +141,12 @@ func NewCLIContextWithInput(input io.Reader) CLIContext {
|
||||||
return NewCLIContextWithInputAndFrom(input, viper.GetString(flags.FlagFrom))
|
return NewCLIContextWithInputAndFrom(input, viper.GetString(flags.FlagFrom))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithKeyring returns a copy of the context with an updated keyring.
|
||||||
|
func (ctx CLIContext) WithKeyring(k keyring.Keyring) CLIContext {
|
||||||
|
ctx.Keyring = k
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
// WithInput returns a copy of the context with an updated input.
|
// WithInput returns a copy of the context with an updated input.
|
||||||
func (ctx CLIContext) WithInput(r io.Reader) CLIContext {
|
func (ctx CLIContext) WithInput(r io.Reader) CLIContext {
|
||||||
ctx.Input = r
|
ctx.Input = r
|
||||||
|
@ -307,7 +325,7 @@ func (ctx CLIContext) PrintOutput(toPrint interface{}) error {
|
||||||
// GetFromFields returns a from account address and Keybase name given either
|
// 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
|
// an address or key name. If genOnly is true, only a valid Bech32 cosmos
|
||||||
// address is returned.
|
// address is returned.
|
||||||
func GetFromFields(input io.Reader, from string, genOnly bool) (sdk.AccAddress, string, error) {
|
func GetFromFields(kr keyring.Keyring, from string, genOnly bool) (sdk.AccAddress, string, error) {
|
||||||
if from == "" {
|
if from == "" {
|
||||||
return nil, "", nil
|
return nil, "", nil
|
||||||
}
|
}
|
||||||
|
@ -321,20 +339,14 @@ func GetFromFields(input io.Reader, from string, genOnly bool) (sdk.AccAddress,
|
||||||
return addr, "", nil
|
return addr, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
keybase, err := keyring.NewKeyring(sdk.KeyringServiceName(),
|
|
||||||
viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), input)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var info keyring.Info
|
var info keyring.Info
|
||||||
if addr, err := sdk.AccAddressFromBech32(from); err == nil {
|
if addr, err := sdk.AccAddressFromBech32(from); err == nil {
|
||||||
info, err = keybase.GetByAddress(addr)
|
info, err = kr.KeyByAddress(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
info, err = keybase.Get(from)
|
info, err = kr.Key(from)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
@ -342,3 +354,10 @@ func GetFromFields(input io.Reader, from string, genOnly bool) (sdk.AccAddress,
|
||||||
|
|
||||||
return info.GetAddress(), info.GetName(), nil
|
return info.GetAddress(), info.GetName(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newKeyringFromFlags(backend, homedir string, input io.Reader, genOnly bool) (keyring.Keyring, error) {
|
||||||
|
if genOnly {
|
||||||
|
return keyring.New(sdk.KeyringServiceName(), keyring.BackendMemory, homedir, input)
|
||||||
|
}
|
||||||
|
return keyring.New(sdk.KeyringServiceName(), backend, homedir, input)
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
package context
|
package context_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,7 +18,7 @@ func TestCLIContext_WithOffline(t *testing.T) {
|
||||||
viper.Set(flags.FlagOffline, true)
|
viper.Set(flags.FlagOffline, true)
|
||||||
viper.Set(flags.FlagNode, "tcp://localhost:26657")
|
viper.Set(flags.FlagNode, "tcp://localhost:26657")
|
||||||
|
|
||||||
ctx := NewCLIContext()
|
ctx := context.NewCLIContext()
|
||||||
require.True(t, ctx.Offline)
|
require.True(t, ctx.Offline)
|
||||||
require.Nil(t, ctx.Client)
|
require.Nil(t, ctx.Client)
|
||||||
|
|
||||||
|
@ -24,7 +27,7 @@ func TestCLIContext_WithOffline(t *testing.T) {
|
||||||
viper.Set(flags.FlagOffline, false)
|
viper.Set(flags.FlagOffline, false)
|
||||||
viper.Set(flags.FlagNode, "tcp://localhost:26657")
|
viper.Set(flags.FlagNode, "tcp://localhost:26657")
|
||||||
|
|
||||||
ctx = NewCLIContext()
|
ctx = context.NewCLIContext()
|
||||||
require.False(t, ctx.Offline)
|
require.False(t, ctx.Offline)
|
||||||
require.NotNil(t, ctx.Client)
|
require.NotNil(t, ctx.Client)
|
||||||
}
|
}
|
||||||
|
@ -59,10 +62,26 @@ func TestCLIContext_WithGenOnly(t *testing.T) {
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tt := tt
|
tt := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
ctx := NewCLIContextWithFrom(tt.from)
|
ctx := context.NewCLIContextWithFrom(tt.from)
|
||||||
|
|
||||||
require.Equal(t, tt.expectedFromAddr, ctx.FromAddress)
|
require.Equal(t, tt.expectedFromAddr, ctx.FromAddress)
|
||||||
require.Equal(t, tt.expectedFromName, ctx.FromName)
|
require.Equal(t, tt.expectedFromName, ctx.FromName)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCLIContext_WithKeyring(t *testing.T) {
|
||||||
|
viper.Set(flags.FlagGenerateOnly, true)
|
||||||
|
ctx := context.NewCLIContextWithFrom("cosmos1q7380u26f7ntke3facjmynajs4umlr329vr4ja")
|
||||||
|
require.NotNil(t, ctx.Keyring)
|
||||||
|
kr := ctx.Keyring
|
||||||
|
ctx = ctx.WithKeyring(nil)
|
||||||
|
require.Nil(t, ctx.Keyring)
|
||||||
|
ctx = ctx.WithKeyring(kr)
|
||||||
|
require.Equal(t, kr, ctx.Keyring)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
viper.Set(flags.FlagKeyringBackend, keyring.BackendMemory)
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ package input
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -36,40 +36,15 @@ func GetPassword(prompt string, buf *bufio.Reader) (pass string, err error) {
|
||||||
return pass, nil
|
return pass, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCheckPassword will prompt for a password twice to verify they
|
|
||||||
// match (for creating a new password).
|
|
||||||
// It enforces the password length. Only parses password once if
|
|
||||||
// input is piped in.
|
|
||||||
func GetCheckPassword(prompt, prompt2 string, buf *bufio.Reader) (string, error) {
|
|
||||||
// simple read on no-tty
|
|
||||||
if !inputIsTty() {
|
|
||||||
return GetPassword(prompt, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: own function???
|
|
||||||
pass, err := GetPassword(prompt, buf)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
pass2, err := GetPassword(prompt2, buf)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if pass != pass2 {
|
|
||||||
return "", errors.New("passphrases don't match")
|
|
||||||
}
|
|
||||||
return pass, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetConfirmation will request user give the confirmation from stdin.
|
// GetConfirmation will request user give the confirmation from stdin.
|
||||||
// "y", "Y", "yes", "YES", and "Yes" all count as confirmations.
|
// "y", "Y", "yes", "YES", and "Yes" all count as confirmations.
|
||||||
// If the input is not recognized, it returns false and a nil error.
|
// If the input is not recognized, it returns false and a nil error.
|
||||||
func GetConfirmation(prompt string, buf *bufio.Reader) (bool, error) {
|
func GetConfirmation(prompt string, r *bufio.Reader, w io.Writer) (bool, error) {
|
||||||
if inputIsTty() {
|
if inputIsTty() {
|
||||||
fmt.Printf("%s [y/N]: ", prompt)
|
fmt.Printf("%s [y/N]: ", prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := readLineFromBuf(buf)
|
response, err := readLineFromBuf(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -90,7 +65,7 @@ func GetConfirmation(prompt string, buf *bufio.Reader) (bool, error) {
|
||||||
// GetString simply returns the trimmed string output of a given reader.
|
// GetString simply returns the trimmed string output of a given reader.
|
||||||
func GetString(prompt string, buf *bufio.Reader) (string, error) {
|
func GetString(prompt string, buf *bufio.Reader) (string, error) {
|
||||||
if inputIsTty() && prompt != "" {
|
if inputIsTty() && prompt != "" {
|
||||||
PrintPrefixed(prompt)
|
fmt.Fprintf(os.Stderr, "> %s\n", prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := readLineFromBuf(buf)
|
out, err := readLineFromBuf(buf)
|
||||||
|
@ -117,9 +92,3 @@ func readLineFromBuf(buf *bufio.Reader) (string, error) {
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(pass), nil
|
return strings.TrimSpace(pass), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintPrefixed prints a string with > prefixed for use in prompts.
|
|
||||||
func PrintPrefixed(msg string) {
|
|
||||||
msg = fmt.Sprintf("> %s\n", msg)
|
|
||||||
fmt.Fprint(os.Stderr, msg)
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
"github.com/cosmos/cosmos-sdk/client/input"
|
"github.com/cosmos/cosmos-sdk/client/input"
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ const (
|
||||||
flagInteractive = "interactive"
|
flagInteractive = "interactive"
|
||||||
flagRecover = "recover"
|
flagRecover = "recover"
|
||||||
flagNoBackup = "no-backup"
|
flagNoBackup = "no-backup"
|
||||||
|
flagCoinType = "coin-type"
|
||||||
flagAccount = "account"
|
flagAccount = "account"
|
||||||
flagIndex = "index"
|
flagIndex = "index"
|
||||||
flagMultisig = "multisig"
|
flagMultisig = "multisig"
|
||||||
|
@ -73,19 +75,20 @@ the flag --nosort is set.
|
||||||
cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)")
|
cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)")
|
||||||
cmd.Flags().Bool(flags.FlagDryRun, false, "Perform action, but don't add key to local keystore")
|
cmd.Flags().Bool(flags.FlagDryRun, false, "Perform action, but don't add key to local keystore")
|
||||||
cmd.Flags().String(flagHDPath, "", "Manual HD Path derivation (overrides BIP44 config)")
|
cmd.Flags().String(flagHDPath, "", "Manual HD Path derivation (overrides BIP44 config)")
|
||||||
|
cmd.Flags().Uint32(flagCoinType, sdk.CoinType, "coin type number for HD derivation")
|
||||||
cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation")
|
cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation")
|
||||||
cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation")
|
cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation")
|
||||||
cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response")
|
cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response")
|
||||||
cmd.Flags().String(flagKeyAlgo, string(keyring.Secp256k1), "Key signing algorithm to generate keys for")
|
cmd.Flags().String(flagKeyAlgo, string(hd.Secp256k1Type), "Key signing algorithm to generate keys for")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func getKeybase(transient bool, buf io.Reader) (keyring.Keybase, error) {
|
func getKeybase(transient bool, buf io.Reader) (keyring.Keyring, error) {
|
||||||
if transient {
|
if transient {
|
||||||
return keyring.NewKeyring(sdk.KeyringServiceName(), keyring.BackendMemory, viper.GetString(flags.FlagHome), buf)
|
return keyring.New(sdk.KeyringServiceName(), keyring.BackendMemory, viper.GetString(flags.FlagHome), buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf)
|
return keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runAddCmd(cmd *cobra.Command, args []string) error {
|
func runAddCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
@ -107,7 +110,7 @@ input
|
||||||
output
|
output
|
||||||
- armor encrypted private key (saved to file)
|
- armor encrypted private key (saved to file)
|
||||||
*/
|
*/
|
||||||
func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *bufio.Reader) error {
|
func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keyring, inBuf *bufio.Reader) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
@ -115,25 +118,27 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
||||||
interactive := viper.GetBool(flagInteractive)
|
interactive := viper.GetBool(flagInteractive)
|
||||||
showMnemonic := !viper.GetBool(flagNoBackup)
|
showMnemonic := !viper.GetBool(flagNoBackup)
|
||||||
|
|
||||||
algo := keyring.SigningAlgo(viper.GetString(flagKeyAlgo))
|
algo, err := keyring.NewSigningAlgoFromString(viper.GetString(flagKeyAlgo))
|
||||||
if algo == keyring.SigningAlgo("") {
|
if err != nil {
|
||||||
algo = keyring.Secp256k1
|
algo = hd.Secp256k1
|
||||||
}
|
|
||||||
if !keyring.IsSupportedAlgorithm(kb.SupportedAlgos(), algo) {
|
|
||||||
return keyring.ErrUnsupportedSigningAlgo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !viper.GetBool(flags.FlagDryRun) {
|
if !viper.GetBool(flags.FlagDryRun) {
|
||||||
_, err = kb.Get(name)
|
_, err = kb.Key(name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// account exists, ask for user confirmation
|
// account exists, ask for user confirmation
|
||||||
response, err2 := input.GetConfirmation(fmt.Sprintf("override the existing name %s", name), inBuf)
|
response, err2 := input.GetConfirmation(fmt.Sprintf("override the existing name %s", name), inBuf, cmd.ErrOrStderr())
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return err2
|
return err2
|
||||||
}
|
}
|
||||||
if !response {
|
if !response {
|
||||||
return errors.New("aborted")
|
return errors.New("aborted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err2 = kb.Delete(name)
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
multisigKeys := viper.GetStringSlice(flagMultisig)
|
multisigKeys := viper.GetStringSlice(flagMultisig)
|
||||||
|
@ -146,7 +151,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, keyname := range multisigKeys {
|
for _, keyname := range multisigKeys {
|
||||||
k, err := kb.Get(keyname)
|
k, err := kb.Key(keyname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -161,7 +166,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pk := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks)
|
pk := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks)
|
||||||
if _, err := kb.CreateMulti(name, pk); err != nil {
|
if _, err := kb.SaveMultisig(name, pk); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,13 +180,14 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = kb.CreateOffline(name, pk, algo)
|
_, err = kb.SavePubKey(name, pk, algo.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
coinType := uint32(viper.GetInt(flagCoinType))
|
||||||
account := uint32(viper.GetInt(flagAccount))
|
account := uint32(viper.GetInt(flagAccount))
|
||||||
index := uint32(viper.GetInt(flagIndex))
|
index := uint32(viper.GetInt(flagIndex))
|
||||||
|
|
||||||
|
@ -189,7 +195,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
||||||
var hdPath string
|
var hdPath string
|
||||||
|
|
||||||
if useBIP44 {
|
if useBIP44 {
|
||||||
hdPath = keyring.CreateHDPath(account, index).String()
|
hdPath = hd.CreateHDPath(coinType, account, index).String()
|
||||||
} else {
|
} else {
|
||||||
hdPath = viper.GetString(flagHDPath)
|
hdPath = viper.GetString(flagHDPath)
|
||||||
}
|
}
|
||||||
|
@ -201,12 +207,8 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
||||||
return errors.New("cannot set custom bip32 path with ledger")
|
return errors.New("cannot set custom bip32 path with ledger")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !keyring.IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) {
|
|
||||||
return keyring.ErrUnsupportedSigningAlgo
|
|
||||||
}
|
|
||||||
|
|
||||||
bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix()
|
bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix()
|
||||||
info, err := kb.CreateLedger(name, keyring.Secp256k1, bech32PrefixAccAddr, account, index)
|
info, err := kb.SaveLedgerKey(name, hd.Secp256k1, bech32PrefixAccAddr, coinType, account, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -269,7 +271,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := kb.CreateAccount(name, mnemonic, bip39Passphrase, DefaultKeyPass, hdPath, algo)
|
info, err := kb.NewAccount(name, mnemonic, bip39Passphrase, hdPath, algo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,9 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
|
||||||
t.Cleanup(kbCleanUp)
|
t.Cleanup(kbCleanUp)
|
||||||
viper.Set(flags.FlagHome, kbHome)
|
viper.Set(flags.FlagHome, kbHome)
|
||||||
viper.Set(flags.FlagUseLedger, true)
|
viper.Set(flags.FlagUseLedger, true)
|
||||||
|
viper.Set(flagAccount, "0")
|
||||||
|
viper.Set(flagIndex, "0")
|
||||||
|
viper.Set(flagCoinType, "330")
|
||||||
|
|
||||||
/// Test Text
|
/// Test Text
|
||||||
viper.Set(cli.OutputFlag, OutputFormatText)
|
viper.Set(cli.OutputFlag, OutputFormatText)
|
||||||
|
@ -50,14 +53,14 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
|
||||||
require.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
require.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
||||||
|
|
||||||
// Now check that it has been stored properly
|
// Now check that it has been stored properly
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, kb)
|
require.NotNil(t, kb)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
kb.Delete("keyname1", "", false)
|
kb.Delete("keyname1")
|
||||||
})
|
})
|
||||||
mockIn.Reset("test1234\n")
|
mockIn.Reset("test1234\n")
|
||||||
key1, err := kb.Get("keyname1")
|
key1, err := kb.Key("keyname1")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, key1)
|
require.NotNil(t, key1)
|
||||||
|
|
||||||
|
@ -90,17 +93,18 @@ func Test_runAddCmdLedger(t *testing.T) {
|
||||||
viper.Set(cli.OutputFlag, OutputFormatText)
|
viper.Set(cli.OutputFlag, OutputFormatText)
|
||||||
// Now enter password
|
// Now enter password
|
||||||
mockIn.Reset("test1234\ntest1234\n")
|
mockIn.Reset("test1234\ntest1234\n")
|
||||||
|
viper.Set(flagCoinType, sdk.CoinType)
|
||||||
require.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
require.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
||||||
|
|
||||||
// Now check that it has been stored properly
|
// Now check that it has been stored properly
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, kb)
|
require.NotNil(t, kb)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
kb.Delete("keyname1", "", false)
|
kb.Delete("keyname1")
|
||||||
})
|
})
|
||||||
mockIn.Reset("test1234\n")
|
mockIn.Reset("test1234\n")
|
||||||
key1, err := kb.Get("keyname1")
|
key1, err := kb.Key("keyname1")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, key1)
|
require.NotNil(t, key1)
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/libs/cli"
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
|
@ -17,36 +16,37 @@ import (
|
||||||
|
|
||||||
func Test_runAddCmdBasic(t *testing.T) {
|
func Test_runAddCmdBasic(t *testing.T) {
|
||||||
cmd := AddKeyCommand()
|
cmd := AddKeyCommand()
|
||||||
assert.NotNil(t, cmd)
|
require.NotNil(t, cmd)
|
||||||
mockIn, _, _ := tests.ApplyMockIO(cmd)
|
mockIn, _, _ := tests.ApplyMockIO(cmd)
|
||||||
|
|
||||||
kbHome, kbCleanUp := tests.NewTestCaseDir(t)
|
kbHome, kbCleanUp := tests.NewTestCaseDir(t)
|
||||||
assert.NotNil(t, kbHome)
|
require.NotNil(t, kbHome)
|
||||||
t.Cleanup(kbCleanUp)
|
t.Cleanup(kbCleanUp)
|
||||||
viper.Set(flags.FlagHome, kbHome)
|
viper.Set(flags.FlagHome, kbHome)
|
||||||
viper.Set(cli.OutputFlag, OutputFormatText)
|
viper.Set(cli.OutputFlag, OutputFormatText)
|
||||||
|
viper.Set(flags.FlagUseLedger, false)
|
||||||
|
|
||||||
mockIn.Reset("y\n")
|
mockIn.Reset("y\n")
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
kb.Delete("keyname1", "", false) // nolint:errcheck
|
kb.Delete("keyname1") // nolint:errcheck
|
||||||
kb.Delete("keyname2", "", false) // nolint:errcheck
|
kb.Delete("keyname2") // nolint:errcheck
|
||||||
})
|
})
|
||||||
assert.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
require.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
||||||
|
|
||||||
mockIn.Reset("N\n")
|
mockIn.Reset("N\n")
|
||||||
assert.Error(t, runAddCmd(cmd, []string{"keyname1"}))
|
require.Error(t, runAddCmd(cmd, []string{"keyname1"}))
|
||||||
|
|
||||||
assert.NoError(t, runAddCmd(cmd, []string{"keyname2"}))
|
require.NoError(t, runAddCmd(cmd, []string{"keyname2"}))
|
||||||
assert.Error(t, runAddCmd(cmd, []string{"keyname2"}))
|
require.Error(t, runAddCmd(cmd, []string{"keyname2"}))
|
||||||
mockIn.Reset("y\n")
|
mockIn.Reset("y\n")
|
||||||
assert.NoError(t, runAddCmd(cmd, []string{"keyname2"}))
|
require.NoError(t, runAddCmd(cmd, []string{"keyname2"}))
|
||||||
|
|
||||||
// test --dry-run
|
// test --dry-run
|
||||||
assert.NoError(t, runAddCmd(cmd, []string{"keyname4"}))
|
require.NoError(t, runAddCmd(cmd, []string{"keyname4"}))
|
||||||
assert.Error(t, runAddCmd(cmd, []string{"keyname4"}))
|
require.Error(t, runAddCmd(cmd, []string{"keyname4"}))
|
||||||
|
|
||||||
viper.Set(flags.FlagDryRun, true)
|
viper.Set(flags.FlagDryRun, true)
|
||||||
assert.NoError(t, runAddCmd(cmd, []string{"keyname4"}))
|
require.NoError(t, runAddCmd(cmd, []string{"keyname4"}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package keys
|
package keys_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"bytes"
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,15 +58,9 @@ func TestMarshalJSON(t *testing.T) {
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tt := tt
|
tt := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := MarshalJSON(tt.args.o)
|
got, err := keys.MarshalJSON(tt.args.o)
|
||||||
if (err != nil) != tt.wantErr {
|
require.Equal(t, tt.wantErr, err != nil)
|
||||||
t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
require.True(t, bytes.Equal(got, tt.want))
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Printf("%s\n", got)
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("MarshalJSON() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,10 +88,8 @@ func TestUnmarshalJSON(t *testing.T) {
|
||||||
for idx, tt := range tests {
|
for idx, tt := range tests {
|
||||||
idx, tt := idx, tt
|
idx, tt := idx, tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if err := UnmarshalJSON(tt.args.bz, tt.args.ptr); (err != nil) != tt.wantErr {
|
err := keys.UnmarshalJSON(tt.args.bz, tt.args.ptr)
|
||||||
t.Errorf("unmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
require.Equal(t, tt.wantErr, err != nil)
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm deserialized objects are the same
|
// Confirm deserialized objects are the same
|
||||||
require.Equal(t, data.Keys[idx], data.Answers[idx])
|
require.Equal(t, data.Keys[idx], data.Answers[idx])
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,6 @@ package keys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
"github.com/cosmos/cosmos-sdk/client/input"
|
"github.com/cosmos/cosmos-sdk/client/input"
|
||||||
|
@ -43,49 +42,36 @@ private keys stored in a ledger device cannot be deleted with the CLI.
|
||||||
func runDeleteCmd(cmd *cobra.Command, args []string) error {
|
func runDeleteCmd(cmd *cobra.Command, args []string) error {
|
||||||
buf := bufio.NewReader(cmd.InOrStdin())
|
buf := bufio.NewReader(cmd.InOrStdin())
|
||||||
|
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range args {
|
for _, name := range args {
|
||||||
info, err := kb.Get(name)
|
info, err := kb.Key(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.GetType() == keyring.TypeLedger || info.GetType() == keyring.TypeOffline {
|
// confirm deletion, unless -y is passed
|
||||||
// confirm deletion, unless -y is passed
|
if !viper.GetBool(flagYes) {
|
||||||
if !viper.GetBool(flagYes) {
|
if yes, err := input.GetConfirmation("Key reference will be deleted. Continue?", buf, cmd.ErrOrStderr()); err != nil {
|
||||||
if err := confirmDeletion(buf); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := kb.Delete(name, "", true); err != nil {
|
|
||||||
return err
|
return err
|
||||||
|
} else if !yes {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
cmd.PrintErrln("Public key reference deleted")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// old password and skip flag arguments are ignored
|
if err := kb.Delete(name); err != nil {
|
||||||
if err := kb.Delete(name, "", true); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if info.GetType() == keyring.TypeLedger || info.GetType() == keyring.TypeOffline {
|
||||||
|
cmd.PrintErrln("Public key reference deleted")
|
||||||
|
continue
|
||||||
|
}
|
||||||
cmd.PrintErrln("Key deleted forever (uh oh!)")
|
cmd.PrintErrln("Key deleted forever (uh oh!)")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func confirmDeletion(buf *bufio.Reader) error {
|
|
||||||
answer, err := input.GetConfirmation("Key reference will be deleted. Continue?", buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !answer {
|
|
||||||
return errors.New("aborted")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package keys
|
package keys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
"github.com/cosmos/cosmos-sdk/tests"
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
|
@ -20,30 +20,25 @@ func Test_runDeleteCmd(t *testing.T) {
|
||||||
|
|
||||||
yesF, _ := deleteKeyCommand.Flags().GetBool(flagYes)
|
yesF, _ := deleteKeyCommand.Flags().GetBool(flagYes)
|
||||||
forceF, _ := deleteKeyCommand.Flags().GetBool(flagForce)
|
forceF, _ := deleteKeyCommand.Flags().GetBool(flagForce)
|
||||||
|
|
||||||
require.False(t, yesF)
|
require.False(t, yesF)
|
||||||
require.False(t, forceF)
|
require.False(t, forceF)
|
||||||
|
|
||||||
fakeKeyName1 := "runDeleteCmd_Key1"
|
fakeKeyName1 := "runDeleteCmd_Key1"
|
||||||
fakeKeyName2 := "runDeleteCmd_Key2"
|
fakeKeyName2 := "runDeleteCmd_Key2"
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
|
|
||||||
require.NoError(t, err)
|
|
||||||
t.Cleanup(func() {
|
|
||||||
kb.Delete("runDeleteCmd_Key1", "", false) // nolint:errcheck
|
|
||||||
kb.Delete("runDeleteCmd_Key2", "", false) // nolint:errcheck
|
|
||||||
|
|
||||||
})
|
|
||||||
// Now add a temporary keybase
|
// Now add a temporary keybase
|
||||||
kbHome, cleanUp := tests.NewTestCaseDir(t)
|
kbHome, cleanUp := tests.NewTestCaseDir(t)
|
||||||
t.Cleanup(cleanUp)
|
t.Cleanup(cleanUp)
|
||||||
viper.Set(flags.FlagHome, kbHome)
|
viper.Set(flags.FlagHome, kbHome)
|
||||||
|
|
||||||
// Now
|
// Now
|
||||||
kb, err = keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
|
path := sdk.GetConfig().GetFullFundraiserPath()
|
||||||
|
backend := viper.GetString(flags.FlagKeyringBackend)
|
||||||
|
kb, err := keyring.New(sdk.KeyringServiceName(), backend, kbHome, mockIn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keyring.Secp256k1)
|
_, err = kb.NewAccount(fakeKeyName1, tests.TestMnemonic, "", path, hd.Secp256k1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
_, _, err = kb.NewMnemonic(fakeKeyName2, keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
|
||||||
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keyring.Secp256k1)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = runDeleteCmd(deleteKeyCommand, []string{"blah"})
|
err = runDeleteCmd(deleteKeyCommand, []string{"blah"})
|
||||||
|
@ -55,53 +50,21 @@ func Test_runDeleteCmd(t *testing.T) {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, "EOF", err.Error())
|
require.Equal(t, "EOF", err.Error())
|
||||||
|
|
||||||
{
|
_, err = kb.Key(fakeKeyName1)
|
||||||
_, err = kb.Get(fakeKeyName1)
|
require.NoError(t, err)
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Now there is a confirmation
|
// Now there is a confirmation
|
||||||
viper.Set(flagYes, true)
|
viper.Set(flagYes, true)
|
||||||
require.NoError(t, runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1}))
|
require.NoError(t, runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1}))
|
||||||
|
|
||||||
_, err = kb.Get(fakeKeyName1)
|
_, err = kb.Key(fakeKeyName1)
|
||||||
require.Error(t, err) // Key1 is gone
|
require.Error(t, err) // Key1 is gone
|
||||||
}
|
|
||||||
|
|
||||||
viper.Set(flagYes, true)
|
viper.Set(flagYes, true)
|
||||||
_, err = kb.Get(fakeKeyName2)
|
_, err = kb.Key(fakeKeyName2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName2})
|
err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName2})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, err = kb.Get(fakeKeyName2)
|
_, err = kb.Key(fakeKeyName2)
|
||||||
require.Error(t, err) // Key2 is gone
|
require.Error(t, err) // Key2 is gone
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_confirmDeletion(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
buf *bufio.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
answerYes := bufio.NewReader(strings.NewReader("y\n"))
|
|
||||||
answerYes2 := bufio.NewReader(strings.NewReader("Y\n"))
|
|
||||||
answerNo := bufio.NewReader(strings.NewReader("n\n"))
|
|
||||||
answerInvalid := bufio.NewReader(strings.NewReader("245\n"))
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"Y", args{answerYes}, false},
|
|
||||||
{"y", args{answerYes2}, false},
|
|
||||||
{"N", args{answerNo}, true},
|
|
||||||
{"BAD", args{answerInvalid}, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
tt := tt
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if err := confirmDeletion(tt.args.buf); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("confirmDeletion() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -25,21 +25,17 @@ func ExportKeyCommand() *cobra.Command {
|
||||||
|
|
||||||
func runExportCmd(cmd *cobra.Command, args []string) error {
|
func runExportCmd(cmd *cobra.Command, args []string) error {
|
||||||
buf := bufio.NewReader(cmd.InOrStdin())
|
buf := bufio.NewReader(cmd.InOrStdin())
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptPassword, err := input.GetPassword("Enter passphrase to decrypt your key:", buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
encryptPassword, err := input.GetPassword("Enter passphrase to encrypt the exported key:", buf)
|
encryptPassword, err := input.GetPassword("Enter passphrase to encrypt the exported key:", buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
armored, err := kb.ExportPrivKey(args[0], decryptPassword, encryptPassword)
|
armored, err := kb.ExportPrivKeyArmor(args[0], encryptPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
"github.com/cosmos/cosmos-sdk/tests"
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
|
@ -22,13 +24,14 @@ func Test_runExportCmd(t *testing.T) {
|
||||||
viper.Set(flags.FlagHome, kbHome)
|
viper.Set(flags.FlagHome, kbHome)
|
||||||
|
|
||||||
// create a key
|
// create a key
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
kb.Delete("keyname1", "", false) // nolint:errcheck
|
kb.Delete("keyname1") // nolint:errcheck
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err = kb.CreateAccount("keyname1", tests.TestMnemonic, "", "123456789", "", keyring.Secp256k1)
|
path := sdk.GetConfig().GetFullFundraiserPath()
|
||||||
|
_, err = kb.NewAccount("keyname1", tests.TestMnemonic, "", path, hd.Secp256k1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Now enter password
|
// Now enter password
|
||||||
|
|
|
@ -26,7 +26,7 @@ func ImportKeyCommand() *cobra.Command {
|
||||||
|
|
||||||
func runImportCmd(cmd *cobra.Command, args []string) error {
|
func runImportCmd(cmd *cobra.Command, args []string) error {
|
||||||
buf := bufio.NewReader(cmd.InOrStdin())
|
buf := bufio.NewReader(cmd.InOrStdin())
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,10 @@ func Test_runImportCmd(t *testing.T) {
|
||||||
t.Cleanup(cleanUp)
|
t.Cleanup(cleanUp)
|
||||||
viper.Set(flags.FlagHome, kbHome)
|
viper.Set(flags.FlagHome, kbHome)
|
||||||
|
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
kb.Delete("keyname1", "", false) // nolint:errcheck
|
kb.Delete("keyname1") // nolint:errcheck
|
||||||
})
|
})
|
||||||
|
|
||||||
keyfile := filepath.Join(kbHome, "key.asc")
|
keyfile := filepath.Join(kbHome, "key.asc")
|
||||||
|
|
|
@ -26,7 +26,7 @@ along with their associated name and address.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func runListCmd(cmd *cobra.Command, _ []string) error {
|
func runListCmd(cmd *cobra.Command, _ []string) error {
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), cmd.InOrStdin())
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), cmd.InOrStdin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
"github.com/cosmos/cosmos-sdk/tests"
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
|
@ -31,14 +33,15 @@ func Test_runListCmd(t *testing.T) {
|
||||||
viper.Set(flags.FlagHome, kbHome2)
|
viper.Set(flags.FlagHome, kbHome2)
|
||||||
|
|
||||||
mockIn, _, _ := tests.ApplyMockIO(cmdBasic)
|
mockIn, _, _ := tests.ApplyMockIO(cmdBasic)
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = kb.CreateAccount("something", tests.TestMnemonic, "", "", "", keyring.Secp256k1)
|
path := "" //sdk.GetConfig().GetFullFundraiserPath()
|
||||||
|
_, err = kb.NewAccount("something", tests.TestMnemonic, "", path, hd.Secp256k1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
kb.Delete("something", "", false) // nolint:errcheck
|
kb.Delete("something") // nolint:errcheck
|
||||||
|
|
||||||
})
|
})
|
||||||
testData := []struct {
|
testData := []struct {
|
||||||
|
|
|
@ -60,21 +60,21 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
||||||
keyringServiceName := sdk.KeyringServiceName()
|
keyringServiceName := sdk.KeyringServiceName()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tmpDir string
|
tmpDir string
|
||||||
keybase keyring.Keybase
|
migrator keyring.InfoImporter
|
||||||
)
|
)
|
||||||
|
|
||||||
if viper.GetBool(flags.FlagDryRun) {
|
if viper.GetBool(flags.FlagDryRun) {
|
||||||
tmpDir, err = ioutil.TempDir("", "keybase-migrate-dryrun")
|
tmpDir, err = ioutil.TempDir("", "migrator-migrate-dryrun")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to create temporary directory for dryrun migration")
|
return errors.Wrap(err, "failed to create temporary directory for dryrun migration")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
keybase, err = keyring.NewKeyring(keyringServiceName, "test", tmpDir, buf)
|
migrator, err = keyring.NewInfoImporter(keyringServiceName, "test", tmpDir, buf)
|
||||||
} else {
|
} else {
|
||||||
keybase, err = keyring.NewKeyring(keyringServiceName, viper.GetString(flags.FlagKeyringBackend), rootDir, buf)
|
migrator, err = keyring.NewInfoImporter(keyringServiceName, viper.GetString(flags.FlagKeyringBackend), rootDir, buf)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, fmt.Sprintf(
|
return errors.Wrap(err, fmt.Sprintf(
|
||||||
|
@ -92,16 +92,10 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
||||||
keyName := key.GetName()
|
keyName := key.GetName()
|
||||||
keyType := key.GetType()
|
keyType := key.GetType()
|
||||||
|
|
||||||
// skip key if already migrated
|
|
||||||
if _, err := keybase.Get(keyName); err == nil {
|
|
||||||
cmd.PrintErrf("Key '%s (%s)' already exists; skipping ...\n", key.GetName(), keyType)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.PrintErrf("Migrating key: '%s (%s)' ...\n", key.GetName(), keyType)
|
cmd.PrintErrf("Migrating key: '%s (%s)' ...\n", key.GetName(), keyType)
|
||||||
|
|
||||||
// allow user to skip migrating specific keys
|
// allow user to skip migrating specific keys
|
||||||
ok, err := input.GetConfirmation("Skip key migration?", buf)
|
ok, err := input.GetConfirmation("Skip key migration?", buf, cmd.ErrOrStderr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -110,7 +104,7 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if keyType != keyring.TypeLocal {
|
if keyType != keyring.TypeLocal {
|
||||||
if err := keybase.Import(keyName, legKeyInfo); err != nil {
|
if err := migrator.Import(keyName, legKeyInfo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +124,7 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := keybase.ImportPrivKey(keyName, armoredPriv, migratePassphrase); err != nil {
|
if err := migrator.Import(keyName, armoredPriv); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package keys
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/otiai10/copy"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
"github.com/cosmos/cosmos-sdk/tests"
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
|
|
||||||
|
@ -18,18 +20,18 @@ func Test_runMigrateCmd(t *testing.T) {
|
||||||
mockIn, _, _ := tests.ApplyMockIO(cmd)
|
mockIn, _, _ := tests.ApplyMockIO(cmd)
|
||||||
|
|
||||||
kbHome, kbCleanUp := tests.NewTestCaseDir(t)
|
kbHome, kbCleanUp := tests.NewTestCaseDir(t)
|
||||||
|
copy.Copy("testdata", kbHome)
|
||||||
assert.NotNil(t, kbHome)
|
assert.NotNil(t, kbHome)
|
||||||
t.Cleanup(kbCleanUp)
|
t.Cleanup(kbCleanUp)
|
||||||
viper.Set(flags.FlagHome, kbHome)
|
viper.Set(flags.FlagHome, kbHome)
|
||||||
|
|
||||||
viper.Set(cli.OutputFlag, OutputFormatText)
|
viper.Set(cli.OutputFlag, OutputFormatText)
|
||||||
|
|
||||||
mockIn.Reset("test1234\ntest1234\n")
|
|
||||||
assert.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
assert.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
||||||
|
|
||||||
viper.Set(flags.FlagDryRun, true)
|
viper.Set(flags.FlagDryRun, true)
|
||||||
cmd = MigrateCommand()
|
cmd = MigrateCommand()
|
||||||
mockIn, _, _ = tests.ApplyMockIO(cmd)
|
mockIn, _, _ = tests.ApplyMockIO(cmd)
|
||||||
mockIn.Reset("test1234\n")
|
mockIn.Reset("test1234\ntest1234\n")
|
||||||
assert.NoError(t, runMigrateCmd(cmd, []string{}))
|
assert.NoError(t, runMigrateCmd(cmd, []string{}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ func runMnemonicCmd(cmd *cobra.Command, args []string) error {
|
||||||
if len(inputEntropy) < 43 {
|
if len(inputEntropy) < 43 {
|
||||||
return fmt.Errorf("256-bits is 43 characters in Base-64, and 100 in Base-6. You entered %v, and probably want more", len(inputEntropy))
|
return fmt.Errorf("256-bits is 43 characters in Base-64, and 100 in Base-6. You entered %v, and probably want more", len(inputEntropy))
|
||||||
}
|
}
|
||||||
conf, err := input.GetConfirmation(fmt.Sprintf("> Input length: %d", len(inputEntropy)), buf)
|
conf, err := input.GetConfirmation(fmt.Sprintf("> Input length: %d", len(inputEntropy)), buf, cmd.ErrOrStderr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCommands(t *testing.T) {
|
func TestCommands(t *testing.T) {
|
||||||
|
@ -21,5 +22,6 @@ func TestCommands(t *testing.T) {
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
viper.Set(flags.FlagKeyringBackend, keyring.BackendTest)
|
viper.Set(flags.FlagKeyringBackend, keyring.BackendTest)
|
||||||
|
viper.Set(flagCoinType, sdk.CoinType)
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ consisting of all the keys provided by name and multisig threshold.`,
|
||||||
func runShowCmd(cmd *cobra.Command, args []string) (err error) {
|
func runShowCmd(cmd *cobra.Command, args []string) (err error) {
|
||||||
var info keyring.Info
|
var info keyring.Info
|
||||||
|
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), cmd.InOrStdin())
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), cmd.InOrStdin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -142,15 +142,15 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchKey(kb keyring.Keybase, keyref string) (keyring.Info, error) {
|
func fetchKey(kb keyring.Keyring, keyref string) (keyring.Info, error) {
|
||||||
info, err := kb.Get(keyref)
|
info, err := kb.Key(keyref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
accAddr, err := sdk.AccAddressFromBech32(keyref)
|
accAddr, err := sdk.AccAddressFromBech32(keyref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return info, err
|
return info, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err = kb.GetByAddress(accAddr)
|
info, err = kb.KeyByAddress(accAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return info, errors.New("key not found")
|
return info, errors.New("key not found")
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
"github.com/cosmos/cosmos-sdk/tests"
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
@ -24,7 +25,7 @@ func Test_multiSigKey_Properties(t *testing.T) {
|
||||||
require.Equal(t, "myMultisig", tmp.GetName())
|
require.Equal(t, "myMultisig", tmp.GetName())
|
||||||
require.Equal(t, keyring.TypeMulti, tmp.GetType())
|
require.Equal(t, keyring.TypeMulti, tmp.GetType())
|
||||||
require.Equal(t, "D3923267FA8A3DD367BB768FA8BDC8FF7F89DA3F", tmp.GetPubKey().Address().String())
|
require.Equal(t, "D3923267FA8A3DD367BB768FA8BDC8FF7F89DA3F", tmp.GetPubKey().Address().String())
|
||||||
require.Equal(t, "cosmos16wfryel63g7axeamw68630wglalcnk3l0zuadc", tmp.GetAddress().String())
|
require.Equal(t, "cosmos16wfryel63g7axeamw68630wglalcnk3l0zuadc", sdk.MustBech32ifyAddressBytes("cosmos", tmp.GetAddress()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_showKeysCmd(t *testing.T) {
|
func Test_showKeysCmd(t *testing.T) {
|
||||||
|
@ -48,16 +49,19 @@ func Test_runShowCmd(t *testing.T) {
|
||||||
|
|
||||||
fakeKeyName1 := "runShowCmd_Key1"
|
fakeKeyName1 := "runShowCmd_Key1"
|
||||||
fakeKeyName2 := "runShowCmd_Key2"
|
fakeKeyName2 := "runShowCmd_Key2"
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
kb.Delete("runShowCmd_Key1", "", false)
|
kb.Delete("runShowCmd_Key1")
|
||||||
kb.Delete("runShowCmd_Key2", "", false)
|
kb.Delete("runShowCmd_Key2")
|
||||||
})
|
})
|
||||||
_, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keyring.Secp256k1)
|
|
||||||
|
path := hd.NewFundraiserParams(1, sdk.CoinType, 0).String()
|
||||||
|
_, err = kb.NewAccount(fakeKeyName1, tests.TestMnemonic, "", path, hd.Secp256k1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keyring.Secp256k1)
|
path2 := hd.NewFundraiserParams(1, sdk.CoinType, 1).String()
|
||||||
|
_, err = kb.NewAccount(fakeKeyName2, tests.TestMnemonic, "", path2, hd.Secp256k1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Now try single key
|
// Now try single key
|
||||||
|
@ -69,7 +73,7 @@ func Test_runShowCmd(t *testing.T) {
|
||||||
// try fetch by name
|
// try fetch by name
|
||||||
require.NoError(t, runShowCmd(cmd, []string{fakeKeyName1}))
|
require.NoError(t, runShowCmd(cmd, []string{fakeKeyName1}))
|
||||||
// try fetch by addr
|
// try fetch by addr
|
||||||
info, err := kb.Get(fakeKeyName1)
|
info, err := kb.Key(fakeKeyName1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, runShowCmd(cmd, []string{info.GetAddress().String()}))
|
require.NoError(t, runShowCmd(cmd, []string{info.GetAddress().String()}))
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ type AccountRetriever interface {
|
||||||
// Factory defines a client transaction factory that facilitates generating and
|
// Factory defines a client transaction factory that facilitates generating and
|
||||||
// signing an application-specific transaction.
|
// signing an application-specific transaction.
|
||||||
type Factory struct {
|
type Factory struct {
|
||||||
keybase keyring.Keybase
|
keybase keyring.Keyring
|
||||||
txGenerator Generator
|
txGenerator Generator
|
||||||
accountRetriever AccountRetriever
|
accountRetriever AccountRetriever
|
||||||
accountNumber uint64
|
accountNumber uint64
|
||||||
|
@ -36,7 +36,7 @@ type Factory struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromCLI(input io.Reader) Factory {
|
func NewFactoryFromCLI(input io.Reader) Factory {
|
||||||
kb, err := keyring.NewKeyring(
|
kb, err := keyring.New(
|
||||||
sdk.KeyringServiceName(),
|
sdk.KeyringServiceName(),
|
||||||
viper.GetString(flags.FlagKeyringBackend),
|
viper.GetString(flags.FlagKeyringBackend),
|
||||||
viper.GetString(flags.FlagHome),
|
viper.GetString(flags.FlagHome),
|
||||||
|
@ -68,7 +68,7 @@ func (f Factory) AccountNumber() uint64 { return f.accountNumber }
|
||||||
func (f Factory) Sequence() uint64 { return f.sequence }
|
func (f Factory) Sequence() uint64 { return f.sequence }
|
||||||
func (f Factory) Gas() uint64 { return f.gas }
|
func (f Factory) Gas() uint64 { return f.gas }
|
||||||
func (f Factory) GasAdjustment() float64 { return f.gasAdjustment }
|
func (f Factory) GasAdjustment() float64 { return f.gasAdjustment }
|
||||||
func (f Factory) Keybase() keyring.Keybase { return f.keybase }
|
func (f Factory) Keybase() keyring.Keyring { return f.keybase }
|
||||||
func (f Factory) ChainID() string { return f.chainID }
|
func (f Factory) ChainID() string { return f.chainID }
|
||||||
func (f Factory) Memo() string { return f.memo }
|
func (f Factory) Memo() string { return f.memo }
|
||||||
func (f Factory) Fees() sdk.Coins { return f.fees }
|
func (f Factory) Fees() sdk.Coins { return f.fees }
|
||||||
|
@ -126,7 +126,7 @@ func (f Factory) WithGasPrices(gasPrices string) Factory {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithKeybase returns a copy of the Factory with updated Keybase.
|
// WithKeybase returns a copy of the Factory with updated Keybase.
|
||||||
func (f Factory) WithKeybase(keybase keyring.Keybase) Factory {
|
func (f Factory) WithKeybase(keybase keyring.Keyring) Factory {
|
||||||
f.keybase = keybase
|
f.keybase = keybase
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ func BroadcastTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "%s\n\n", out)
|
_, _ = fmt.Fprintf(os.Stderr, "%s\n\n", out)
|
||||||
|
|
||||||
buf := bufio.NewReader(os.Stdin)
|
buf := bufio.NewReader(os.Stdin)
|
||||||
ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf)
|
ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf, os.Stderr)
|
||||||
if err != nil || !ok {
|
if err != nil || !ok {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction")
|
_, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction")
|
||||||
return err
|
return err
|
||||||
|
@ -332,7 +332,7 @@ func Sign(txf Factory, name, passphrase string, tx ClientTx) ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sigBytes, pubkey, err := txf.keybase.Sign(name, passphrase, signBytes)
|
sigBytes, pubkey, err := txf.keybase.Sign(name, signBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,11 @@ import (
|
||||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
"github.com/tendermint/tendermint/crypto/xsalsa20symmetric"
|
"github.com/tendermint/tendermint/crypto/xsalsa20symmetric"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/crypto"
|
"github.com/cosmos/cosmos-sdk/crypto"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestArmorUnarmorPrivKey(t *testing.T) {
|
func TestArmorUnarmorPrivKey(t *testing.T) {
|
||||||
|
@ -26,7 +29,7 @@ func TestArmorUnarmorPrivKey(t *testing.T) {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
decrypted, algo, err := crypto.UnarmorDecryptPrivKey(armored, "passphrase")
|
decrypted, algo, err := crypto.UnarmorDecryptPrivKey(armored, "passphrase")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, string(keyring.Secp256k1), algo)
|
require.Equal(t, string(hd.Secp256k1Type), algo)
|
||||||
require.True(t, priv.Equals(decrypted))
|
require.True(t, priv.Equals(decrypted))
|
||||||
|
|
||||||
// empty string
|
// empty string
|
||||||
|
@ -70,14 +73,14 @@ func TestArmorUnarmorPubKey(t *testing.T) {
|
||||||
cstore := keyring.NewInMemory()
|
cstore := keyring.NewInMemory()
|
||||||
|
|
||||||
// Add keys and see they return in alphabetical order
|
// Add keys and see they return in alphabetical order
|
||||||
info, _, err := cstore.CreateMnemonic("Bob", keyring.English, "passphrase", keyring.Secp256k1)
|
info, _, err := cstore.NewMnemonic("Bob", keyring.English, types.FullFundraiserPath, hd.Secp256k1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
armored := crypto.ArmorPubKeyBytes(info.GetPubKey().Bytes(), "")
|
armored := crypto.ArmorPubKeyBytes(info.GetPubKey().Bytes(), "")
|
||||||
pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armored)
|
pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armored)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
pub, err := cryptoAmino.PubKeyFromBytes(pubBytes)
|
pub, err := cryptoAmino.PubKeyFromBytes(pubBytes)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, string(keyring.Secp256k1), algo)
|
require.Equal(t, string(hd.Secp256k1Type), algo)
|
||||||
require.True(t, pub.Equals(info.GetPubKey()))
|
require.True(t, pub.Equals(info.GetPubKey()))
|
||||||
|
|
||||||
armored = crypto.ArmorPubKeyBytes(info.GetPubKey().Bytes(), "unknown")
|
armored = crypto.ArmorPubKeyBytes(info.GetPubKey().Bytes(), "unknown")
|
||||||
|
@ -88,7 +91,7 @@ func TestArmorUnarmorPubKey(t *testing.T) {
|
||||||
require.Equal(t, "unknown", algo)
|
require.Equal(t, "unknown", algo)
|
||||||
require.True(t, pub.Equals(info.GetPubKey()))
|
require.True(t, pub.Equals(info.GetPubKey()))
|
||||||
|
|
||||||
armored, err = cstore.ExportPrivKey("Bob", "passphrase", "alessio")
|
armored, err = cstore.ExportPrivKeyArmor("Bob", "passphrase")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, _, err = crypto.UnarmorPubKeyBytes(armored)
|
_, _, err = crypto.UnarmorPubKeyBytes(armored)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package hd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/go-bip39"
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PubKeyType defines an algorithm to derive key-pairs which can be used for cryptographic signing.
|
||||||
|
type PubKeyType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MultiType implies that a pubkey is a multisignature
|
||||||
|
MultiType = PubKeyType("multi")
|
||||||
|
// Secp256k1Type uses the Bitcoin secp256k1 ECDSA parameters.
|
||||||
|
Secp256k1Type = PubKeyType("secp256k1")
|
||||||
|
// Ed25519Type represents the Ed25519Type signature system.
|
||||||
|
// It is currently not supported for end-user keys (wallets/ledgers).
|
||||||
|
Ed25519Type = PubKeyType("ed25519")
|
||||||
|
// Sr25519Type represents the Sr25519Type signature system.
|
||||||
|
Sr25519Type = PubKeyType("sr25519")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters.
|
||||||
|
Secp256k1 = secp256k1Algo{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeriveFn func(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error)
|
||||||
|
type GenerateFn func(bz []byte) crypto.PrivKey
|
||||||
|
|
||||||
|
type WalletGenerator interface {
|
||||||
|
Derive(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error)
|
||||||
|
Generate(bz []byte) crypto.PrivKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type secp256k1Algo struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s secp256k1Algo) Name() PubKeyType {
|
||||||
|
return Secp256k1Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive derives and returns the secp256k1 private key for the given seed and HD path.
|
||||||
|
func (s secp256k1Algo) Derive() DeriveFn {
|
||||||
|
return func(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) {
|
||||||
|
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
masterPriv, ch := ComputeMastersFromSeed(seed)
|
||||||
|
if len(hdPath) == 0 {
|
||||||
|
return masterPriv[:], nil
|
||||||
|
}
|
||||||
|
derivedKey, err := DerivePrivateKeyForPath(masterPriv, ch, hdPath)
|
||||||
|
return derivedKey[:], err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate generates a secp256k1 private key from the given bytes.
|
||||||
|
func (s secp256k1Algo) Generate() GenerateFn {
|
||||||
|
return func(bz []byte) crypto.PrivKey {
|
||||||
|
var bzArr [32]byte
|
||||||
|
copy(bzArr[:], bz)
|
||||||
|
return secp256k1.PrivKeySecp256k1(bzArr)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package hd_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDefaults(t *testing.T) {
|
||||||
|
require.Equal(t, hd.PubKeyType("multi"), hd.MultiType)
|
||||||
|
require.Equal(t, hd.PubKeyType("secp256k1"), hd.Secp256k1Type)
|
||||||
|
require.Equal(t, hd.PubKeyType("ed25519"), hd.Ed25519Type)
|
||||||
|
require.Equal(t, hd.PubKeyType("sr25519"), hd.Sr25519Type)
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Package hd provides support for hierarchical deterministic wallets generation and derivation.
|
||||||
|
//
|
||||||
|
// The user must understand the overall concept of the BIP 32 and the BIP 44 specs:
|
||||||
|
// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
||||||
|
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||||
|
//
|
||||||
|
// In combination with the bip39 package in go-crypto this package provides the functionality for
|
||||||
|
// deriving keys using a BIP 44 HD path, or, more general, by passing a BIP 32 path.
|
||||||
|
//
|
||||||
|
// In particular, this package (together with bip39) provides all necessary functionality to derive
|
||||||
|
// keys from mnemonics generated during the cosmos fundraiser.
|
||||||
|
package hd
|
|
@ -1,4 +1,4 @@
|
||||||
package hd
|
package hd_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
@ -13,6 +13,8 @@ import (
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
)
|
)
|
||||||
|
|
||||||
type addrData struct {
|
type addrData struct {
|
||||||
|
@ -24,19 +26,23 @@ type addrData struct {
|
||||||
Addr string
|
Addr string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFullFundraiserPath(t *testing.T) {
|
||||||
|
require.Equal(t, "44'/118'/0'/0/0", hd.NewFundraiserParams(0, 118, 0).String())
|
||||||
|
}
|
||||||
|
|
||||||
func initFundraiserTestVectors(t *testing.T) []addrData {
|
func initFundraiserTestVectors(t *testing.T) []addrData {
|
||||||
// NOTE: atom fundraiser address
|
// NOTE: atom fundraiser address
|
||||||
// var hdPath string = "m/44'/118'/0'/0/0"
|
// var hdPath string = "m/44'/118'/0'/0/0"
|
||||||
var hdToAddrTable []addrData
|
var hdToAddrTable []addrData
|
||||||
|
|
||||||
b, err := ioutil.ReadFile("test.json")
|
b, err := ioutil.ReadFile("testdata/test.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not read fundraiser test vector file (test.json): %s", err)
|
t.Fatalf("could not read fundraiser test vector file (testdata/test.json): %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(b, &hdToAddrTable)
|
err = json.Unmarshal(b, &hdToAddrTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not decode test vectors (test.json): %s", err)
|
t.Fatalf("could not decode test vectors (testdata/test.json): %s", err)
|
||||||
}
|
}
|
||||||
return hdToAddrTable
|
return hdToAddrTable
|
||||||
}
|
}
|
||||||
|
@ -56,8 +62,8 @@ func TestFundraiserCompatibility(t *testing.T) {
|
||||||
t.Log("================================")
|
t.Log("================================")
|
||||||
t.Logf("ROUND: %d MNEMONIC: %s", i, d.Mnemonic)
|
t.Logf("ROUND: %d MNEMONIC: %s", i, d.Mnemonic)
|
||||||
|
|
||||||
master, ch := ComputeMastersFromSeed(seed)
|
master, ch := hd.ComputeMastersFromSeed(seed)
|
||||||
priv, err := DerivePrivateKeyForPath(master, ch, "44'/118'/0'/0/0")
|
priv, err := hd.DerivePrivateKeyForPath(master, ch, "44'/118'/0'/0/0")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
pub := secp256k1.PrivKeySecp256k1(priv).PubKey()
|
pub := secp256k1.PrivKeySecp256k1(priv).PubKey()
|
||||||
|
|
|
@ -1,20 +1,8 @@
|
||||||
// Package hd provides basic functionality Hierarchical Deterministic Wallets.
|
|
||||||
//
|
|
||||||
// The user must understand the overall concept of the BIP 32 and the BIP 44 specs:
|
|
||||||
// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
|
||||||
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
|
||||||
//
|
|
||||||
// In combination with the bip39 package in go-crypto this package provides the functionality for deriving keys using a
|
|
||||||
// BIP 44 HD path, or, more general, by passing a BIP 32 path.
|
|
||||||
//
|
|
||||||
// In particular, this package (together with bip39) provides all necessary functionality to derive keys from
|
|
||||||
// mnemonics generated during the cosmos fundraiser.
|
|
||||||
package hd
|
package hd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -255,3 +243,8 @@ func i64(key []byte, data []byte) (il [32]byte, ir [32]byte) {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateHDPath returns BIP 44 object from account and index parameters.
|
||||||
|
func CreateHDPath(coinType, account, index uint32) *BIP44Params {
|
||||||
|
return NewFundraiserParams(account, coinType, index)
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
package hd
|
package hd_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
"github.com/cosmos/cosmos-sdk/types"
|
"github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
bip39 "github.com/cosmos/go-bip39"
|
bip39 "github.com/cosmos/go-bip39"
|
||||||
|
@ -21,9 +22,9 @@ func mnemonicToSeed(mnemonic string) []byte {
|
||||||
|
|
||||||
// nolint:govet
|
// nolint:govet
|
||||||
func ExampleStringifyPathParams() {
|
func ExampleStringifyPathParams() {
|
||||||
path := NewParams(44, 0, 0, false, 0)
|
path := hd.NewParams(44, 0, 0, false, 0)
|
||||||
fmt.Println(path.String())
|
fmt.Println(path.String())
|
||||||
path = NewParams(44, 33, 7, true, 9)
|
path = hd.NewParams(44, 33, 7, true, 9)
|
||||||
fmt.Println(path.String())
|
fmt.Println(path.String())
|
||||||
// Output:
|
// Output:
|
||||||
// 44'/0'/0'/0/0
|
// 44'/0'/0'/0/0
|
||||||
|
@ -31,40 +32,40 @@ func ExampleStringifyPathParams() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStringifyFundraiserPathParams(t *testing.T) {
|
func TestStringifyFundraiserPathParams(t *testing.T) {
|
||||||
path := NewFundraiserParams(4, types.CoinType, 22)
|
path := hd.NewFundraiserParams(4, types.CoinType, 22)
|
||||||
require.Equal(t, "44'/118'/4'/0/22", path.String())
|
require.Equal(t, "44'/118'/4'/0/22", path.String())
|
||||||
|
|
||||||
path = NewFundraiserParams(4, types.CoinType, 57)
|
path = hd.NewFundraiserParams(4, types.CoinType, 57)
|
||||||
require.Equal(t, "44'/118'/4'/0/57", path.String())
|
require.Equal(t, "44'/118'/4'/0/57", path.String())
|
||||||
|
|
||||||
path = NewFundraiserParams(4, 12345, 57)
|
path = hd.NewFundraiserParams(4, 12345, 57)
|
||||||
require.Equal(t, "44'/12345'/4'/0/57", path.String())
|
require.Equal(t, "44'/12345'/4'/0/57", path.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathToArray(t *testing.T) {
|
func TestPathToArray(t *testing.T) {
|
||||||
path := NewParams(44, 118, 1, false, 4)
|
path := hd.NewParams(44, 118, 1, false, 4)
|
||||||
require.Equal(t, "[44 118 1 0 4]", fmt.Sprintf("%v", path.DerivationPath()))
|
require.Equal(t, "[44 118 1 0 4]", fmt.Sprintf("%v", path.DerivationPath()))
|
||||||
|
|
||||||
path = NewParams(44, 118, 2, true, 15)
|
path = hd.NewParams(44, 118, 2, true, 15)
|
||||||
require.Equal(t, "[44 118 2 1 15]", fmt.Sprintf("%v", path.DerivationPath()))
|
require.Equal(t, "[44 118 2 1 15]", fmt.Sprintf("%v", path.DerivationPath()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParamsFromPath(t *testing.T) {
|
func TestParamsFromPath(t *testing.T) {
|
||||||
goodCases := []struct {
|
goodCases := []struct {
|
||||||
params *BIP44Params
|
params *hd.BIP44Params
|
||||||
path string
|
path string
|
||||||
}{
|
}{
|
||||||
{&BIP44Params{44, 0, 0, false, 0}, "44'/0'/0'/0/0"},
|
{&hd.BIP44Params{44, 0, 0, false, 0}, "44'/0'/0'/0/0"},
|
||||||
{&BIP44Params{44, 1, 0, false, 0}, "44'/1'/0'/0/0"},
|
{&hd.BIP44Params{44, 1, 0, false, 0}, "44'/1'/0'/0/0"},
|
||||||
{&BIP44Params{44, 0, 1, false, 0}, "44'/0'/1'/0/0"},
|
{&hd.BIP44Params{44, 0, 1, false, 0}, "44'/0'/1'/0/0"},
|
||||||
{&BIP44Params{44, 0, 0, true, 0}, "44'/0'/0'/1/0"},
|
{&hd.BIP44Params{44, 0, 0, true, 0}, "44'/0'/0'/1/0"},
|
||||||
{&BIP44Params{44, 0, 0, false, 1}, "44'/0'/0'/0/1"},
|
{&hd.BIP44Params{44, 0, 0, false, 1}, "44'/0'/0'/0/1"},
|
||||||
{&BIP44Params{44, 1, 1, true, 1}, "44'/1'/1'/1/1"},
|
{&hd.BIP44Params{44, 1, 1, true, 1}, "44'/1'/1'/1/1"},
|
||||||
{&BIP44Params{44, 118, 52, true, 41}, "44'/118'/52'/1/41"},
|
{&hd.BIP44Params{44, 118, 52, true, 41}, "44'/118'/52'/1/41"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, c := range goodCases {
|
for i, c := range goodCases {
|
||||||
params, err := NewParamsFromPath(c.path)
|
params, err := hd.NewParamsFromPath(c.path)
|
||||||
errStr := fmt.Sprintf("%d %v", i, c)
|
errStr := fmt.Sprintf("%d %v", i, c)
|
||||||
assert.NoError(t, err, errStr)
|
assert.NoError(t, err, errStr)
|
||||||
assert.EqualValues(t, c.params, params, errStr)
|
assert.EqualValues(t, c.params, params, errStr)
|
||||||
|
@ -93,7 +94,7 @@ func TestParamsFromPath(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, c := range badCases {
|
for i, c := range badCases {
|
||||||
params, err := NewParamsFromPath(c.path)
|
params, err := hd.NewParamsFromPath(c.path)
|
||||||
errStr := fmt.Sprintf("%d %v", i, c)
|
errStr := fmt.Sprintf("%d %v", i, c)
|
||||||
assert.Nil(t, params, errStr)
|
assert.Nil(t, params, errStr)
|
||||||
assert.Error(t, err, errStr)
|
assert.Error(t, err, errStr)
|
||||||
|
@ -106,38 +107,38 @@ func ExampleSomeBIP32TestVecs() {
|
||||||
|
|
||||||
seed := mnemonicToSeed("barrel original fuel morning among eternal " +
|
seed := mnemonicToSeed("barrel original fuel morning among eternal " +
|
||||||
"filter ball stove pluck matrix mechanic")
|
"filter ball stove pluck matrix mechanic")
|
||||||
master, ch := ComputeMastersFromSeed(seed)
|
master, ch := hd.ComputeMastersFromSeed(seed)
|
||||||
fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)")
|
fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)")
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
// cosmos
|
// cosmos
|
||||||
priv, err := DerivePrivateKeyForPath(master, ch, types.FullFundraiserPath)
|
priv, err := hd.DerivePrivateKeyForPath(master, ch, types.FullFundraiserPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("INVALID")
|
fmt.Println("INVALID")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(hex.EncodeToString(priv[:]))
|
fmt.Println(hex.EncodeToString(priv[:]))
|
||||||
}
|
}
|
||||||
// bitcoin
|
// bitcoin
|
||||||
priv, err = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/0")
|
priv, err = hd.DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("INVALID")
|
fmt.Println("INVALID")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(hex.EncodeToString(priv[:]))
|
fmt.Println(hex.EncodeToString(priv[:]))
|
||||||
}
|
}
|
||||||
// ether
|
// ether
|
||||||
priv, err = DerivePrivateKeyForPath(master, ch, "44'/60'/0'/0/0")
|
priv, err = hd.DerivePrivateKeyForPath(master, ch, "44'/60'/0'/0/0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("INVALID")
|
fmt.Println("INVALID")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(hex.EncodeToString(priv[:]))
|
fmt.Println(hex.EncodeToString(priv[:]))
|
||||||
}
|
}
|
||||||
// INVALID
|
// INVALID
|
||||||
priv, err = DerivePrivateKeyForPath(master, ch, "X/0'/0'/0/0")
|
priv, err = hd.DerivePrivateKeyForPath(master, ch, "X/0'/0'/0/0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("INVALID")
|
fmt.Println("INVALID")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(hex.EncodeToString(priv[:]))
|
fmt.Println(hex.EncodeToString(priv[:]))
|
||||||
}
|
}
|
||||||
priv, err = DerivePrivateKeyForPath(master, ch, "-44/0'/0'/0/0")
|
priv, err = hd.DerivePrivateKeyForPath(master, ch, "-44/0'/0'/0/0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("INVALID")
|
fmt.Println("INVALID")
|
||||||
} else {
|
} else {
|
||||||
|
@ -151,14 +152,14 @@ func ExampleSomeBIP32TestVecs() {
|
||||||
seed = mnemonicToSeed(
|
seed = mnemonicToSeed(
|
||||||
"advice process birth april short trust crater change bacon monkey medal garment " +
|
"advice process birth april short trust crater change bacon monkey medal garment " +
|
||||||
"gorilla ranch hour rival razor call lunar mention taste vacant woman sister")
|
"gorilla ranch hour rival razor call lunar mention taste vacant woman sister")
|
||||||
master, ch = ComputeMastersFromSeed(seed)
|
master, ch = hd.ComputeMastersFromSeed(seed)
|
||||||
priv, _ = DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4")
|
priv, _ = hd.DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4")
|
||||||
fmt.Println(hex.EncodeToString(priv[:]))
|
fmt.Println(hex.EncodeToString(priv[:]))
|
||||||
|
|
||||||
seed = mnemonicToSeed("idea naive region square margin day captain habit " +
|
seed = mnemonicToSeed("idea naive region square margin day captain habit " +
|
||||||
"gun second farm pact pulse someone armed")
|
"gun second farm pact pulse someone armed")
|
||||||
master, ch = ComputeMastersFromSeed(seed)
|
master, ch = hd.ComputeMastersFromSeed(seed)
|
||||||
priv, _ = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420")
|
priv, _ = hd.DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420")
|
||||||
fmt.Println(hex.EncodeToString(priv[:]))
|
fmt.Println(hex.EncodeToString(priv[:]))
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
@ -167,8 +168,8 @@ func ExampleSomeBIP32TestVecs() {
|
||||||
|
|
||||||
// bip32 path: m/0/7
|
// bip32 path: m/0/7
|
||||||
seed = mnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history")
|
seed = mnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history")
|
||||||
master, ch = ComputeMastersFromSeed(seed)
|
master, ch = hd.ComputeMastersFromSeed(seed)
|
||||||
priv, _ = DerivePrivateKeyForPath(master, ch, "0/7")
|
priv, _ = hd.DerivePrivateKeyForPath(master, ch, "0/7")
|
||||||
fmt.Println(hex.EncodeToString(priv[:]))
|
fmt.Println(hex.EncodeToString(priv[:]))
|
||||||
|
|
||||||
// Output: keys from fundraiser test-vector (cosmos, bitcoin, ether)
|
// Output: keys from fundraiser test-vector (cosmos, bitcoin, ether)
|
||||||
|
@ -188,3 +189,27 @@ func ExampleSomeBIP32TestVecs() {
|
||||||
//
|
//
|
||||||
// c4c11d8c03625515905d7e89d25dfc66126fbc629ecca6db489a1a72fc4bda78
|
// c4c11d8c03625515905d7e89d25dfc66126fbc629ecca6db489a1a72fc4bda78
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateHDPath(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
coinType uint32
|
||||||
|
account uint32
|
||||||
|
index uint32
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want hd.BIP44Params
|
||||||
|
}{
|
||||||
|
{"44'/0'/0'/0/0", args{0, 0, 0}, hd.BIP44Params{Purpose: 44}},
|
||||||
|
{"44'/114'/0'/0/0", args{114, 0, 0}, hd.BIP44Params{Purpose: 44, CoinType: 114, Account: 0, AddressIndex: 0}},
|
||||||
|
{"44'/114'/1'/1/0", args{114, 1, 1}, hd.BIP44Params{Purpose: 44, CoinType: 114, Account: 1, AddressIndex: 1}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tt := tt
|
||||||
|
require.Equal(t, tt.want, *hd.CreateHDPath(tt.args.coinType, tt.args.account, tt.args.index))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,234 +0,0 @@
|
||||||
package keyring
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
|
||||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/crypto"
|
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
|
||||||
"github.com/cosmos/cosmos-sdk/types"
|
|
||||||
bip39 "github.com/cosmos/go-bip39"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// baseKeybase is an auxiliary type that groups Keybase storage agnostic features
|
|
||||||
// together.
|
|
||||||
baseKeybase struct {
|
|
||||||
options kbOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
keyWriter interface {
|
|
||||||
writeLocalKeyer
|
|
||||||
infoWriter
|
|
||||||
}
|
|
||||||
|
|
||||||
writeLocalKeyer interface {
|
|
||||||
writeLocalKey(name string, priv tmcrypto.PrivKey, algo SigningAlgo) Info
|
|
||||||
}
|
|
||||||
|
|
||||||
infoWriter interface {
|
|
||||||
writeInfo(name string, info Info)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var fundraiserPath = types.GetConfig().GetFullFundraiserPath()
|
|
||||||
|
|
||||||
// newBaseKeybase generates the base keybase with defaulting to tendermint SECP256K1 key type
|
|
||||||
func newBaseKeybase(optionsFns ...KeybaseOption) baseKeybase {
|
|
||||||
// Default options for keybase
|
|
||||||
options := kbOptions{
|
|
||||||
keygenFunc: StdPrivKeyGen,
|
|
||||||
deriveFunc: StdDeriveKey,
|
|
||||||
supportedAlgos: []SigningAlgo{Secp256k1},
|
|
||||||
supportedAlgosLedger: []SigningAlgo{Secp256k1},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, optionFn := range optionsFns {
|
|
||||||
optionFn(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseKeybase{options: options}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StdPrivKeyGen is the default PrivKeyGen function in the keybase.
|
|
||||||
// For now, it only supports Secp256k1
|
|
||||||
func StdPrivKeyGen(bz []byte, algo SigningAlgo) (tmcrypto.PrivKey, error) {
|
|
||||||
if algo == Secp256k1 {
|
|
||||||
return SecpPrivKeyGen(bz), nil
|
|
||||||
}
|
|
||||||
return nil, ErrUnsupportedSigningAlgo
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecpPrivKeyGen generates a secp256k1 private key from the given bytes
|
|
||||||
func SecpPrivKeyGen(bz []byte) tmcrypto.PrivKey {
|
|
||||||
var bzArr [32]byte
|
|
||||||
copy(bzArr[:], bz)
|
|
||||||
return secp256k1.PrivKeySecp256k1(bzArr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateAccount creates an account Info object.
|
|
||||||
func (kb baseKeybase) CreateAccount(
|
|
||||||
keyWriter keyWriter, name, mnemonic, bip39Passphrase, encryptPasswd, hdPath string, algo SigningAlgo,
|
|
||||||
) (Info, error) {
|
|
||||||
|
|
||||||
// create master key and derive first key for keyring
|
|
||||||
derivedPriv, err := kb.options.deriveFunc(mnemonic, bip39Passphrase, hdPath, algo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
privKey, err := kb.options.keygenFunc(derivedPriv, algo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var info Info
|
|
||||||
|
|
||||||
if encryptPasswd != "" {
|
|
||||||
info = keyWriter.writeLocalKey(name, privKey, algo)
|
|
||||||
} else {
|
|
||||||
info = kb.writeOfflineKey(keyWriter, name, privKey.PubKey(), algo)
|
|
||||||
}
|
|
||||||
|
|
||||||
return info, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateLedger creates a new reference to a Ledger key pair. It returns a public
|
|
||||||
// key and a derivation path. It returns an error if the device could not be queried.
|
|
||||||
func (kb baseKeybase) CreateLedger(
|
|
||||||
w infoWriter, name string, algo SigningAlgo, hrp string, account, index uint32,
|
|
||||||
) (Info, error) {
|
|
||||||
|
|
||||||
if !IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) {
|
|
||||||
return nil, ErrUnsupportedSigningAlgo
|
|
||||||
}
|
|
||||||
|
|
||||||
coinType := types.GetConfig().GetCoinType()
|
|
||||||
hdPath := hd.NewFundraiserParams(account, coinType, index)
|
|
||||||
|
|
||||||
priv, _, err := crypto.NewPrivKeyLedgerSecp256k1(*hdPath, hrp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return kb.writeLedgerKey(w, name, priv.PubKey(), *hdPath, algo), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMnemonic generates a new key with the given algorithm and language pair.
|
|
||||||
func (kb baseKeybase) CreateMnemonic(
|
|
||||||
keyWriter keyWriter, name string, language Language, passwd string, algo SigningAlgo,
|
|
||||||
) (info Info, mnemonic string, err error) {
|
|
||||||
|
|
||||||
if language != English {
|
|
||||||
return nil, "", ErrUnsupportedLanguage
|
|
||||||
}
|
|
||||||
|
|
||||||
if !IsSupportedAlgorithm(kb.SupportedAlgos(), algo) {
|
|
||||||
return nil, "", ErrUnsupportedSigningAlgo
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default number of words (24): This generates a mnemonic directly from the
|
|
||||||
// number of words by reading system entropy.
|
|
||||||
entropy, err := bip39.NewEntropy(defaultEntropySize)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
mnemonic, err = bip39.NewMnemonic(entropy)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err = kb.CreateAccount(keyWriter, name, mnemonic, DefaultBIP39Passphrase, passwd, fundraiserPath, algo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return info, mnemonic, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kb baseKeybase) writeLedgerKey(w infoWriter, name string, pub tmcrypto.PubKey, path hd.BIP44Params, algo SigningAlgo) Info {
|
|
||||||
info := newLedgerInfo(name, pub, path, algo)
|
|
||||||
w.writeInfo(name, info)
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kb baseKeybase) writeOfflineKey(w infoWriter, name string, pub tmcrypto.PubKey, algo SigningAlgo) Info {
|
|
||||||
info := newOfflineInfo(name, pub, algo)
|
|
||||||
w.writeInfo(name, info)
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kb baseKeybase) writeMultisigKey(w infoWriter, name string, pub tmcrypto.PubKey) Info {
|
|
||||||
info := NewMultiInfo(name, pub)
|
|
||||||
w.writeInfo(name, info)
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
// StdDeriveKey is the default DeriveKey function in the keybase.
|
|
||||||
// For now, it only supports Secp256k1
|
|
||||||
func StdDeriveKey(mnemonic string, bip39Passphrase, hdPath string, algo SigningAlgo) ([]byte, error) {
|
|
||||||
if algo == Secp256k1 {
|
|
||||||
return SecpDeriveKey(mnemonic, bip39Passphrase, hdPath)
|
|
||||||
}
|
|
||||||
return nil, ErrUnsupportedSigningAlgo
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecpDeriveKey derives and returns the secp256k1 private key for the given seed and HD path.
|
|
||||||
func SecpDeriveKey(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) {
|
|
||||||
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
masterPriv, ch := hd.ComputeMastersFromSeed(seed)
|
|
||||||
if len(hdPath) == 0 {
|
|
||||||
return masterPriv[:], nil
|
|
||||||
}
|
|
||||||
derivedKey, err := hd.DerivePrivateKeyForPath(masterPriv, ch, hdPath)
|
|
||||||
return derivedKey[:], err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateHDPath returns BIP 44 object from account and index parameters.
|
|
||||||
func CreateHDPath(account uint32, index uint32) *hd.BIP44Params {
|
|
||||||
return hd.NewFundraiserParams(account, types.GetConfig().GetCoinType(), index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SupportedAlgos returns a list of supported signing algorithms.
|
|
||||||
func (kb baseKeybase) SupportedAlgos() []SigningAlgo {
|
|
||||||
return kb.options.supportedAlgos
|
|
||||||
}
|
|
||||||
|
|
||||||
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
|
|
||||||
func (kb baseKeybase) SupportedAlgosLedger() []SigningAlgo {
|
|
||||||
return kb.options.supportedAlgosLedger
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignWithLedger signs a binary message with the ledger device referenced by an Info object
|
|
||||||
// and returns the signed bytes and the public key. It returns an error if the device could
|
|
||||||
// not be queried or it returned an error.
|
|
||||||
func SignWithLedger(info Info, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) {
|
|
||||||
switch info.(type) {
|
|
||||||
case *ledgerInfo, ledgerInfo:
|
|
||||||
default:
|
|
||||||
return nil, nil, errors.New("not a ledger object")
|
|
||||||
}
|
|
||||||
path, err := info.GetPath()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
priv, err := crypto.NewPrivKeyLedgerSecp256k1Unsafe(*path)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err = priv.Sign(msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig, priv.PubKey(), nil
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CryptoCdc defines the codec required for keys and info
|
// CryptoCdc defines the codec required for keys and info
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
// generated keys are discarded when the process terminates or the type instance is garbage
|
// generated keys are discarded when the process terminates or the type instance is garbage
|
||||||
// collected.
|
// collected.
|
||||||
//
|
//
|
||||||
// NewKeyring
|
// New
|
||||||
//
|
//
|
||||||
// The NewKeyring constructor returns an implementation backed by a keyring library
|
// The New constructor returns an implementation backed by a keyring library
|
||||||
// (https://github.com/99designs/keyring), whose aim is to provide a common abstraction and uniform
|
// (https://github.com/99designs/keyring), whose aim is to provide a common abstraction and uniform
|
||||||
// interface between secret stores available for Windows, macOS, and most GNU/Linux distributions
|
// interface between secret stores available for Windows, macOS, and most GNU/Linux distributions
|
||||||
// as well as operating system-agnostic encrypted file-based backends.
|
// as well as operating system-agnostic encrypted file-based backends.
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
"github.com/tendermint/tendermint/crypto/multisig"
|
"github.com/tendermint/tendermint/crypto/multisig"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
"github.com/cosmos/cosmos-sdk/types"
|
"github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ type Info interface {
|
||||||
// Bip44 Path
|
// Bip44 Path
|
||||||
GetPath() (*hd.BIP44Params, error)
|
GetPath() (*hd.BIP44Params, error)
|
||||||
// Algo
|
// Algo
|
||||||
GetAlgo() SigningAlgo
|
GetAlgo() hd.PubKeyType
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -39,10 +39,10 @@ type localInfo struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PubKey crypto.PubKey `json:"pubkey"`
|
PubKey crypto.PubKey `json:"pubkey"`
|
||||||
PrivKeyArmor string `json:"privkey.armor"`
|
PrivKeyArmor string `json:"privkey.armor"`
|
||||||
Algo SigningAlgo `json:"algo"`
|
Algo hd.PubKeyType `json:"algo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLocalInfo(name string, pub crypto.PubKey, privArmor string, algo SigningAlgo) Info {
|
func newLocalInfo(name string, pub crypto.PubKey, privArmor string, algo hd.PubKeyType) Info {
|
||||||
return &localInfo{
|
return &localInfo{
|
||||||
Name: name,
|
Name: name,
|
||||||
PubKey: pub,
|
PubKey: pub,
|
||||||
|
@ -72,7 +72,7 @@ func (i localInfo) GetAddress() types.AccAddress {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetType implements Info interface
|
// GetType implements Info interface
|
||||||
func (i localInfo) GetAlgo() SigningAlgo {
|
func (i localInfo) GetAlgo() hd.PubKeyType {
|
||||||
return i.Algo
|
return i.Algo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,10 +87,10 @@ type ledgerInfo struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PubKey crypto.PubKey `json:"pubkey"`
|
PubKey crypto.PubKey `json:"pubkey"`
|
||||||
Path hd.BIP44Params `json:"path"`
|
Path hd.BIP44Params `json:"path"`
|
||||||
Algo SigningAlgo `json:"algo"`
|
Algo hd.PubKeyType `json:"algo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLedgerInfo(name string, pub crypto.PubKey, path hd.BIP44Params, algo SigningAlgo) Info {
|
func newLedgerInfo(name string, pub crypto.PubKey, path hd.BIP44Params, algo hd.PubKeyType) Info {
|
||||||
return &ledgerInfo{
|
return &ledgerInfo{
|
||||||
Name: name,
|
Name: name,
|
||||||
PubKey: pub,
|
PubKey: pub,
|
||||||
|
@ -120,7 +120,7 @@ func (i ledgerInfo) GetAddress() types.AccAddress {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPath implements Info interface
|
// GetPath implements Info interface
|
||||||
func (i ledgerInfo) GetAlgo() SigningAlgo {
|
func (i ledgerInfo) GetAlgo() hd.PubKeyType {
|
||||||
return i.Algo
|
return i.Algo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,10 +135,10 @@ func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) {
|
||||||
type offlineInfo struct {
|
type offlineInfo struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PubKey crypto.PubKey `json:"pubkey"`
|
PubKey crypto.PubKey `json:"pubkey"`
|
||||||
Algo SigningAlgo `json:"algo"`
|
Algo hd.PubKeyType `json:"algo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOfflineInfo(name string, pub crypto.PubKey, algo SigningAlgo) Info {
|
func newOfflineInfo(name string, pub crypto.PubKey, algo hd.PubKeyType) Info {
|
||||||
return &offlineInfo{
|
return &offlineInfo{
|
||||||
Name: name,
|
Name: name,
|
||||||
PubKey: pub,
|
PubKey: pub,
|
||||||
|
@ -162,7 +162,7 @@ func (i offlineInfo) GetPubKey() crypto.PubKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAlgo returns the signing algorithm for the key
|
// GetAlgo returns the signing algorithm for the key
|
||||||
func (i offlineInfo) GetAlgo() SigningAlgo {
|
func (i offlineInfo) GetAlgo() hd.PubKeyType {
|
||||||
return i.Algo
|
return i.Algo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,8 +228,8 @@ func (i multiInfo) GetAddress() types.AccAddress {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPath implements Info interface
|
// GetPath implements Info interface
|
||||||
func (i multiInfo) GetAlgo() SigningAlgo {
|
func (i multiInfo) GetAlgo() hd.PubKeyType {
|
||||||
return MultiAlgo
|
return hd.MultiType
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPath implements Info interface
|
// GetPath implements Info interface
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
package keyring
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Keybase exposes operations on a generic keystore
|
|
||||||
type Keybase interface {
|
|
||||||
// CRUD on the keystore
|
|
||||||
List() ([]Info, error)
|
|
||||||
// Get returns the public information about one key.
|
|
||||||
Get(name string) (Info, error)
|
|
||||||
// Get performs a by-address lookup and returns the public
|
|
||||||
// information about one key if there's any.
|
|
||||||
GetByAddress(address types.AccAddress) (Info, error)
|
|
||||||
// Delete removes a key.
|
|
||||||
Delete(name, passphrase string, skipPass bool) error
|
|
||||||
// Sign bytes, looking up the private key to use.
|
|
||||||
Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error)
|
|
||||||
|
|
||||||
// CreateMnemonic generates a new mnemonic, derives a hierarchical deterministic
|
|
||||||
// key from that. and persists it to storage, encrypted using the provided password.
|
|
||||||
// It returns the generated mnemonic and the key Info. It returns an error if it fails to
|
|
||||||
// generate a key for the given algo type, or if another key is already stored under the
|
|
||||||
// same name.
|
|
||||||
CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error)
|
|
||||||
|
|
||||||
// CreateAccount converts a mnemonic to a private key and BIP 32 HD Path
|
|
||||||
// and persists it, encrypted with the given password.
|
|
||||||
CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo) (Info, error)
|
|
||||||
|
|
||||||
// CreateLedger creates, stores, and returns a new Ledger key reference
|
|
||||||
CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error)
|
|
||||||
|
|
||||||
// CreateOffline creates, stores, and returns a new offline key reference
|
|
||||||
CreateOffline(name string, pubkey crypto.PubKey, algo SigningAlgo) (info Info, err error)
|
|
||||||
|
|
||||||
// CreateMulti creates, stores, and returns a new multsig (offline) key reference
|
|
||||||
CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error)
|
|
||||||
|
|
||||||
// Import imports ASCII armored Info objects.
|
|
||||||
Import(name string, armor string) (err error)
|
|
||||||
|
|
||||||
// ImportPrivKey imports a private key in ASCII armor format.
|
|
||||||
// It returns an error if a key with the same name exists or a wrong encryption passphrase is
|
|
||||||
// supplied.
|
|
||||||
ImportPrivKey(name, armor, passphrase string) error
|
|
||||||
|
|
||||||
// ImportPubKey imports ASCII-armored public keys.
|
|
||||||
// Store a new Info object holding a public key only, i.e. it will
|
|
||||||
// not be possible to sign with it as it lacks the secret key.
|
|
||||||
ImportPubKey(name string, armor string) (err error)
|
|
||||||
|
|
||||||
// Export exports an Info object in ASCII armored format.
|
|
||||||
Export(name string) (armor string, err error)
|
|
||||||
|
|
||||||
// ExportPubKey returns public keys in ASCII armored format.
|
|
||||||
// Retrieve a Info object by its name and return the public key in
|
|
||||||
// a portable format.
|
|
||||||
ExportPubKey(name string) (armor string, err error)
|
|
||||||
|
|
||||||
// ExportPrivKey returns a private key in ASCII armored format.
|
|
||||||
// It returns an error if the key does not exist or a wrong encryption passphrase is supplied.
|
|
||||||
ExportPrivKey(name, decryptPassphrase, encryptPassphrase string) (armor string, err error)
|
|
||||||
|
|
||||||
// ExportPrivateKeyObject *only* works on locally-stored keys. Temporary method until we redo the exporting API
|
|
||||||
ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error)
|
|
||||||
|
|
||||||
// SupportedAlgos returns a list of signing algorithms supported by the keybase
|
|
||||||
SupportedAlgos() []SigningAlgo
|
|
||||||
|
|
||||||
// SupportedAlgosLedger returns a list of signing algorithms supported by the keybase's ledger integration
|
|
||||||
SupportedAlgosLedger() []SigningAlgo
|
|
||||||
}
|
|
|
@ -12,15 +12,16 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/99designs/keyring"
|
"github.com/99designs/keyring"
|
||||||
|
"github.com/cosmos/go-bip39"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/tendermint/crypto/bcrypt"
|
"github.com/tendermint/crypto/bcrypt"
|
||||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/input"
|
"github.com/cosmos/cosmos-sdk/client/input"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto"
|
"github.com/cosmos/cosmos-sdk/crypto"
|
||||||
"github.com/cosmos/cosmos-sdk/types"
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,48 +35,116 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
keyringDirNameFmt = "keyring-%s"
|
keyringFileDirName = "keyring-file"
|
||||||
testKeyringDirNameFmt = "keyring-test-%s"
|
keyringTestDirName = "keyring-test"
|
||||||
passKeyringPrefix = keyringDirNameFmt
|
passKeyringPrefix = "keyring-%s"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Keybase = keyringKeybase{}
|
var (
|
||||||
|
_ Keyring = &keystore{}
|
||||||
|
maxPassphraseEntryAttempts = 3
|
||||||
|
)
|
||||||
|
|
||||||
// keyringKeybase implements the Keybase interface by using the Keyring library
|
// Keyring exposes operations over a backend supported by github.com/99designs/keyring.
|
||||||
// for account key persistence.
|
type Keyring interface {
|
||||||
type keyringKeybase struct {
|
// List all keys.
|
||||||
base baseKeybase
|
List() ([]Info, error)
|
||||||
db keyring.Keyring
|
|
||||||
|
// Key and KeyByAddress return keys by uid and address respectively.
|
||||||
|
Key(uid string) (Info, error)
|
||||||
|
KeyByAddress(address sdk.Address) (Info, error)
|
||||||
|
|
||||||
|
// Delete and DeleteByAddress remove keys from the keyring.
|
||||||
|
Delete(uid string) error
|
||||||
|
DeleteByAddress(address sdk.Address) error
|
||||||
|
|
||||||
|
// NewMnemonic generates a new mnemonic, derives a hierarchical deterministic
|
||||||
|
// key from that, and persists it to the storage. Returns the generated mnemonic and the key
|
||||||
|
// Info. It returns an error if it fails to generate a key for the given algo type, or if
|
||||||
|
// another key is already stored under the same name.
|
||||||
|
NewMnemonic(uid string, language Language, hdPath string, algo SignatureAlgo) (Info, string, error)
|
||||||
|
|
||||||
|
// NewAccount converts a mnemonic to a private key and BIP-39 HD Path and persists it.
|
||||||
|
NewAccount(uid, mnemonic, bip39Passwd, hdPath string, algo SignatureAlgo) (Info, error)
|
||||||
|
|
||||||
|
// SaveLedgerKey retrieves a public key reference from a Ledger device and persists it.
|
||||||
|
SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (Info, error)
|
||||||
|
|
||||||
|
// SavePubKey stores a public key and returns the persisted Info structure.
|
||||||
|
SavePubKey(uid string, pubkey tmcrypto.PubKey, algo hd.PubKeyType) (Info, error)
|
||||||
|
|
||||||
|
// SaveMultisig stores and returns a new multsig (offline) key reference.
|
||||||
|
SaveMultisig(uid string, pubkey tmcrypto.PubKey) (Info, error)
|
||||||
|
|
||||||
|
Signer
|
||||||
|
|
||||||
|
Importer
|
||||||
|
Exporter
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxPassphraseEntryAttempts = 3
|
// Signer is implemented by key stores that want to provide signing capabilities.
|
||||||
|
type Signer interface {
|
||||||
|
// Sign sign byte messages with a user key.
|
||||||
|
Sign(uid string, msg []byte) ([]byte, tmcrypto.PubKey, error)
|
||||||
|
|
||||||
func newKeyringKeybase(db keyring.Keyring, opts ...KeybaseOption) Keybase {
|
// SignByAddress sign byte messages with a user key providing the address.
|
||||||
return keyringKeybase{
|
SignByAddress(address sdk.Address, msg []byte) ([]byte, tmcrypto.PubKey, error)
|
||||||
db: db,
|
|
||||||
base: newBaseKeybase(opts...),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKeyring creates a new instance of a keyring. Keybase
|
// Importer is implemented by key stores that support import of public and private keys.
|
||||||
// options can be applied when generating this new Keybase.
|
type Importer interface {
|
||||||
// Available backends are "os", "file", "kwallet", "pass", "test".
|
// ImportPrivKey imports ASCII armored passphrase-encrypted private keys.
|
||||||
func NewKeyring(
|
ImportPrivKey(uid, armor, passphrase string) error
|
||||||
appName, backend, rootDir string, userInput io.Reader, opts ...KeybaseOption,
|
// ImportPubKey imports ASCII armored public keys.
|
||||||
) (Keybase, error) {
|
ImportPubKey(uid string, armor string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exporter is implemented by key stores that support export of public and private keys.
|
||||||
|
type Exporter interface {
|
||||||
|
// Export public key
|
||||||
|
ExportPubKeyArmor(uid string) (string, error)
|
||||||
|
ExportPubKeyArmorByAddress(address sdk.Address) (string, error)
|
||||||
|
// ExportPrivKey returns a private key in ASCII armored format.
|
||||||
|
// It returns an error if the key does not exist or a wrong encryption passphrase is supplied.
|
||||||
|
ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error)
|
||||||
|
ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option overrides keyring configuration options.
|
||||||
|
type Option func(options *Options)
|
||||||
|
|
||||||
|
//Options define the options of the Keyring
|
||||||
|
type Options struct {
|
||||||
|
SupportedAlgos SigningAlgoList
|
||||||
|
SupportedAlgosLedger SigningAlgoList
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInMemory creates a transient keyring useful for testing
|
||||||
|
// purposes and on-the-fly key generation.
|
||||||
|
// Keybase options can be applied when generating this new Keybase.
|
||||||
|
func NewInMemory(opts ...Option) Keyring {
|
||||||
|
return newKeystore(keyring.NewArrayKeyring(nil), opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKeyring creates a new instance of a keyring.
|
||||||
|
// Keyring ptions can be applied when generating the new instance.
|
||||||
|
// Available backends are "os", "file", "kwallet", "memory", "pass", "test".
|
||||||
|
func New(
|
||||||
|
appName, backend, rootDir string, userInput io.Reader, opts ...Option,
|
||||||
|
) (Keyring, error) {
|
||||||
|
|
||||||
var db keyring.Keyring
|
var db keyring.Keyring
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch backend {
|
switch backend {
|
||||||
case BackendMemory:
|
case BackendMemory:
|
||||||
return NewInMemory(opts...), nil
|
return NewInMemory(opts...), err
|
||||||
case BackendTest:
|
case BackendTest:
|
||||||
db, err = keyring.Open(lkbToKeyringConfig(appName, rootDir, nil, true))
|
db, err = keyring.Open(newTestBackendKeyringConfig(appName, rootDir))
|
||||||
case BackendFile:
|
case BackendFile:
|
||||||
db, err = keyring.Open(newFileBackendKeyringConfig(appName, rootDir, userInput))
|
db, err = keyring.Open(newFileBackendKeyringConfig(appName, rootDir, userInput))
|
||||||
case BackendOS:
|
case BackendOS:
|
||||||
db, err = keyring.Open(lkbToKeyringConfig(appName, rootDir, userInput, false))
|
db, err = keyring.Open(newOSBackendKeyringConfig(appName, rootDir, userInput))
|
||||||
case BackendKWallet:
|
case BackendKWallet:
|
||||||
db, err = keyring.Open(newKWalletBackendKeyringConfig(appName, rootDir, userInput))
|
db, err = keyring.Open(newKWalletBackendKeyringConfig(appName, rootDir, userInput))
|
||||||
case BackendPass:
|
case BackendPass:
|
||||||
|
@ -83,168 +152,72 @@ func NewKeyring(
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown keyring backend %v", backend)
|
return nil, fmt.Errorf("unknown keyring backend %v", backend)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return newKeyringKeybase(db, opts...), nil
|
return newKeystore(db, opts...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInMemory creates a transient keyring useful for testing
|
type keystore struct {
|
||||||
// purposes and on-the-fly key generation.
|
db keyring.Keyring
|
||||||
// Keybase options can be applied when generating this new Keybase.
|
options Options
|
||||||
func NewInMemory(opts ...KeybaseOption) Keybase {
|
|
||||||
return newKeyringKeybase(keyring.NewArrayKeyring(nil), opts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateMnemonic generates a new key and persists it to storage, encrypted
|
func newKeystore(kr keyring.Keyring, opts ...Option) keystore {
|
||||||
// using the provided password. It returns the generated mnemonic and the key Info.
|
// Default options for keybase
|
||||||
// An error is returned if it fails to generate a key for the given algo type,
|
options := Options{
|
||||||
// or if another key is already stored under the same name.
|
SupportedAlgos: SigningAlgoList{hd.Secp256k1},
|
||||||
func (kb keyringKeybase) CreateMnemonic(
|
SupportedAlgosLedger: SigningAlgoList{hd.Secp256k1},
|
||||||
name string, language Language, passwd string, algo SigningAlgo,
|
}
|
||||||
) (info Info, mnemonic string, err error) {
|
|
||||||
|
|
||||||
return kb.base.CreateMnemonic(kb, name, language, passwd, algo)
|
for _, optionFn := range opts {
|
||||||
|
optionFn(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keystore{kr, options}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAccount converts a mnemonic to a private key and persists it, encrypted
|
func (ks keystore) ExportPubKeyArmor(uid string) (string, error) {
|
||||||
// with the given password.
|
bz, err := ks.Key(uid)
|
||||||
func (kb keyringKeybase) CreateAccount(
|
|
||||||
name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo,
|
|
||||||
) (Info, error) {
|
|
||||||
|
|
||||||
return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateLedger creates a new locally-stored reference to a Ledger keypair.
|
|
||||||
// It returns the created key info and an error if the Ledger could not be queried.
|
|
||||||
func (kb keyringKeybase) CreateLedger(
|
|
||||||
name string, algo SigningAlgo, hrp string, account, index uint32,
|
|
||||||
) (Info, error) {
|
|
||||||
|
|
||||||
return kb.base.CreateLedger(kb, name, algo, hrp, account, index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOffline creates a new reference to an offline keypair. It returns the
|
|
||||||
// created key info.
|
|
||||||
func (kb keyringKeybase) CreateOffline(name string, pub tmcrypto.PubKey, algo SigningAlgo) (Info, error) {
|
|
||||||
return kb.base.writeOfflineKey(kb, name, pub, algo), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMulti creates a new reference to a multisig (offline) keypair. It
|
|
||||||
// returns the created key Info object.
|
|
||||||
func (kb keyringKeybase) CreateMulti(name string, pub tmcrypto.PubKey) (Info, error) {
|
|
||||||
return kb.base.writeMultisigKey(kb, name, pub), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns the keys from storage in alphabetical order.
|
|
||||||
func (kb keyringKeybase) List() ([]Info, error) {
|
|
||||||
var res []Info
|
|
||||||
keys, err := kb.db.Keys()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(keys)
|
if bz == nil {
|
||||||
|
return "", fmt.Errorf("no key to export with name: %s", uid)
|
||||||
for _, key := range keys {
|
|
||||||
if strings.HasSuffix(key, infoSuffix) {
|
|
||||||
rawInfo, err := kb.db.Get(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rawInfo.Data) == 0 {
|
|
||||||
return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := unmarshalInfo(rawInfo.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, info)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return crypto.ArmorPubKeyBytes(bz.GetPubKey().Bytes(), string(bz.GetAlgo())), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the public information about one key.
|
func (ks keystore) ExportPubKeyArmorByAddress(address sdk.Address) (string, error) {
|
||||||
func (kb keyringKeybase) Get(name string) (Info, error) {
|
info, err := ks.KeyByAddress(address)
|
||||||
key := infoKey(name)
|
|
||||||
|
|
||||||
bs, err := kb.db.Get(string(key))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(bs.Data) == 0 {
|
return ks.ExportPubKeyArmor(info.GetName())
|
||||||
return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return unmarshalInfo(bs.Data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByAddress fetches a key by address and returns its public information.
|
func (ks keystore) ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error) {
|
||||||
func (kb keyringKeybase) GetByAddress(address types.AccAddress) (Info, error) {
|
priv, err := ks.ExportPrivateKeyObject(uid)
|
||||||
ik, err := kb.db.Get(string(addrHexKey(address)))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ik.Data) == 0 {
|
info, err := ks.Key(uid)
|
||||||
return nil, fmt.Errorf("key with address %s not found", address)
|
|
||||||
}
|
|
||||||
|
|
||||||
bs, err := kb.db.Get(string(ik.Data))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return unmarshalInfo(bs.Data)
|
return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
|
||||||
}
|
|
||||||
|
|
||||||
// Sign signs an arbitrary set of bytes with the named key. It returns an error
|
|
||||||
// if the key doesn't exist or the decryption fails.
|
|
||||||
func (kb keyringKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) {
|
|
||||||
info, err := kb.Get(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var priv tmcrypto.PrivKey
|
|
||||||
|
|
||||||
switch i := info.(type) {
|
|
||||||
case localInfo:
|
|
||||||
if i.PrivKeyArmor == "" {
|
|
||||||
return nil, nil, fmt.Errorf("private key not available")
|
|
||||||
}
|
|
||||||
|
|
||||||
priv, err = cryptoAmino.PrivKeyFromBytes([]byte(i.PrivKeyArmor))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
case ledgerInfo:
|
|
||||||
return SignWithLedger(info, msg)
|
|
||||||
|
|
||||||
case offlineInfo, multiInfo:
|
|
||||||
return nil, info.GetPubKey(), errors.New("cannot sign with offline keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err = priv.Sign(msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig, priv.PubKey(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportPrivateKeyObject exports an armored private key object.
|
// ExportPrivateKeyObject exports an armored private key object.
|
||||||
func (kb keyringKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcrypto.PrivKey, error) {
|
func (ks keystore) ExportPrivateKeyObject(uid string) (tmcrypto.PrivKey, error) {
|
||||||
info, err := kb.Get(name)
|
info, err := ks.Key(uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -270,92 +243,18 @@ func (kb keyringKeybase) ExportPrivateKeyObject(name string, passphrase string)
|
||||||
return priv, nil
|
return priv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export exports armored private key to the caller.
|
func (ks keystore) ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error) {
|
||||||
func (kb keyringKeybase) Export(name string) (armor string, err error) {
|
byAddress, err := ks.KeyByAddress(address)
|
||||||
bz, err := kb.db.Get(string(infoKey(name)))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if bz.Data == nil {
|
return ks.ExportPrivKeyArmor(byAddress.GetName(), encryptPassphrase)
|
||||||
return "", fmt.Errorf("no key to export with name: %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.ArmorInfoBytes(bz.Data), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportPubKey returns public keys in ASCII armored format. It retrieves an Info
|
func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error {
|
||||||
// object by its name and return the public key in a portable format.
|
if _, err := ks.Key(uid); err == nil {
|
||||||
func (kb keyringKeybase) ExportPubKey(name string) (armor string, err error) {
|
return fmt.Errorf("cannot overwrite key: %s", uid)
|
||||||
bz, err := kb.Get(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if bz == nil {
|
|
||||||
return "", fmt.Errorf("no key to export with name: %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.ArmorPubKeyBytes(bz.GetPubKey().Bytes(), string(bz.GetAlgo())), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import imports armored private key.
|
|
||||||
func (kb keyringKeybase) Import(name string, armor string) error {
|
|
||||||
bz, _ := kb.Get(name)
|
|
||||||
|
|
||||||
if bz != nil {
|
|
||||||
pubkey := bz.GetPubKey()
|
|
||||||
|
|
||||||
if len(pubkey.Bytes()) > 0 {
|
|
||||||
return fmt.Errorf("cannot overwrite data for name: %s", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
infoBytes, err := crypto.UnarmorInfoBytes(armor)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := unmarshalInfo(infoBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
kb.writeInfo(name, info)
|
|
||||||
|
|
||||||
err = kb.db.Set(keyring.Item{
|
|
||||||
Key: string(addrHexKey(info.GetAddress())),
|
|
||||||
Data: infoKey(name),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportPrivKey returns a private key in ASCII armored format. An error is returned
|
|
||||||
// if the key does not exist or a wrong encryption passphrase is supplied.
|
|
||||||
func (kb keyringKeybase) ExportPrivKey(name, decryptPassphrase, encryptPassphrase string) (armor string, err error) {
|
|
||||||
priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := kb.Get(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImportPrivKey imports a private key in ASCII armor format. An error is returned
|
|
||||||
// if a key with the same name exists or a wrong encryption passphrase is
|
|
||||||
// supplied.
|
|
||||||
func (kb keyringKeybase) ImportPrivKey(name, armor, passphrase string) error {
|
|
||||||
if kb.HasKey(name) {
|
|
||||||
return fmt.Errorf("cannot overwrite key: %s", name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
privKey, algo, err := crypto.UnarmorDecryptPrivKey(armor, passphrase)
|
privKey, algo, err := crypto.UnarmorDecryptPrivKey(armor, passphrase)
|
||||||
|
@ -363,28 +262,17 @@ func (kb keyringKeybase) ImportPrivKey(name, armor, passphrase string) error {
|
||||||
return errors.Wrap(err, "failed to decrypt private key")
|
return errors.Wrap(err, "failed to decrypt private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: The keyring keystore has no need for a passphrase.
|
_, err = ks.writeLocalKey(uid, privKey, hd.PubKeyType(algo))
|
||||||
kb.writeLocalKey(name, privKey, SigningAlgo(algo))
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasKey returns whether the key exists in the keyring.
|
func (ks keystore) ImportPubKey(uid string, armor string) error {
|
||||||
func (kb keyringKeybase) HasKey(name string) bool {
|
if _, err := ks.Key(uid); err == nil {
|
||||||
bz, _ := kb.Get(name)
|
return fmt.Errorf("cannot overwrite key: %s", uid)
|
||||||
return bz != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImportPubKey imports an ASCII-armored public key. It will store a new Info
|
|
||||||
// object holding a public key only, i.e. it will not be possible to sign with
|
|
||||||
// it as it lacks the secret key.
|
|
||||||
func (kb keyringKeybase) ImportPubKey(name string, armor string) error {
|
|
||||||
bz, _ := kb.Get(name)
|
|
||||||
if bz != nil {
|
|
||||||
pubkey := bz.GetPubKey()
|
|
||||||
|
|
||||||
if len(pubkey.Bytes()) > 0 {
|
|
||||||
return fmt.Errorf("cannot overwrite data for name: %s", name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armor)
|
pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armor)
|
||||||
|
@ -397,27 +285,7 @@ func (kb keyringKeybase) ImportPubKey(name string, armor string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(algo))
|
_, err = ks.writeOfflineKey(uid, pubKey, hd.PubKeyType(algo))
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete removes key forever, but we must present the proper passphrase before
|
|
||||||
// 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, _ string, _ bool) error {
|
|
||||||
// verify we have the proper password before deleting
|
|
||||||
info, err := kb.Get(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = kb.db.Remove(string(addrHexKey(info.GetAddress())))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = kb.db.Remove(string(infoKey(name)))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -425,59 +293,257 @@ func (kb keyringKeybase) Delete(name, _ string, _ bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportedAlgos returns a list of supported signing algorithms.
|
func (ks keystore) Sign(uid string, msg []byte) ([]byte, tmcrypto.PubKey, error) {
|
||||||
func (kb keyringKeybase) SupportedAlgos() []SigningAlgo {
|
info, err := ks.Key(uid)
|
||||||
return kb.base.SupportedAlgos()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
|
|
||||||
func (kb keyringKeybase) SupportedAlgosLedger() []SigningAlgo {
|
|
||||||
return kb.base.SupportedAlgosLedger()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kb keyringKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, algo SigningAlgo) Info {
|
|
||||||
// encrypt private key using keyring
|
|
||||||
pub := priv.PubKey()
|
|
||||||
info := newLocalInfo(name, pub, string(priv.Bytes()), algo)
|
|
||||||
|
|
||||||
kb.writeInfo(name, info)
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kb keyringKeybase) writeInfo(name string, info Info) {
|
|
||||||
// write the info by key
|
|
||||||
key := infoKey(name)
|
|
||||||
serializedInfo := marshalInfo(info)
|
|
||||||
|
|
||||||
err := kb.db.Set(keyring.Item{
|
|
||||||
Key: string(key),
|
|
||||||
Data: serializedInfo,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = kb.db.Set(keyring.Item{
|
var priv tmcrypto.PrivKey
|
||||||
Key: string(addrHexKey(info.GetAddress())),
|
|
||||||
Data: key,
|
switch i := info.(type) {
|
||||||
})
|
case localInfo:
|
||||||
if err != nil {
|
if i.PrivKeyArmor == "" {
|
||||||
panic(err)
|
return nil, nil, fmt.Errorf("private key not available")
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err = cryptoAmino.PrivKeyFromBytes([]byte(i.PrivKeyArmor))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case ledgerInfo:
|
||||||
|
return SignWithLedger(info, msg)
|
||||||
|
|
||||||
|
case offlineInfo, multiInfo:
|
||||||
|
return nil, info.GetPubKey(), errors.New("cannot sign with offline keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sig, err := priv.Sign(msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sig, priv.PubKey(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lkbToKeyringConfig(appName, dir string, buf io.Reader, test bool) keyring.Config {
|
func (ks keystore) SignByAddress(address sdk.Address, msg []byte) ([]byte, tmcrypto.PubKey, error) {
|
||||||
if test {
|
key, err := ks.KeyByAddress(address)
|
||||||
return keyring.Config{
|
if err != nil {
|
||||||
AllowedBackends: []keyring.BackendType{keyring.FileBackend},
|
return nil, nil, err
|
||||||
ServiceName: appName,
|
}
|
||||||
FileDir: filepath.Join(dir, fmt.Sprintf(testKeyringDirNameFmt, appName)),
|
|
||||||
FilePasswordFunc: func(_ string) (string, error) {
|
return ks.Sign(key.GetName(), msg)
|
||||||
return "test", nil
|
}
|
||||||
},
|
|
||||||
|
func (ks keystore) SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (Info, error) {
|
||||||
|
if !ks.options.SupportedAlgosLedger.Contains(algo) {
|
||||||
|
return nil, ErrUnsupportedSigningAlgo
|
||||||
|
}
|
||||||
|
|
||||||
|
hdPath := hd.NewFundraiserParams(account, coinType, index)
|
||||||
|
|
||||||
|
priv, _, err := crypto.NewPrivKeyLedgerSecp256k1(*hdPath, hrp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ks.writeLedgerKey(uid, priv.PubKey(), *hdPath, algo.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) writeLedgerKey(name string, pub tmcrypto.PubKey, path hd.BIP44Params, algo hd.PubKeyType) (Info, error) {
|
||||||
|
info := newLedgerInfo(name, pub, path, algo)
|
||||||
|
err := ks.writeInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) SaveMultisig(uid string, pubkey tmcrypto.PubKey) (Info, error) {
|
||||||
|
return ks.writeMultisigKey(uid, pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) SavePubKey(uid string, pubkey tmcrypto.PubKey, algo hd.PubKeyType) (Info, error) {
|
||||||
|
return ks.writeOfflineKey(uid, pubkey, algo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) DeleteByAddress(address sdk.Address) error {
|
||||||
|
info, err := ks.KeyByAddress(address)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ks.Delete(info.GetName())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) Delete(uid string) error {
|
||||||
|
info, err := ks.Key(uid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ks.db.Remove(addrHexKeyAsString(info.GetAddress()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ks.db.Remove(string(infoKey(uid)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) KeyByAddress(address sdk.Address) (Info, error) {
|
||||||
|
ik, err := ks.db.Get(addrHexKeyAsString(address))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ik.Data) == 0 {
|
||||||
|
return nil, fmt.Errorf("key with address %s not found", address)
|
||||||
|
}
|
||||||
|
|
||||||
|
bs, err := ks.db.Get(string(ik.Data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshalInfo(bs.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) List() ([]Info, error) {
|
||||||
|
var res []Info
|
||||||
|
keys, err := ks.db.Keys()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
if strings.HasSuffix(key, infoSuffix) {
|
||||||
|
rawInfo, err := ks.db.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rawInfo.Data) == 0 {
|
||||||
|
return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := unmarshalInfo(rawInfo.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) NewMnemonic(uid string, language Language, hdPath string, algo SignatureAlgo) (Info, string, error) {
|
||||||
|
if language != English {
|
||||||
|
return nil, "", ErrUnsupportedLanguage
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ks.isSupportedSigningAlgo(algo) {
|
||||||
|
return nil, "", ErrUnsupportedSigningAlgo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default number of words (24): This generates a mnemonic directly from the
|
||||||
|
// number of words by reading system entropy.
|
||||||
|
entropy, err := bip39.NewEntropy(defaultEntropySize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
mnemonic, err := bip39.NewMnemonic(entropy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := ks.NewAccount(uid, mnemonic, DefaultBIP39Passphrase, hdPath, algo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, mnemonic, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) NewAccount(uid string, mnemonic string, bip39Passphrase string, hdPath string, algo SignatureAlgo) (Info, error) {
|
||||||
|
if !ks.isSupportedSigningAlgo(algo) {
|
||||||
|
return nil, ErrUnsupportedSigningAlgo
|
||||||
|
}
|
||||||
|
|
||||||
|
// create master key and derive first key for keyring
|
||||||
|
derivedPriv, err := algo.Derive()(mnemonic, bip39Passphrase, hdPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
privKey := algo.Generate()(derivedPriv)
|
||||||
|
|
||||||
|
return ks.writeLocalKey(uid, privKey, algo.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) isSupportedSigningAlgo(algo SignatureAlgo) bool {
|
||||||
|
return ks.options.SupportedAlgos.Contains(algo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) Key(uid string) (Info, error) {
|
||||||
|
key := infoKey(uid)
|
||||||
|
|
||||||
|
bs, err := ks.db.Get(string(key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bs.Data) == 0 {
|
||||||
|
return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshalInfo(bs.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignWithLedger signs a binary message with the ledger device referenced by an Info object
|
||||||
|
// and returns the signed bytes and the public key. It returns an error if the device could
|
||||||
|
// not be queried or it returned an error.
|
||||||
|
func SignWithLedger(info Info, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) {
|
||||||
|
switch info.(type) {
|
||||||
|
case *ledgerInfo, ledgerInfo:
|
||||||
|
default:
|
||||||
|
return nil, nil, errors.New("not a ledger object")
|
||||||
|
}
|
||||||
|
path, err := info.GetPath()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err := crypto.NewPrivKeyLedgerSecp256k1Unsafe(*path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err = priv.Sign(msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sig, priv.PubKey(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOSBackendKeyringConfig(appName, dir string, buf io.Reader) keyring.Config {
|
||||||
return keyring.Config{
|
return keyring.Config{
|
||||||
ServiceName: appName,
|
ServiceName: appName,
|
||||||
FileDir: dir,
|
FileDir: dir,
|
||||||
|
@ -485,6 +551,17 @@ func lkbToKeyringConfig(appName, dir string, buf io.Reader, test bool) keyring.C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTestBackendKeyringConfig(appName, dir string) keyring.Config {
|
||||||
|
return keyring.Config{
|
||||||
|
AllowedBackends: []keyring.BackendType{keyring.FileBackend},
|
||||||
|
ServiceName: appName,
|
||||||
|
FileDir: filepath.Join(dir, keyringTestDirName),
|
||||||
|
FilePasswordFunc: func(_ string) (string, error) {
|
||||||
|
return "test", nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newKWalletBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config {
|
func newKWalletBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config {
|
||||||
return keyring.Config{
|
return keyring.Config{
|
||||||
AllowedBackends: []keyring.BackendType{keyring.KWalletBackend},
|
AllowedBackends: []keyring.BackendType{keyring.KWalletBackend},
|
||||||
|
@ -494,7 +571,7 @@ func newKWalletBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Conf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPassBackendKeyringConfig(appName, dir string, _ io.Reader) keyring.Config {
|
func newPassBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config {
|
||||||
prefix := fmt.Sprintf(passKeyringPrefix, appName)
|
prefix := fmt.Sprintf(passKeyringPrefix, appName)
|
||||||
return keyring.Config{
|
return keyring.Config{
|
||||||
AllowedBackends: []keyring.BackendType{keyring.PassBackend},
|
AllowedBackends: []keyring.BackendType{keyring.PassBackend},
|
||||||
|
@ -504,7 +581,7 @@ func newPassBackendKeyringConfig(appName, dir string, _ io.Reader) keyring.Confi
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFileBackendKeyringConfig(name, dir string, buf io.Reader) keyring.Config {
|
func newFileBackendKeyringConfig(name, dir string, buf io.Reader) keyring.Config {
|
||||||
fileDir := filepath.Join(dir, fmt.Sprintf(keyringDirNameFmt, name))
|
fileDir := filepath.Join(dir, keyringFileDirName)
|
||||||
return keyring.Config{
|
return keyring.Config{
|
||||||
AllowedBackends: []keyring.BackendType{keyring.FileBackend},
|
AllowedBackends: []keyring.BackendType{keyring.FileBackend},
|
||||||
ServiceName: name,
|
ServiceName: name,
|
||||||
|
@ -586,6 +663,88 @@ func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addrHexKey(address types.AccAddress) []byte {
|
func (ks keystore) writeLocalKey(name string, priv tmcrypto.PrivKey, algo hd.PubKeyType) (Info, error) {
|
||||||
return []byte(fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix))
|
// encrypt private key using keyring
|
||||||
|
pub := priv.PubKey()
|
||||||
|
|
||||||
|
info := newLocalInfo(name, pub, string(priv.Bytes()), algo)
|
||||||
|
err := ks.writeInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) writeInfo(info Info) error {
|
||||||
|
// write the info by key
|
||||||
|
key := infoKey(info.GetName())
|
||||||
|
serializedInfo := marshalInfo(info)
|
||||||
|
|
||||||
|
exists, err := ks.existsInDb(info)
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf("public key already exist in keybase")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ks.db.Set(keyring.Item{
|
||||||
|
Key: string(key),
|
||||||
|
Data: serializedInfo,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ks.db.Set(keyring.Item{
|
||||||
|
Key: addrHexKeyAsString(info.GetAddress()),
|
||||||
|
Data: key,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) existsInDb(info Info) (bool, error) {
|
||||||
|
if _, err := ks.db.Get(addrHexKeyAsString(info.GetAddress())); err == nil {
|
||||||
|
return true, nil // address lookup succeeds - info exists
|
||||||
|
} else if err != keyring.ErrKeyNotFound {
|
||||||
|
return false, err // received unexpected error - returns error
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ks.db.Get(string(infoKey(info.GetName()))); err == nil {
|
||||||
|
return true, nil // uid lookup succeeds - info exists
|
||||||
|
} else if err != keyring.ErrKeyNotFound {
|
||||||
|
return false, err // received unexpected error - returns
|
||||||
|
}
|
||||||
|
|
||||||
|
// both lookups failed, info does not exist
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) writeOfflineKey(name string, pub tmcrypto.PubKey, algo hd.PubKeyType) (Info, error) {
|
||||||
|
info := newOfflineInfo(name, pub, algo)
|
||||||
|
err := ks.writeInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ks keystore) writeMultisigKey(name string, pub tmcrypto.PubKey) (Info, error) {
|
||||||
|
info := NewMultiInfo(name, pub)
|
||||||
|
err := ks.writeInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addrHexKeyAsString(address sdk.Address) string {
|
||||||
|
return fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
//+build ledger test_ledger_mock
|
||||||
|
|
||||||
|
package keyring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInMemoryCreateLedger(t *testing.T) {
|
||||||
|
kb := NewInMemory()
|
||||||
|
|
||||||
|
ledger, err := kb.SaveLedgerKey("some_account", hd.Secp256k1, "cosmos", 118, 3, 1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error())
|
||||||
|
require.Nil(t, ledger)
|
||||||
|
t.Skip("ledger nano S: support for ledger devices is not available in this executable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The mock is available, check that the address is correct
|
||||||
|
pubKey := ledger.GetPubKey()
|
||||||
|
pk, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk)
|
||||||
|
|
||||||
|
// Check that restoring the key gets the same results
|
||||||
|
restoredKey, err := kb.Key("some_account")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, restoredKey)
|
||||||
|
require.Equal(t, "some_account", restoredKey.GetName())
|
||||||
|
require.Equal(t, TypeLedger, restoredKey.GetType())
|
||||||
|
pubKey = restoredKey.GetPubKey()
|
||||||
|
pk, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk)
|
||||||
|
|
||||||
|
path, err := restoredKey.GetPath()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "44'/118'/3'/0/1", path.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSignVerify does some detailed checks on how we sign and validate
|
||||||
|
// signatures
|
||||||
|
func TestSignVerifyKeyRingWithLedger(t *testing.T) {
|
||||||
|
dir, cleanup := tests.NewTestCaseDir(t)
|
||||||
|
t.Cleanup(cleanup)
|
||||||
|
kb, err := New("keybasename", "test", dir, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
i1, err := kb.SaveLedgerKey("key", hd.Secp256k1, "cosmos", 118, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error())
|
||||||
|
t.Skip("ledger nano S: support for ledger devices is not available in this executable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.Equal(t, "key", i1.GetName())
|
||||||
|
|
||||||
|
d1 := []byte("my first message")
|
||||||
|
s1, pub1, err := kb.Sign("key", d1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
s2, pub2, err := SignWithLedger(i1, d1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.True(t, pub1.Equals(pub2))
|
||||||
|
require.True(t, bytes.Equal(s1, s2))
|
||||||
|
|
||||||
|
require.Equal(t, i1.GetPubKey(), pub1)
|
||||||
|
require.Equal(t, i1.GetPubKey(), pub2)
|
||||||
|
require.True(t, pub1.VerifyBytes(d1, s1))
|
||||||
|
require.True(t, i1.GetPubKey().VerifyBytes(d1, s1))
|
||||||
|
require.True(t, bytes.Equal(s1, s2))
|
||||||
|
|
||||||
|
localInfo, _, err := kb.NewMnemonic("test", English, types.FullFundraiserPath, hd.Secp256k1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, _, err = SignWithLedger(localInfo, d1)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, "not a ledger object", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAltKeyring_SaveLedgerKey(t *testing.T) {
|
||||||
|
dir, clean := tests.NewTestCaseDir(t)
|
||||||
|
t.Cleanup(clean)
|
||||||
|
|
||||||
|
keyring, err := New(t.Name(), BackendTest, dir, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Test unsupported Algo
|
||||||
|
_, err = keyring.SaveLedgerKey("key", notSupportedAlgo{}, "cosmos", 118, 0, 0)
|
||||||
|
require.EqualError(t, err, ErrUnsupportedSigningAlgo.Error())
|
||||||
|
|
||||||
|
ledger, err := keyring.SaveLedgerKey("some_account", hd.Secp256k1, "cosmos", 118, 3, 1)
|
||||||
|
if err != nil {
|
||||||
|
require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error())
|
||||||
|
t.Skip("ledger nano S: support for ledger devices is not available in this executable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// The mock is available, check that the address is correct
|
||||||
|
require.Equal(t, "some_account", ledger.GetName())
|
||||||
|
pubKey := ledger.GetPubKey()
|
||||||
|
pk, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk)
|
||||||
|
|
||||||
|
// Check that restoring the key gets the same results
|
||||||
|
restoredKey, err := keyring.Key("some_account")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, restoredKey)
|
||||||
|
require.Equal(t, "some_account", restoredKey.GetName())
|
||||||
|
require.Equal(t, TypeLedger, restoredKey.GetType())
|
||||||
|
pubKey = restoredKey.GetPubKey()
|
||||||
|
pk, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk)
|
||||||
|
|
||||||
|
path, err := restoredKey.GetPath()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "44'/118'/3'/0/1", path.String())
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -2,6 +2,7 @@ package keyring
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -179,10 +180,53 @@ func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the underlying storage.
|
// Close the underlying storage.
|
||||||
func (kb dbKeybase) Close() error {
|
func (kb dbKeybase) Close() error { return kb.db.Close() }
|
||||||
return kb.db.Close()
|
|
||||||
|
func infoKey(name string) []byte { return []byte(fmt.Sprintf("%s.%s", name, infoSuffix)) }
|
||||||
|
|
||||||
|
// InfoImporter is implemented by those types that want to provide functions necessary
|
||||||
|
// to migrate keys from LegacyKeybase types to Keyring types.
|
||||||
|
type InfoImporter interface {
|
||||||
|
// Import imports ASCII-armored private keys.
|
||||||
|
Import(uid string, armor string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func infoKey(name string) []byte {
|
type keyringMigrator struct {
|
||||||
return []byte(fmt.Sprintf("%s.%s", name, infoSuffix))
|
kr keystore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInfoImporter(
|
||||||
|
appName, backend, rootDir string, userInput io.Reader, opts ...Option,
|
||||||
|
) (InfoImporter, error) {
|
||||||
|
keyring, err := New(appName, backend, rootDir, userInput, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return keyringMigrator{}, err
|
||||||
|
}
|
||||||
|
kr := keyring.(keystore)
|
||||||
|
return keyringMigrator{kr}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m keyringMigrator) Import(uid string, armor string) error {
|
||||||
|
_, err := m.kr.Key(uid)
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("cannot overwrite key %q", uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
infoBytes, err := crypto.UnarmorInfoBytes(armor)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := unmarshalInfo(infoBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.kr.writeInfo(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeybaseOption overrides options for the db.
|
||||||
|
type KeybaseOption func(*kbOptions)
|
||||||
|
|
||||||
|
type kbOptions struct {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package keyring_test
|
package keyring_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -38,7 +39,21 @@ func TestLegacyKeybase(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, armor)
|
require.NotEmpty(t, armor)
|
||||||
|
|
||||||
|
_, err = kb.ExportPrivKey(keys[0].GetName(), "12345678", "12345678")
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
armoredInfo, err := kb.Export(keys[0].GetName())
|
armoredInfo, err := kb.Export(keys[0].GetName())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, armoredInfo)
|
require.NotEmpty(t, armoredInfo)
|
||||||
|
|
||||||
|
importer, err := keyring.NewInfoImporter("cosmos", "memory", "", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = importer.Import("test", "")
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, io.EOF, err)
|
||||||
|
require.NoError(t, importer.Import("test", armoredInfo))
|
||||||
|
|
||||||
|
err = importer.Import("test", armoredInfo)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, `public key already exist in keybase`, err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
package keyring
|
|
||||||
|
|
||||||
// KeybaseOption overrides options for the db
|
|
||||||
type KeybaseOption func(*kbOptions)
|
|
||||||
|
|
||||||
type kbOptions struct {
|
|
||||||
keygenFunc PrivKeyGenFunc
|
|
||||||
deriveFunc DeriveKeyFunc
|
|
||||||
supportedAlgos []SigningAlgo
|
|
||||||
supportedAlgosLedger []SigningAlgo
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithKeygenFunc applies an overridden key generation function to generate the private key.
|
|
||||||
func WithKeygenFunc(f PrivKeyGenFunc) KeybaseOption {
|
|
||||||
return func(o *kbOptions) {
|
|
||||||
o.keygenFunc = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithDeriveFunc applies an overridden key derivation function to generate the private key.
|
|
||||||
func WithDeriveFunc(f DeriveKeyFunc) KeybaseOption {
|
|
||||||
return func(o *kbOptions) {
|
|
||||||
o.deriveFunc = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithSupportedAlgos defines the list of accepted SigningAlgos.
|
|
||||||
func WithSupportedAlgos(algos []SigningAlgo) KeybaseOption {
|
|
||||||
return func(o *kbOptions) {
|
|
||||||
o.supportedAlgos = algos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithSupportedAlgosLedger defines the list of accepted SigningAlgos compatible with Ledger.
|
|
||||||
func WithSupportedAlgosLedger(algos []SigningAlgo) KeybaseOption {
|
|
||||||
return func(o *kbOptions) {
|
|
||||||
o.supportedAlgosLedger = algos
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +1,33 @@
|
||||||
package keyring
|
package keyring
|
||||||
|
|
||||||
// SigningAlgo defines an algorithm to derive key-pairs which can be used for cryptographic signing.
|
import (
|
||||||
type SigningAlgo string
|
"fmt"
|
||||||
|
|
||||||
const (
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
// MultiAlgo implies that a pubkey is a multisignature
|
|
||||||
MultiAlgo = SigningAlgo("multi")
|
|
||||||
// Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters.
|
|
||||||
Secp256k1 = SigningAlgo("secp256k1")
|
|
||||||
// Ed25519 represents the Ed25519 signature system.
|
|
||||||
// It is currently not supported for end-user keys (wallets/ledgers).
|
|
||||||
Ed25519 = SigningAlgo("ed25519")
|
|
||||||
// Sr25519 represents the Sr25519 signature system.
|
|
||||||
Sr25519 = SigningAlgo("sr25519")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsSupportedAlgorithm returns whether the signing algorithm is in the passed-in list of supported algorithms.
|
type SignatureAlgo interface {
|
||||||
func IsSupportedAlgorithm(supported []SigningAlgo, algo SigningAlgo) bool {
|
Name() hd.PubKeyType
|
||||||
for _, supportedAlgo := range supported {
|
Derive() hd.DeriveFn
|
||||||
if algo == supportedAlgo {
|
Generate() hd.GenerateFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSigningAlgoFromString(str string) (SignatureAlgo, error) {
|
||||||
|
if str != string(hd.Secp256k1.Name()) {
|
||||||
|
return nil, fmt.Errorf("provided algorithm `%s` is not supported", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hd.Secp256k1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SigningAlgoList []SignatureAlgo
|
||||||
|
|
||||||
|
func (l SigningAlgoList) Contains(algo SignatureAlgo) bool {
|
||||||
|
for _, cAlgo := range l {
|
||||||
|
if cAlgo.Name() == algo.Name() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package keyring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewSigningAlgoByString(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
algoStr string
|
||||||
|
isSupported bool
|
||||||
|
expectedAlgo SignatureAlgo
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"supported algorithm",
|
||||||
|
"secp256k1",
|
||||||
|
true,
|
||||||
|
hd.Secp256k1,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"not supported",
|
||||||
|
"notsupportedalgo",
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
fmt.Errorf("provided algorithm `notsupportedalgo` is not supported"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
algorithm, err := NewSigningAlgoFromString(tt.algoStr)
|
||||||
|
if tt.isSupported {
|
||||||
|
require.Equal(t, hd.Secp256k1, algorithm)
|
||||||
|
} else {
|
||||||
|
require.EqualError(t, err, tt.expectedErr.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAltSigningAlgoList_Contains(t *testing.T) {
|
||||||
|
list := SigningAlgoList{
|
||||||
|
hd.Secp256k1,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, list.Contains(hd.Secp256k1))
|
||||||
|
assert.False(t, list.Contains(notSupportedAlgo{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
type notSupportedAlgo struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n notSupportedAlgo) Name() hd.PubKeyType {
|
||||||
|
return "notSupported"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n notSupportedAlgo) Derive() hd.DeriveFn {
|
||||||
|
return hd.Secp256k1.Derive()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n notSupportedAlgo) Generate() hd.GenerateFn {
|
||||||
|
return hd.Secp256k1.Generate()
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
package keyring
|
package keyring
|
||||||
|
|
||||||
import "github.com/tendermint/tendermint/crypto"
|
import (
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
|
)
|
||||||
|
|
||||||
// Language is a language to create the BIP 39 mnemonic in.
|
// Language is a language to create the BIP 39 mnemonic in.
|
||||||
// Currently, only english is supported though.
|
// Currently, only english is supported though.
|
||||||
|
@ -62,7 +66,7 @@ func (kt KeyType) String() string {
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// DeriveKeyFunc defines the function to derive a new key from a seed and hd path
|
// DeriveKeyFunc defines the function to derive a new key from a seed and hd path
|
||||||
DeriveKeyFunc func(mnemonic string, bip39Passphrase, hdPath string, algo SigningAlgo) ([]byte, error)
|
DeriveKeyFunc func(mnemonic string, bip39Passphrase, hdPath string, algo hd.PubKeyType) ([]byte, error)
|
||||||
// PrivKeyGenFunc defines the function to convert derived key bytes to a tendermint private key
|
// PrivKeyGenFunc defines the function to convert derived key bytes to a tendermint private key
|
||||||
PrivKeyGenFunc func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error)
|
PrivKeyGenFunc func(bz []byte, algo hd.PubKeyType) (crypto.PrivKey, error)
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ func Test_writeReadLedgerInfo(t *testing.T) {
|
||||||
bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A")
|
bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A")
|
||||||
copy(tmpKey[:], bz)
|
copy(tmpKey[:], bz)
|
||||||
|
|
||||||
lInfo := newLedgerInfo("some_name", tmpKey, *hd.NewFundraiserParams(5, sdk.CoinType, 1), Secp256k1)
|
lInfo := newLedgerInfo("some_name", tmpKey, *hd.NewFundraiserParams(5, sdk.CoinType, 1), hd.Secp256k1Type)
|
||||||
assert.Equal(t, TypeLedger, lInfo.GetType())
|
assert.Equal(t, TypeLedger, lInfo.GetType())
|
||||||
|
|
||||||
path, err := lInfo.GetPath()
|
path, err := lInfo.GetPath()
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
|
|
||||||
bip39 "github.com/cosmos/go-bip39"
|
bip39 "github.com/cosmos/go-bip39"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
"github.com/cosmos/cosmos-sdk/tests"
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||||
tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1"
|
tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
"github.com/cosmos/cosmos-sdk/tests"
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,9 @@ package server
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
@ -11,31 +13,39 @@ import (
|
||||||
// GenerateCoinKey returns the address of a public key, along with the secret
|
// GenerateCoinKey returns the address of a public key, along with the secret
|
||||||
// phrase to recover the private key.
|
// phrase to recover the private key.
|
||||||
func GenerateCoinKey() (sdk.AccAddress, string, error) {
|
func GenerateCoinKey() (sdk.AccAddress, string, error) {
|
||||||
|
|
||||||
// generate a private key, with recovery phrase
|
// generate a private key, with recovery phrase
|
||||||
info, secret, err := keyring.NewInMemory().CreateMnemonic(
|
info, secret, err := keyring.NewInMemory().NewMnemonic("name", keyring.English, types.FullFundraiserPath, hd.Secp256k1)
|
||||||
"name", keyring.English, "pass", keyring.Secp256k1)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sdk.AccAddress([]byte{}), "", err
|
return sdk.AccAddress([]byte{}), "", err
|
||||||
}
|
}
|
||||||
addr := info.GetPubKey().Address()
|
return sdk.AccAddress(info.GetPubKey().Address()), secret, nil
|
||||||
return sdk.AccAddress(addr), secret, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateSaveCoinKey returns the address of a public key, along with the secret
|
// GenerateSaveCoinKey returns the address of a public key, along with the secret
|
||||||
// phrase to recover the private key.
|
// phrase to recover the private key.
|
||||||
func GenerateSaveCoinKey(keybase keyring.Keybase, keyName, keyPass string, overwrite bool) (sdk.AccAddress, string, error) {
|
func GenerateSaveCoinKey(keybase keyring.Keyring, keyName, keyPass string, overwrite bool) (sdk.AccAddress, string, error) {
|
||||||
|
exists := false
|
||||||
|
_, err := keybase.Key(keyName)
|
||||||
|
if err == nil {
|
||||||
|
exists = true
|
||||||
|
}
|
||||||
|
|
||||||
// ensure no overwrite
|
// ensure no overwrite
|
||||||
if !overwrite {
|
if !overwrite && exists {
|
||||||
_, err := keybase.Get(keyName)
|
return sdk.AccAddress([]byte{}), "", fmt.Errorf(
|
||||||
if err == nil {
|
"key already exists, overwrite is disabled")
|
||||||
return sdk.AccAddress([]byte{}), "", fmt.Errorf(
|
|
||||||
"key already exists, overwrite is disabled")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate a private key, with recovery phrase
|
// generate a private key, with recovery phrase
|
||||||
info, secret, err := keybase.CreateMnemonic(keyName, keyring.English, keyPass, keyring.Secp256k1)
|
if exists {
|
||||||
|
err = keybase.Delete(keyName)
|
||||||
|
if err != nil {
|
||||||
|
return sdk.AccAddress([]byte{}), "", fmt.Errorf(
|
||||||
|
"failed to overwrite key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info, secret, err := keybase.NewMnemonic(keyName, keyring.English, types.FullFundraiserPath, hd.Secp256k1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sdk.AccAddress([]byte{}), "", err
|
return sdk.AccAddress([]byte{}), "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,11 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
"github.com/cosmos/cosmos-sdk/tests"
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateCoinKey(t *testing.T) {
|
func TestGenerateCoinKey(t *testing.T) {
|
||||||
|
@ -16,7 +18,7 @@ func TestGenerateCoinKey(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Test creation
|
// Test creation
|
||||||
info, err := keyring.NewInMemory().CreateAccount("xxx", mnemonic, "", "012345678", keyring.CreateHDPath(0, 0).String(), keyring.Secp256k1)
|
info, err := keyring.NewInMemory().NewAccount("xxx", mnemonic, "", hd.NewFundraiserParams(0, types.GetConfig().GetCoinType(), 0).String(), hd.Secp256k1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, addr, info.GetAddress())
|
require.Equal(t, addr, info.GetAddress())
|
||||||
}
|
}
|
||||||
|
@ -26,19 +28,19 @@ func TestGenerateSaveCoinKey(t *testing.T) {
|
||||||
dir, cleanup := tests.NewTestCaseDir(t)
|
dir, cleanup := tests.NewTestCaseDir(t)
|
||||||
t.Cleanup(cleanup)
|
t.Cleanup(cleanup)
|
||||||
|
|
||||||
kb, err := keyring.NewKeyring(t.Name(), "test", dir, nil)
|
kb, err := keyring.New(t.Name(), "test", dir, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
addr, mnemonic, err := server.GenerateSaveCoinKey(kb, "keyname", "012345678", false)
|
addr, mnemonic, err := server.GenerateSaveCoinKey(kb, "keyname", "012345678", false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Test key was actually saved
|
// Test key was actually saved
|
||||||
info, err := kb.Get("keyname")
|
info, err := kb.Key("keyname")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, addr, info.GetAddress())
|
require.Equal(t, addr, info.GetAddress())
|
||||||
|
|
||||||
// Test in-memory recovery
|
// Test in-memory recovery
|
||||||
info, err = keyring.NewInMemory().CreateAccount("xxx", mnemonic, "", "012345678", keyring.CreateHDPath(0, 0).String(), keyring.Secp256k1)
|
info, err = keyring.NewInMemory().NewAccount("xxx", mnemonic, "", hd.NewFundraiserParams(0, types.GetConfig().GetCoinType(), 0).String(), hd.Secp256k1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, addr, info.GetAddress())
|
require.Equal(t, addr, info.GetAddress())
|
||||||
}
|
}
|
||||||
|
@ -48,7 +50,7 @@ func TestGenerateSaveCoinKeyOverwriteFlag(t *testing.T) {
|
||||||
dir, cleanup := tests.NewTestCaseDir(t)
|
dir, cleanup := tests.NewTestCaseDir(t)
|
||||||
t.Cleanup(cleanup)
|
t.Cleanup(cleanup)
|
||||||
|
|
||||||
kb, err := keyring.NewKeyring(t.Name(), "test", dir, nil)
|
kb, err := keyring.New(t.Name(), "test", dir, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
keyname := "justakey"
|
keyname := "justakey"
|
||||||
|
|
|
@ -65,13 +65,13 @@ func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string)
|
||||||
}
|
}
|
||||||
|
|
||||||
inBuf := bufio.NewReader(cmd.InOrStdin())
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(),
|
kb, err := keyring.New(sdk.KeyringServiceName(),
|
||||||
viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), inBuf)
|
viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), inBuf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
multisigInfo, err := kb.Get(args[1])
|
multisigInfo, err := kb.Key(args[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ func CompleteAndBroadcastTxCLI(txBldr authtypes.TxBuilder, cliCtx context.CLICon
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "%s\n\n", json)
|
_, _ = fmt.Fprintf(os.Stderr, "%s\n\n", json)
|
||||||
|
|
||||||
buf := bufio.NewReader(os.Stdin)
|
buf := bufio.NewReader(os.Stdin)
|
||||||
ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf)
|
ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf, os.Stderr)
|
||||||
if err != nil || !ok {
|
if err != nil || !ok {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction")
|
_, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction")
|
||||||
return err
|
return err
|
||||||
|
@ -184,7 +184,7 @@ func SignStdTx(
|
||||||
|
|
||||||
var signedStdTx authtypes.StdTx
|
var signedStdTx authtypes.StdTx
|
||||||
|
|
||||||
info, err := txBldr.Keybase().Get(name)
|
info, err := txBldr.Keybase().Key(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return signedStdTx, err
|
return signedStdTx, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParamsEqual(t *testing.T) {
|
func TestParamsEqual(t *testing.T) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
// TxBuilder implements a transaction context created in SDK modules.
|
// TxBuilder implements a transaction context created in SDK modules.
|
||||||
type TxBuilder struct {
|
type TxBuilder struct {
|
||||||
txEncoder sdk.TxEncoder
|
txEncoder sdk.TxEncoder
|
||||||
keybase keyring.Keybase
|
keybase keyring.Keyring
|
||||||
accountNumber uint64
|
accountNumber uint64
|
||||||
sequence uint64
|
sequence uint64
|
||||||
gas uint64
|
gas uint64
|
||||||
|
@ -53,7 +53,7 @@ func NewTxBuilder(
|
||||||
// NewTxBuilderFromCLI returns a new initialized TxBuilder with parameters from
|
// NewTxBuilderFromCLI returns a new initialized TxBuilder with parameters from
|
||||||
// the command line using Viper.
|
// the command line using Viper.
|
||||||
func NewTxBuilderFromCLI(input io.Reader) TxBuilder {
|
func NewTxBuilderFromCLI(input io.Reader) TxBuilder {
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), input)
|
kb, err := keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ func (bldr TxBuilder) Gas() uint64 { return bldr.gas }
|
||||||
func (bldr TxBuilder) GasAdjustment() float64 { return bldr.gasAdjustment }
|
func (bldr TxBuilder) GasAdjustment() float64 { return bldr.gasAdjustment }
|
||||||
|
|
||||||
// Keybase returns the keybase
|
// Keybase returns the keybase
|
||||||
func (bldr TxBuilder) Keybase() keyring.Keybase { return bldr.keybase }
|
func (bldr TxBuilder) Keybase() keyring.Keyring { return bldr.keybase }
|
||||||
|
|
||||||
// SimulateAndExecute returns the option to simulate and then execute the transaction
|
// SimulateAndExecute returns the option to simulate and then execute the transaction
|
||||||
// using the gas from the simulation results
|
// using the gas from the simulation results
|
||||||
|
@ -149,7 +149,7 @@ func (bldr TxBuilder) WithGasPrices(gasPrices string) TxBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithKeybase returns a copy of the context with updated keybase.
|
// WithKeybase returns a copy of the context with updated keybase.
|
||||||
func (bldr TxBuilder) WithKeybase(keybase keyring.Keybase) TxBuilder {
|
func (bldr TxBuilder) WithKeybase(keybase keyring.Keyring) TxBuilder {
|
||||||
bldr.keybase = keybase
|
bldr.keybase = keybase
|
||||||
return bldr
|
return bldr
|
||||||
}
|
}
|
||||||
|
@ -272,17 +272,17 @@ func (bldr TxBuilder) SignStdTx(name, passphrase string, stdTx StdTx, appendSig
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeSignature builds a StdSignature given keybase, key name, passphrase, and a StdSignMsg.
|
// MakeSignature builds a StdSignature given keybase, key name, passphrase, and a StdSignMsg.
|
||||||
func MakeSignature(keybase keyring.Keybase, name, passphrase string,
|
func MakeSignature(keybase keyring.Keyring, name, passphrase string,
|
||||||
msg StdSignMsg) (sig StdSignature, err error) {
|
msg StdSignMsg) (sig StdSignature, err error) {
|
||||||
|
|
||||||
if keybase == nil {
|
if keybase == nil {
|
||||||
keybase, err = keyring.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), os.Stdin)
|
keybase, err = keyring.New(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), os.Stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sigBytes, pubkey, err := keybase.Sign(name, passphrase, msg.Bytes())
|
sigBytes, pubkey, err := keybase.Sign(name, msg.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,14 +93,14 @@ func GenTxCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, sm
|
||||||
}
|
}
|
||||||
|
|
||||||
inBuf := bufio.NewReader(cmd.InOrStdin())
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
||||||
kb, err := keyring.NewKeyring(sdk.KeyringServiceName(),
|
kb, err := keyring.New(sdk.KeyringServiceName(),
|
||||||
viper.GetString(flags.FlagKeyringBackend), viper.GetString(flagClientHome), inBuf)
|
viper.GetString(flags.FlagKeyringBackend), viper.GetString(flagClientHome), inBuf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to initialize keybase")
|
return errors.Wrap(err, "failed to initialize keybase")
|
||||||
}
|
}
|
||||||
|
|
||||||
name := viper.GetString(flags.FlagName)
|
name := viper.GetString(flags.FlagName)
|
||||||
key, err := kb.Get(name)
|
key, err := kb.Key(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to read from keybase")
|
return errors.Wrap(err, "failed to read from keybase")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue