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`.
|
||||
* (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.
|
||||
* (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.
|
||||
* (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
|
||||
`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/`.
|
||||
* (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/input) [\#5904](https://github.com/cosmos/cosmos-sdk/pull/5904) Removal of unnecessary `GetCheckPassword`, `PrintPrefixed` functions.
|
||||
* (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
|
||||
|
||||
|
@ -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.
|
||||
* (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
|
||||
when method receivers are offline/multisig keys.
|
||||
* (crypto/keyring) [\#5844](https://github.com/cosmos/cosmos-sdk/pull/5844) `Keyring.Sign()` methods no longer decode amino signatures when method receivers
|
||||
are offline/multisig keys.
|
||||
* (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.
|
||||
* (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
|
||||
ChainID string
|
||||
Marshaler codec.Marshaler
|
||||
Keybase keyring.Keybase
|
||||
Input io.Reader
|
||||
Keyring keyring.Keyring
|
||||
Output io.Writer
|
||||
OutputFormat string
|
||||
Height int64
|
||||
|
@ -58,8 +58,19 @@ func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext {
|
|||
var nodeURI string
|
||||
var rpc rpcclient.Client
|
||||
|
||||
homedir := viper.GetString(flags.FlagHome)
|
||||
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 {
|
||||
fmt.Printf("failed to get from fields: %v\n", err)
|
||||
os.Exit(1)
|
||||
|
@ -84,9 +95,10 @@ func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext {
|
|||
Output: os.Stdout,
|
||||
NodeURI: nodeURI,
|
||||
From: viper.GetString(flags.FlagFrom),
|
||||
Keyring: keyring,
|
||||
OutputFormat: viper.GetString(cli.OutputFlag),
|
||||
Height: viper.GetInt64(flags.FlagHeight),
|
||||
HomeDir: viper.GetString(flags.FlagHome),
|
||||
HomeDir: homedir,
|
||||
TrustNode: viper.GetBool(flags.FlagTrustNode),
|
||||
UseLedger: viper.GetBool(flags.FlagUseLedger),
|
||||
BroadcastMode: viper.GetString(flags.FlagBroadcastMode),
|
||||
|
@ -129,6 +141,12 @@ func NewCLIContextWithInput(input io.Reader) CLIContext {
|
|||
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.
|
||||
func (ctx CLIContext) WithInput(r io.Reader) CLIContext {
|
||||
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
|
||||
// an address or key name. If genOnly is true, only a valid Bech32 cosmos
|
||||
// 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 == "" {
|
||||
return nil, "", nil
|
||||
}
|
||||
|
@ -321,20 +339,14 @@ func GetFromFields(input io.Reader, from string, genOnly bool) (sdk.AccAddress,
|
|||
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
|
||||
if addr, err := sdk.AccAddressFromBech32(from); err == nil {
|
||||
info, err = keybase.GetByAddress(addr)
|
||||
info, err = kr.KeyByAddress(addr)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
} else {
|
||||
info, err = keybase.Get(from)
|
||||
info, err = kr.Key(from)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
@ -342,3 +354,10 @@ func GetFromFields(input io.Reader, from string, genOnly bool) (sdk.AccAddress,
|
|||
|
||||
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 (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"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.FlagNode, "tcp://localhost:26657")
|
||||
|
||||
ctx := NewCLIContext()
|
||||
ctx := context.NewCLIContext()
|
||||
require.True(t, ctx.Offline)
|
||||
require.Nil(t, ctx.Client)
|
||||
|
||||
|
@ -24,7 +27,7 @@ func TestCLIContext_WithOffline(t *testing.T) {
|
|||
viper.Set(flags.FlagOffline, false)
|
||||
viper.Set(flags.FlagNode, "tcp://localhost:26657")
|
||||
|
||||
ctx = NewCLIContext()
|
||||
ctx = context.NewCLIContext()
|
||||
require.False(t, ctx.Offline)
|
||||
require.NotNil(t, ctx.Client)
|
||||
}
|
||||
|
@ -59,10 +62,26 @@ func TestCLIContext_WithGenOnly(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
tt := tt
|
||||
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.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 (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
|
@ -36,40 +36,15 @@ func GetPassword(prompt string, buf *bufio.Reader) (pass string, err error) {
|
|||
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.
|
||||
// "y", "Y", "yes", "YES", and "Yes" all count as confirmations.
|
||||
// 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() {
|
||||
fmt.Printf("%s [y/N]: ", prompt)
|
||||
}
|
||||
|
||||
response, err := readLineFromBuf(buf)
|
||||
response, err := readLineFromBuf(r)
|
||||
if err != nil {
|
||||
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.
|
||||
func GetString(prompt string, buf *bufio.Reader) (string, error) {
|
||||
if inputIsTty() && prompt != "" {
|
||||
PrintPrefixed(prompt)
|
||||
fmt.Fprintf(os.Stderr, "> %s\n", prompt)
|
||||
}
|
||||
|
||||
out, err := readLineFromBuf(buf)
|
||||
|
@ -117,9 +92,3 @@ func readLineFromBuf(buf *bufio.Reader) (string, error) {
|
|||
}
|
||||
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/input"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
|
@ -27,6 +28,7 @@ const (
|
|||
flagInteractive = "interactive"
|
||||
flagRecover = "recover"
|
||||
flagNoBackup = "no-backup"
|
||||
flagCoinType = "coin-type"
|
||||
flagAccount = "account"
|
||||
flagIndex = "index"
|
||||
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(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().Uint32(flagCoinType, sdk.CoinType, "coin type 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().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
|
||||
}
|
||||
|
||||
func getKeybase(transient bool, buf io.Reader) (keyring.Keybase, error) {
|
||||
func getKeybase(transient bool, buf io.Reader) (keyring.Keyring, error) {
|
||||
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 {
|
||||
|
@ -107,7 +110,7 @@ input
|
|||
output
|
||||
- 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
|
||||
|
||||
name := args[0]
|
||||
|
@ -115,25 +118,27 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
|||
interactive := viper.GetBool(flagInteractive)
|
||||
showMnemonic := !viper.GetBool(flagNoBackup)
|
||||
|
||||
algo := keyring.SigningAlgo(viper.GetString(flagKeyAlgo))
|
||||
if algo == keyring.SigningAlgo("") {
|
||||
algo = keyring.Secp256k1
|
||||
}
|
||||
if !keyring.IsSupportedAlgorithm(kb.SupportedAlgos(), algo) {
|
||||
return keyring.ErrUnsupportedSigningAlgo
|
||||
algo, err := keyring.NewSigningAlgoFromString(viper.GetString(flagKeyAlgo))
|
||||
if err != nil {
|
||||
algo = hd.Secp256k1
|
||||
}
|
||||
|
||||
if !viper.GetBool(flags.FlagDryRun) {
|
||||
_, err = kb.Get(name)
|
||||
_, err = kb.Key(name)
|
||||
if err == nil {
|
||||
// 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 {
|
||||
return err2
|
||||
}
|
||||
if !response {
|
||||
return errors.New("aborted")
|
||||
}
|
||||
|
||||
err2 = kb.Delete(name)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
}
|
||||
|
||||
multisigKeys := viper.GetStringSlice(flagMultisig)
|
||||
|
@ -146,7 +151,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
|||
}
|
||||
|
||||
for _, keyname := range multisigKeys {
|
||||
k, err := kb.Get(keyname)
|
||||
k, err := kb.Key(keyname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -161,7 +166,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
|||
}
|
||||
|
||||
pk := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks)
|
||||
if _, err := kb.CreateMulti(name, pk); err != nil {
|
||||
if _, err := kb.SaveMultisig(name, pk); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -175,13 +180,14 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = kb.CreateOffline(name, pk, algo)
|
||||
_, err = kb.SavePubKey(name, pk, algo.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
coinType := uint32(viper.GetInt(flagCoinType))
|
||||
account := uint32(viper.GetInt(flagAccount))
|
||||
index := uint32(viper.GetInt(flagIndex))
|
||||
|
||||
|
@ -189,7 +195,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keybase, inBuf *buf
|
|||
var hdPath string
|
||||
|
||||
if useBIP44 {
|
||||
hdPath = keyring.CreateHDPath(account, index).String()
|
||||
hdPath = hd.CreateHDPath(coinType, account, index).String()
|
||||
} else {
|
||||
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")
|
||||
}
|
||||
|
||||
if !keyring.IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) {
|
||||
return keyring.ErrUnsupportedSigningAlgo
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -41,6 +41,9 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
|
|||
t.Cleanup(kbCleanUp)
|
||||
viper.Set(flags.FlagHome, kbHome)
|
||||
viper.Set(flags.FlagUseLedger, true)
|
||||
viper.Set(flagAccount, "0")
|
||||
viper.Set(flagIndex, "0")
|
||||
viper.Set(flagCoinType, "330")
|
||||
|
||||
/// Test Text
|
||||
viper.Set(cli.OutputFlag, OutputFormatText)
|
||||
|
@ -50,14 +53,14 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
|
|||
require.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
||||
|
||||
// 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.NotNil(t, kb)
|
||||
t.Cleanup(func() {
|
||||
kb.Delete("keyname1", "", false)
|
||||
kb.Delete("keyname1")
|
||||
})
|
||||
mockIn.Reset("test1234\n")
|
||||
key1, err := kb.Get("keyname1")
|
||||
key1, err := kb.Key("keyname1")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, key1)
|
||||
|
||||
|
@ -90,17 +93,18 @@ func Test_runAddCmdLedger(t *testing.T) {
|
|||
viper.Set(cli.OutputFlag, OutputFormatText)
|
||||
// Now enter password
|
||||
mockIn.Reset("test1234\ntest1234\n")
|
||||
viper.Set(flagCoinType, sdk.CoinType)
|
||||
require.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
||||
|
||||
// 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.NotNil(t, kb)
|
||||
t.Cleanup(func() {
|
||||
kb.Delete("keyname1", "", false)
|
||||
kb.Delete("keyname1")
|
||||
})
|
||||
mockIn.Reset("test1234\n")
|
||||
key1, err := kb.Get("keyname1")
|
||||
key1, err := kb.Key("keyname1")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, key1)
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
|
@ -17,36 +16,37 @@ import (
|
|||
|
||||
func Test_runAddCmdBasic(t *testing.T) {
|
||||
cmd := AddKeyCommand()
|
||||
assert.NotNil(t, cmd)
|
||||
require.NotNil(t, cmd)
|
||||
mockIn, _, _ := tests.ApplyMockIO(cmd)
|
||||
|
||||
kbHome, kbCleanUp := tests.NewTestCaseDir(t)
|
||||
assert.NotNil(t, kbHome)
|
||||
require.NotNil(t, kbHome)
|
||||
t.Cleanup(kbCleanUp)
|
||||
viper.Set(flags.FlagHome, kbHome)
|
||||
viper.Set(cli.OutputFlag, OutputFormatText)
|
||||
viper.Set(flags.FlagUseLedger, false)
|
||||
|
||||
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)
|
||||
t.Cleanup(func() {
|
||||
kb.Delete("keyname1", "", false) // nolint:errcheck
|
||||
kb.Delete("keyname2", "", false) // nolint:errcheck
|
||||
kb.Delete("keyname1") // 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")
|
||||
assert.Error(t, runAddCmd(cmd, []string{"keyname1"}))
|
||||
require.Error(t, runAddCmd(cmd, []string{"keyname1"}))
|
||||
|
||||
assert.NoError(t, runAddCmd(cmd, []string{"keyname2"}))
|
||||
assert.Error(t, runAddCmd(cmd, []string{"keyname2"}))
|
||||
require.NoError(t, runAddCmd(cmd, []string{"keyname2"}))
|
||||
require.Error(t, runAddCmd(cmd, []string{"keyname2"}))
|
||||
mockIn.Reset("y\n")
|
||||
assert.NoError(t, runAddCmd(cmd, []string{"keyname2"}))
|
||||
require.NoError(t, runAddCmd(cmd, []string{"keyname2"}))
|
||||
|
||||
// test --dry-run
|
||||
assert.NoError(t, runAddCmd(cmd, []string{"keyname4"}))
|
||||
assert.Error(t, runAddCmd(cmd, []string{"keyname4"}))
|
||||
require.NoError(t, runAddCmd(cmd, []string{"keyname4"}))
|
||||
require.Error(t, runAddCmd(cmd, []string{"keyname4"}))
|
||||
|
||||
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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
)
|
||||
|
||||
|
@ -58,15 +58,9 @@ func TestMarshalJSON(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := MarshalJSON(tt.args.o)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s\n", got)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("MarshalJSON() = %v, want %v", got, tt.want)
|
||||
}
|
||||
got, err := keys.MarshalJSON(tt.args.o)
|
||||
require.Equal(t, tt.wantErr, err != nil)
|
||||
require.True(t, bytes.Equal(got, tt.want))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -94,10 +88,8 @@ func TestUnmarshalJSON(t *testing.T) {
|
|||
for idx, tt := range tests {
|
||||
idx, tt := idx, tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := UnmarshalJSON(tt.args.bz, tt.args.ptr); (err != nil) != tt.wantErr {
|
||||
t.Errorf("unmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
err := keys.UnmarshalJSON(tt.args.bz, tt.args.ptr)
|
||||
require.Equal(t, tt.wantErr, err != nil)
|
||||
// Confirm deserialized objects are the same
|
||||
require.Equal(t, data.Keys[idx], data.Answers[idx])
|
||||
})
|
||||
|
|
|
@ -2,7 +2,6 @@ package keys
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"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 {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, name := range args {
|
||||
info, err := kb.Get(name)
|
||||
info, err := kb.Key(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.GetType() == keyring.TypeLedger || info.GetType() == keyring.TypeOffline {
|
||||
// confirm deletion, unless -y is passed
|
||||
if !viper.GetBool(flagYes) {
|
||||
if err := confirmDeletion(buf); err != nil {
|
||||
if yes, err := input.GetConfirmation("Key reference will be deleted. Continue?", buf, cmd.ErrOrStderr()); err != nil {
|
||||
return err
|
||||
} else if !yes {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := kb.Delete(name, "", true); err != nil {
|
||||
if err := kb.Delete(name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.GetType() == keyring.TypeLedger || info.GetType() == keyring.TypeOffline {
|
||||
cmd.PrintErrln("Public key reference deleted")
|
||||
return nil
|
||||
}
|
||||
|
||||
// old password and skip flag arguments are ignored
|
||||
if err := kb.Delete(name, "", true); err != nil {
|
||||
return err
|
||||
continue
|
||||
}
|
||||
cmd.PrintErrln("Key deleted forever (uh oh!)")
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
|
@ -20,30 +20,25 @@ func Test_runDeleteCmd(t *testing.T) {
|
|||
|
||||
yesF, _ := deleteKeyCommand.Flags().GetBool(flagYes)
|
||||
forceF, _ := deleteKeyCommand.Flags().GetBool(flagForce)
|
||||
|
||||
require.False(t, yesF)
|
||||
require.False(t, forceF)
|
||||
|
||||
fakeKeyName1 := "runDeleteCmd_Key1"
|
||||
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
|
||||
kbHome, cleanUp := tests.NewTestCaseDir(t)
|
||||
t.Cleanup(cleanUp)
|
||||
viper.Set(flags.FlagHome, kbHome)
|
||||
|
||||
// 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)
|
||||
_, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keyring.Secp256k1)
|
||||
_, err = kb.NewAccount(fakeKeyName1, tests.TestMnemonic, "", path, hd.Secp256k1)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keyring.Secp256k1)
|
||||
_, _, err = kb.NewMnemonic(fakeKeyName2, keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = runDeleteCmd(deleteKeyCommand, []string{"blah"})
|
||||
|
@ -55,53 +50,21 @@ func Test_runDeleteCmd(t *testing.T) {
|
|||
require.Error(t, err)
|
||||
require.Equal(t, "EOF", err.Error())
|
||||
|
||||
{
|
||||
_, err = kb.Get(fakeKeyName1)
|
||||
_, err = kb.Key(fakeKeyName1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Now there is a confirmation
|
||||
viper.Set(flagYes, true)
|
||||
require.NoError(t, runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1}))
|
||||
|
||||
_, err = kb.Get(fakeKeyName1)
|
||||
_, err = kb.Key(fakeKeyName1)
|
||||
require.Error(t, err) // Key1 is gone
|
||||
}
|
||||
|
||||
viper.Set(flagYes, true)
|
||||
_, err = kb.Get(fakeKeyName2)
|
||||
_, err = kb.Key(fakeKeyName2)
|
||||
require.NoError(t, err)
|
||||
err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName2})
|
||||
require.NoError(t, err)
|
||||
_, err = kb.Get(fakeKeyName2)
|
||||
_, err = kb.Key(fakeKeyName2)
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
armored, err := kb.ExportPrivKey(args[0], decryptPassword, encryptPassword)
|
||||
armored, err := kb.ExportPrivKeyArmor(args[0], encryptPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
|
@ -22,13 +24,14 @@ func Test_runExportCmd(t *testing.T) {
|
|||
viper.Set(flags.FlagHome, kbHome)
|
||||
|
||||
// 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)
|
||||
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)
|
||||
|
||||
// Now enter password
|
||||
|
|
|
@ -26,7 +26,7 @@ func ImportKeyCommand() *cobra.Command {
|
|||
|
||||
func runImportCmd(cmd *cobra.Command, args []string) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -23,10 +23,10 @@ func Test_runImportCmd(t *testing.T) {
|
|||
t.Cleanup(cleanUp)
|
||||
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)
|
||||
t.Cleanup(func() {
|
||||
kb.Delete("keyname1", "", false) // nolint:errcheck
|
||||
kb.Delete("keyname1") // nolint:errcheck
|
||||
})
|
||||
|
||||
keyfile := filepath.Join(kbHome, "key.asc")
|
||||
|
|
|
@ -26,7 +26,7 @@ along with their associated name and address.`,
|
|||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
|
@ -31,14 +33,15 @@ func Test_runListCmd(t *testing.T) {
|
|||
viper.Set(flags.FlagHome, kbHome2)
|
||||
|
||||
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)
|
||||
|
||||
_, 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)
|
||||
|
||||
t.Cleanup(func() {
|
||||
kb.Delete("something", "", false) // nolint:errcheck
|
||||
kb.Delete("something") // nolint:errcheck
|
||||
|
||||
})
|
||||
testData := []struct {
|
||||
|
|
|
@ -61,20 +61,20 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
|||
|
||||
var (
|
||||
tmpDir string
|
||||
keybase keyring.Keybase
|
||||
migrator keyring.InfoImporter
|
||||
)
|
||||
|
||||
if viper.GetBool(flags.FlagDryRun) {
|
||||
tmpDir, err = ioutil.TempDir("", "keybase-migrate-dryrun")
|
||||
tmpDir, err = ioutil.TempDir("", "migrator-migrate-dryrun")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create temporary directory for dryrun migration")
|
||||
}
|
||||
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
keybase, err = keyring.NewKeyring(keyringServiceName, "test", tmpDir, buf)
|
||||
migrator, err = keyring.NewInfoImporter(keyringServiceName, "test", tmpDir, buf)
|
||||
} 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 {
|
||||
return errors.Wrap(err, fmt.Sprintf(
|
||||
|
@ -92,16 +92,10 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
|||
keyName := key.GetName()
|
||||
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)
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
@ -110,7 +104,7 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
if keyType != keyring.TypeLocal {
|
||||
if err := keybase.Import(keyName, legKeyInfo); err != nil {
|
||||
if err := migrator.Import(keyName, legKeyInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -130,7 +124,7 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := keybase.ImportPrivKey(keyName, armoredPriv, migratePassphrase); err != nil {
|
||||
if err := migrator.Import(keyName, armoredPriv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package keys
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/otiai10/copy"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
|
||||
|
@ -18,18 +20,18 @@ func Test_runMigrateCmd(t *testing.T) {
|
|||
mockIn, _, _ := tests.ApplyMockIO(cmd)
|
||||
|
||||
kbHome, kbCleanUp := tests.NewTestCaseDir(t)
|
||||
copy.Copy("testdata", kbHome)
|
||||
assert.NotNil(t, kbHome)
|
||||
t.Cleanup(kbCleanUp)
|
||||
viper.Set(flags.FlagHome, kbHome)
|
||||
|
||||
viper.Set(cli.OutputFlag, OutputFormatText)
|
||||
|
||||
mockIn.Reset("test1234\ntest1234\n")
|
||||
assert.NoError(t, runAddCmd(cmd, []string{"keyname1"}))
|
||||
|
||||
viper.Set(flags.FlagDryRun, true)
|
||||
cmd = MigrateCommand()
|
||||
mockIn, _, _ = tests.ApplyMockIO(cmd)
|
||||
mockIn.Reset("test1234\n")
|
||||
mockIn.Reset("test1234\ntest1234\n")
|
||||
assert.NoError(t, runMigrateCmd(cmd, []string{}))
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ func runMnemonicCmd(cmd *cobra.Command, args []string) error {
|
|||
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))
|
||||
}
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestCommands(t *testing.T) {
|
||||
|
@ -21,5 +22,6 @@ func TestCommands(t *testing.T) {
|
|||
|
||||
func TestMain(m *testing.M) {
|
||||
viper.Set(flags.FlagKeyringBackend, keyring.BackendTest)
|
||||
viper.Set(flagCoinType, sdk.CoinType)
|
||||
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) {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -142,15 +142,15 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func fetchKey(kb keyring.Keybase, keyref string) (keyring.Info, error) {
|
||||
info, err := kb.Get(keyref)
|
||||
func fetchKey(kb keyring.Keyring, keyref string) (keyring.Info, error) {
|
||||
info, err := kb.Key(keyref)
|
||||
if err != nil {
|
||||
accAddr, err := sdk.AccAddressFromBech32(keyref)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
info, err = kb.GetByAddress(accAddr)
|
||||
info, err = kb.KeyByAddress(accAddr)
|
||||
if err != nil {
|
||||
return info, errors.New("key not found")
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
|
||||
"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/tests"
|
||||
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, keyring.TypeMulti, tmp.GetType())
|
||||
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) {
|
||||
|
@ -48,16 +49,19 @@ func Test_runShowCmd(t *testing.T) {
|
|||
|
||||
fakeKeyName1 := "runShowCmd_Key1"
|
||||
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)
|
||||
t.Cleanup(func() {
|
||||
kb.Delete("runShowCmd_Key1", "", false)
|
||||
kb.Delete("runShowCmd_Key2", "", false)
|
||||
kb.Delete("runShowCmd_Key1")
|
||||
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)
|
||||
|
||||
_, 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)
|
||||
|
||||
// Now try single key
|
||||
|
@ -69,7 +73,7 @@ func Test_runShowCmd(t *testing.T) {
|
|||
// try fetch by name
|
||||
require.NoError(t, runShowCmd(cmd, []string{fakeKeyName1}))
|
||||
// try fetch by addr
|
||||
info, err := kb.Get(fakeKeyName1)
|
||||
info, err := kb.Key(fakeKeyName1)
|
||||
require.NoError(t, err)
|
||||
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
|
||||
// signing an application-specific transaction.
|
||||
type Factory struct {
|
||||
keybase keyring.Keybase
|
||||
keybase keyring.Keyring
|
||||
txGenerator Generator
|
||||
accountRetriever AccountRetriever
|
||||
accountNumber uint64
|
||||
|
@ -36,7 +36,7 @@ type Factory struct {
|
|||
}
|
||||
|
||||
func NewFactoryFromCLI(input io.Reader) Factory {
|
||||
kb, err := keyring.NewKeyring(
|
||||
kb, err := keyring.New(
|
||||
sdk.KeyringServiceName(),
|
||||
viper.GetString(flags.FlagKeyringBackend),
|
||||
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) Gas() uint64 { return f.gas }
|
||||
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) Memo() string { return f.memo }
|
||||
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.
|
||||
func (f Factory) WithKeybase(keybase keyring.Keybase) Factory {
|
||||
func (f Factory) WithKeybase(keybase keyring.Keyring) Factory {
|
||||
f.keybase = keybase
|
||||
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)
|
||||
|
||||
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 {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction")
|
||||
return err
|
||||
|
@ -332,7 +332,7 @@ func Sign(txf Factory, name, passphrase string, tx ClientTx) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
sigBytes, pubkey, err := txf.keybase.Sign(name, passphrase, signBytes)
|
||||
sigBytes, pubkey, err := txf.keybase.Sign(name, signBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -15,8 +15,11 @@ import (
|
|||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
"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/keyring"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestArmorUnarmorPrivKey(t *testing.T) {
|
||||
|
@ -26,7 +29,7 @@ func TestArmorUnarmorPrivKey(t *testing.T) {
|
|||
require.Error(t, err)
|
||||
decrypted, algo, err := crypto.UnarmorDecryptPrivKey(armored, "passphrase")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(keyring.Secp256k1), algo)
|
||||
require.Equal(t, string(hd.Secp256k1Type), algo)
|
||||
require.True(t, priv.Equals(decrypted))
|
||||
|
||||
// empty string
|
||||
|
@ -70,14 +73,14 @@ func TestArmorUnarmorPubKey(t *testing.T) {
|
|||
cstore := keyring.NewInMemory()
|
||||
|
||||
// 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)
|
||||
armored := crypto.ArmorPubKeyBytes(info.GetPubKey().Bytes(), "")
|
||||
pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armored)
|
||||
require.NoError(t, err)
|
||||
pub, err := cryptoAmino.PubKeyFromBytes(pubBytes)
|
||||
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()))
|
||||
|
||||
armored = crypto.ArmorPubKeyBytes(info.GetPubKey().Bytes(), "unknown")
|
||||
|
@ -88,7 +91,7 @@ func TestArmorUnarmorPubKey(t *testing.T) {
|
|||
require.Equal(t, "unknown", algo)
|
||||
require.True(t, pub.Equals(info.GetPubKey()))
|
||||
|
||||
armored, err = cstore.ExportPrivKey("Bob", "passphrase", "alessio")
|
||||
armored, err = cstore.ExportPrivKeyArmor("Bob", "passphrase")
|
||||
require.NoError(t, err)
|
||||
_, _, err = crypto.UnarmorPubKeyBytes(armored)
|
||||
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 (
|
||||
"encoding/hex"
|
||||
|
@ -13,6 +13,8 @@ import (
|
|||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
)
|
||||
|
||||
type addrData struct {
|
||||
|
@ -24,19 +26,23 @@ type addrData struct {
|
|||
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 {
|
||||
// NOTE: atom fundraiser address
|
||||
// var hdPath string = "m/44'/118'/0'/0/0"
|
||||
var hdToAddrTable []addrData
|
||||
|
||||
b, err := ioutil.ReadFile("test.json")
|
||||
b, err := ioutil.ReadFile("testdata/test.json")
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
@ -56,8 +62,8 @@ func TestFundraiserCompatibility(t *testing.T) {
|
|||
t.Log("================================")
|
||||
t.Logf("ROUND: %d MNEMONIC: %s", i, d.Mnemonic)
|
||||
|
||||
master, ch := ComputeMastersFromSeed(seed)
|
||||
priv, err := DerivePrivateKeyForPath(master, ch, "44'/118'/0'/0/0")
|
||||
master, ch := hd.ComputeMastersFromSeed(seed)
|
||||
priv, err := hd.DerivePrivateKeyForPath(master, ch, "44'/118'/0'/0/0")
|
||||
require.NoError(t, err)
|
||||
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
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha512"
|
||||
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -255,3 +243,8 @@ func i64(key []byte, data []byte) (il [32]byte, ir [32]byte) {
|
|||
|
||||
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 (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
bip39 "github.com/cosmos/go-bip39"
|
||||
|
@ -21,9 +22,9 @@ func mnemonicToSeed(mnemonic string) []byte {
|
|||
|
||||
// nolint:govet
|
||||
func ExampleStringifyPathParams() {
|
||||
path := NewParams(44, 0, 0, false, 0)
|
||||
path := hd.NewParams(44, 0, 0, false, 0)
|
||||
fmt.Println(path.String())
|
||||
path = NewParams(44, 33, 7, true, 9)
|
||||
path = hd.NewParams(44, 33, 7, true, 9)
|
||||
fmt.Println(path.String())
|
||||
// Output:
|
||||
// 44'/0'/0'/0/0
|
||||
|
@ -31,40 +32,40 @@ func ExampleStringifyPathParams() {
|
|||
}
|
||||
|
||||
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())
|
||||
|
||||
path = NewFundraiserParams(4, types.CoinType, 57)
|
||||
path = hd.NewFundraiserParams(4, types.CoinType, 57)
|
||||
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())
|
||||
}
|
||||
|
||||
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()))
|
||||
|
||||
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()))
|
||||
}
|
||||
|
||||
func TestParamsFromPath(t *testing.T) {
|
||||
goodCases := []struct {
|
||||
params *BIP44Params
|
||||
params *hd.BIP44Params
|
||||
path string
|
||||
}{
|
||||
{&BIP44Params{44, 0, 0, false, 0}, "44'/0'/0'/0/0"},
|
||||
{&BIP44Params{44, 1, 0, false, 0}, "44'/1'/0'/0/0"},
|
||||
{&BIP44Params{44, 0, 1, false, 0}, "44'/0'/1'/0/0"},
|
||||
{&BIP44Params{44, 0, 0, true, 0}, "44'/0'/0'/1/0"},
|
||||
{&BIP44Params{44, 0, 0, false, 1}, "44'/0'/0'/0/1"},
|
||||
{&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, 0, 0, false, 0}, "44'/0'/0'/0/0"},
|
||||
{&hd.BIP44Params{44, 1, 0, false, 0}, "44'/1'/0'/0/0"},
|
||||
{&hd.BIP44Params{44, 0, 1, false, 0}, "44'/0'/1'/0/0"},
|
||||
{&hd.BIP44Params{44, 0, 0, true, 0}, "44'/0'/0'/1/0"},
|
||||
{&hd.BIP44Params{44, 0, 0, false, 1}, "44'/0'/0'/0/1"},
|
||||
{&hd.BIP44Params{44, 1, 1, true, 1}, "44'/1'/1'/1/1"},
|
||||
{&hd.BIP44Params{44, 118, 52, true, 41}, "44'/118'/52'/1/41"},
|
||||
}
|
||||
|
||||
for i, c := range goodCases {
|
||||
params, err := NewParamsFromPath(c.path)
|
||||
params, err := hd.NewParamsFromPath(c.path)
|
||||
errStr := fmt.Sprintf("%d %v", i, c)
|
||||
assert.NoError(t, err, errStr)
|
||||
assert.EqualValues(t, c.params, params, errStr)
|
||||
|
@ -93,7 +94,7 @@ func TestParamsFromPath(t *testing.T) {
|
|||
}
|
||||
|
||||
for i, c := range badCases {
|
||||
params, err := NewParamsFromPath(c.path)
|
||||
params, err := hd.NewParamsFromPath(c.path)
|
||||
errStr := fmt.Sprintf("%d %v", i, c)
|
||||
assert.Nil(t, params, errStr)
|
||||
assert.Error(t, err, errStr)
|
||||
|
@ -106,38 +107,38 @@ func ExampleSomeBIP32TestVecs() {
|
|||
|
||||
seed := mnemonicToSeed("barrel original fuel morning among eternal " +
|
||||
"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()
|
||||
// cosmos
|
||||
priv, err := DerivePrivateKeyForPath(master, ch, types.FullFundraiserPath)
|
||||
priv, err := hd.DerivePrivateKeyForPath(master, ch, types.FullFundraiserPath)
|
||||
if err != nil {
|
||||
fmt.Println("INVALID")
|
||||
} else {
|
||||
fmt.Println(hex.EncodeToString(priv[:]))
|
||||
}
|
||||
// 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 {
|
||||
fmt.Println("INVALID")
|
||||
} else {
|
||||
fmt.Println(hex.EncodeToString(priv[:]))
|
||||
}
|
||||
// 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 {
|
||||
fmt.Println("INVALID")
|
||||
} else {
|
||||
fmt.Println(hex.EncodeToString(priv[:]))
|
||||
}
|
||||
// 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 {
|
||||
fmt.Println("INVALID")
|
||||
} else {
|
||||
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 {
|
||||
fmt.Println("INVALID")
|
||||
} else {
|
||||
|
@ -151,14 +152,14 @@ func ExampleSomeBIP32TestVecs() {
|
|||
seed = mnemonicToSeed(
|
||||
"advice process birth april short trust crater change bacon monkey medal garment " +
|
||||
"gorilla ranch hour rival razor call lunar mention taste vacant woman sister")
|
||||
master, ch = ComputeMastersFromSeed(seed)
|
||||
priv, _ = DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4")
|
||||
master, ch = hd.ComputeMastersFromSeed(seed)
|
||||
priv, _ = hd.DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4")
|
||||
fmt.Println(hex.EncodeToString(priv[:]))
|
||||
|
||||
seed = mnemonicToSeed("idea naive region square margin day captain habit " +
|
||||
"gun second farm pact pulse someone armed")
|
||||
master, ch = ComputeMastersFromSeed(seed)
|
||||
priv, _ = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420")
|
||||
master, ch = hd.ComputeMastersFromSeed(seed)
|
||||
priv, _ = hd.DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420")
|
||||
fmt.Println(hex.EncodeToString(priv[:]))
|
||||
|
||||
fmt.Println()
|
||||
|
@ -167,8 +168,8 @@ func ExampleSomeBIP32TestVecs() {
|
|||
|
||||
// bip32 path: m/0/7
|
||||
seed = mnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history")
|
||||
master, ch = ComputeMastersFromSeed(seed)
|
||||
priv, _ = DerivePrivateKeyForPath(master, ch, "0/7")
|
||||
master, ch = hd.ComputeMastersFromSeed(seed)
|
||||
priv, _ = hd.DerivePrivateKeyForPath(master, ch, "0/7")
|
||||
fmt.Println(hex.EncodeToString(priv[:]))
|
||||
|
||||
// Output: keys from fundraiser test-vector (cosmos, bitcoin, ether)
|
||||
|
@ -188,3 +189,27 @@ func ExampleSomeBIP32TestVecs() {
|
|||
//
|
||||
// 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"
|
||||
|
||||
"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
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
// generated keys are discarded when the process terminates or the type instance is garbage
|
||||
// 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
|
||||
// interface between secret stores available for Windows, macOS, and most GNU/Linux distributions
|
||||
// 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/multisig"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
|
@ -23,7 +23,7 @@ type Info interface {
|
|||
// Bip44 Path
|
||||
GetPath() (*hd.BIP44Params, error)
|
||||
// Algo
|
||||
GetAlgo() SigningAlgo
|
||||
GetAlgo() hd.PubKeyType
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -39,10 +39,10 @@ type localInfo struct {
|
|||
Name string `json:"name"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
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{
|
||||
Name: name,
|
||||
PubKey: pub,
|
||||
|
@ -72,7 +72,7 @@ func (i localInfo) GetAddress() types.AccAddress {
|
|||
}
|
||||
|
||||
// GetType implements Info interface
|
||||
func (i localInfo) GetAlgo() SigningAlgo {
|
||||
func (i localInfo) GetAlgo() hd.PubKeyType {
|
||||
return i.Algo
|
||||
}
|
||||
|
||||
|
@ -87,10 +87,10 @@ type ledgerInfo struct {
|
|||
Name string `json:"name"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
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{
|
||||
Name: name,
|
||||
PubKey: pub,
|
||||
|
@ -120,7 +120,7 @@ func (i ledgerInfo) GetAddress() types.AccAddress {
|
|||
}
|
||||
|
||||
// GetPath implements Info interface
|
||||
func (i ledgerInfo) GetAlgo() SigningAlgo {
|
||||
func (i ledgerInfo) GetAlgo() hd.PubKeyType {
|
||||
return i.Algo
|
||||
}
|
||||
|
||||
|
@ -135,10 +135,10 @@ func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) {
|
|||
type offlineInfo struct {
|
||||
Name string `json:"name"`
|
||||
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{
|
||||
Name: name,
|
||||
PubKey: pub,
|
||||
|
@ -162,7 +162,7 @@ func (i offlineInfo) GetPubKey() crypto.PubKey {
|
|||
}
|
||||
|
||||
// GetAlgo returns the signing algorithm for the key
|
||||
func (i offlineInfo) GetAlgo() SigningAlgo {
|
||||
func (i offlineInfo) GetAlgo() hd.PubKeyType {
|
||||
return i.Algo
|
||||
}
|
||||
|
||||
|
@ -228,8 +228,8 @@ func (i multiInfo) GetAddress() types.AccAddress {
|
|||
}
|
||||
|
||||
// GetPath implements Info interface
|
||||
func (i multiInfo) GetAlgo() SigningAlgo {
|
||||
return MultiAlgo
|
||||
func (i multiInfo) GetAlgo() hd.PubKeyType {
|
||||
return hd.MultiType
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
||||
"github.com/99designs/keyring"
|
||||
"github.com/cosmos/go-bip39"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/tendermint/crypto/bcrypt"
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/input"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -34,48 +35,116 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
keyringDirNameFmt = "keyring-%s"
|
||||
testKeyringDirNameFmt = "keyring-test-%s"
|
||||
passKeyringPrefix = keyringDirNameFmt
|
||||
keyringFileDirName = "keyring-file"
|
||||
keyringTestDirName = "keyring-test"
|
||||
passKeyringPrefix = "keyring-%s"
|
||||
)
|
||||
|
||||
var _ Keybase = keyringKeybase{}
|
||||
var (
|
||||
_ Keyring = &keystore{}
|
||||
maxPassphraseEntryAttempts = 3
|
||||
)
|
||||
|
||||
// keyringKeybase implements the Keybase interface by using the Keyring library
|
||||
// for account key persistence.
|
||||
type keyringKeybase struct {
|
||||
base baseKeybase
|
||||
db keyring.Keyring
|
||||
// Keyring exposes operations over a backend supported by github.com/99designs/keyring.
|
||||
type Keyring interface {
|
||||
// List all keys.
|
||||
List() ([]Info, error)
|
||||
|
||||
// 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 {
|
||||
return keyringKeybase{
|
||||
db: db,
|
||||
base: newBaseKeybase(opts...),
|
||||
}
|
||||
// SignByAddress sign byte messages with a user key providing the address.
|
||||
SignByAddress(address sdk.Address, msg []byte) ([]byte, tmcrypto.PubKey, error)
|
||||
}
|
||||
|
||||
// NewKeyring creates a new instance of a keyring. Keybase
|
||||
// options can be applied when generating this new Keybase.
|
||||
// Available backends are "os", "file", "kwallet", "pass", "test".
|
||||
func NewKeyring(
|
||||
appName, backend, rootDir string, userInput io.Reader, opts ...KeybaseOption,
|
||||
) (Keybase, error) {
|
||||
// Importer is implemented by key stores that support import of public and private keys.
|
||||
type Importer interface {
|
||||
// ImportPrivKey imports ASCII armored passphrase-encrypted private keys.
|
||||
ImportPrivKey(uid, armor, passphrase string) error
|
||||
// ImportPubKey imports ASCII armored public keys.
|
||||
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 err error
|
||||
|
||||
switch backend {
|
||||
case BackendMemory:
|
||||
return NewInMemory(opts...), nil
|
||||
return NewInMemory(opts...), err
|
||||
case BackendTest:
|
||||
db, err = keyring.Open(lkbToKeyringConfig(appName, rootDir, nil, true))
|
||||
db, err = keyring.Open(newTestBackendKeyringConfig(appName, rootDir))
|
||||
case BackendFile:
|
||||
db, err = keyring.Open(newFileBackendKeyringConfig(appName, rootDir, userInput))
|
||||
case BackendOS:
|
||||
db, err = keyring.Open(lkbToKeyringConfig(appName, rootDir, userInput, false))
|
||||
db, err = keyring.Open(newOSBackendKeyringConfig(appName, rootDir, userInput))
|
||||
case BackendKWallet:
|
||||
db, err = keyring.Open(newKWalletBackendKeyringConfig(appName, rootDir, userInput))
|
||||
case BackendPass:
|
||||
|
@ -83,168 +152,72 @@ func NewKeyring(
|
|||
default:
|
||||
return nil, fmt.Errorf("unknown keyring backend %v", backend)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newKeyringKeybase(db, opts...), nil
|
||||
return newKeystore(db, opts...), nil
|
||||
}
|
||||
|
||||
// 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 ...KeybaseOption) Keybase {
|
||||
return newKeyringKeybase(keyring.NewArrayKeyring(nil), opts...)
|
||||
type keystore struct {
|
||||
db keyring.Keyring
|
||||
options Options
|
||||
}
|
||||
|
||||
// CreateMnemonic generates a new key and persists it to storage, encrypted
|
||||
// using the provided password. It returns the generated mnemonic and the key Info.
|
||||
// An error is returned if it fails to generate a key for the given algo type,
|
||||
// or if another key is already stored under the same name.
|
||||
func (kb keyringKeybase) CreateMnemonic(
|
||||
name string, language Language, passwd string, algo SigningAlgo,
|
||||
) (info Info, mnemonic string, err error) {
|
||||
func newKeystore(kr keyring.Keyring, opts ...Option) keystore {
|
||||
// Default options for keybase
|
||||
options := Options{
|
||||
SupportedAlgos: SigningAlgoList{hd.Secp256k1},
|
||||
SupportedAlgosLedger: SigningAlgoList{hd.Secp256k1},
|
||||
}
|
||||
|
||||
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
|
||||
// with the given password.
|
||||
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()
|
||||
func (ks keystore) ExportPubKeyArmor(uid string) (string, error) {
|
||||
bz, err := ks.Key(uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
if strings.HasSuffix(key, infoSuffix) {
|
||||
rawInfo, err := kb.db.Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if bz == nil {
|
||||
return "", fmt.Errorf("no key to export with name: %s", uid)
|
||||
}
|
||||
|
||||
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 (kb keyringKeybase) Get(name string) (Info, error) {
|
||||
key := infoKey(name)
|
||||
|
||||
bs, err := kb.db.Get(string(key))
|
||||
func (ks keystore) ExportPubKeyArmorByAddress(address sdk.Address) (string, error) {
|
||||
info, err := ks.KeyByAddress(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(bs.Data) == 0 {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, name)
|
||||
}
|
||||
|
||||
return unmarshalInfo(bs.Data)
|
||||
return ks.ExportPubKeyArmor(info.GetName())
|
||||
}
|
||||
|
||||
// GetByAddress fetches a key by address and returns its public information.
|
||||
func (kb keyringKeybase) GetByAddress(address types.AccAddress) (Info, error) {
|
||||
ik, err := kb.db.Get(string(addrHexKey(address)))
|
||||
func (ks keystore) ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error) {
|
||||
priv, err := ks.ExportPrivateKeyObject(uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(ik.Data) == 0 {
|
||||
return nil, fmt.Errorf("key with address %s not found", address)
|
||||
}
|
||||
|
||||
bs, err := kb.db.Get(string(ik.Data))
|
||||
info, err := ks.Key(uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
return unmarshalInfo(bs.Data)
|
||||
}
|
||||
|
||||
// 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
|
||||
return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
|
||||
}
|
||||
|
||||
// ExportPrivateKeyObject exports an armored private key object.
|
||||
func (kb keyringKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcrypto.PrivKey, error) {
|
||||
info, err := kb.Get(name)
|
||||
func (ks keystore) ExportPrivateKeyObject(uid string) (tmcrypto.PrivKey, error) {
|
||||
info, err := ks.Key(uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -270,92 +243,18 @@ func (kb keyringKeybase) ExportPrivateKeyObject(name string, passphrase string)
|
|||
return priv, nil
|
||||
}
|
||||
|
||||
// Export exports armored private key to the caller.
|
||||
func (kb keyringKeybase) Export(name string) (armor string, err error) {
|
||||
bz, err := kb.db.Get(string(infoKey(name)))
|
||||
func (ks keystore) ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error) {
|
||||
byAddress, err := ks.KeyByAddress(address)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if bz.Data == nil {
|
||||
return "", fmt.Errorf("no key to export with name: %s", name)
|
||||
}
|
||||
|
||||
return crypto.ArmorInfoBytes(bz.Data), nil
|
||||
return ks.ExportPrivKeyArmor(byAddress.GetName(), encryptPassphrase)
|
||||
}
|
||||
|
||||
// ExportPubKey returns public keys in ASCII armored format. It retrieves an Info
|
||||
// object by its name and return the public key in a portable format.
|
||||
func (kb keyringKeybase) ExportPubKey(name string) (armor string, err error) {
|
||||
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)
|
||||
func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error {
|
||||
if _, err := ks.Key(uid); err == nil {
|
||||
return fmt.Errorf("cannot overwrite key: %s", uid)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// NOTE: The keyring keystore has no need for a passphrase.
|
||||
kb.writeLocalKey(name, privKey, SigningAlgo(algo))
|
||||
_, err = ks.writeLocalKey(uid, privKey, hd.PubKeyType(algo))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasKey returns whether the key exists in the keyring.
|
||||
func (kb keyringKeybase) HasKey(name string) bool {
|
||||
bz, _ := kb.Get(name)
|
||||
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)
|
||||
}
|
||||
func (ks keystore) ImportPubKey(uid string, armor string) error {
|
||||
if _, err := ks.Key(uid); err == nil {
|
||||
return fmt.Errorf("cannot overwrite key: %s", uid)
|
||||
}
|
||||
|
||||
pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armor)
|
||||
|
@ -397,27 +285,7 @@ func (kb keyringKeybase) ImportPubKey(name string, armor string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(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)))
|
||||
_, err = ks.writeOfflineKey(uid, pubKey, hd.PubKeyType(algo))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -425,59 +293,257 @@ func (kb keyringKeybase) Delete(name, _ string, _ bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SupportedAlgos returns a list of supported signing algorithms.
|
||||
func (kb keyringKeybase) SupportedAlgos() []SigningAlgo {
|
||||
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,
|
||||
})
|
||||
func (ks keystore) Sign(uid string, msg []byte) ([]byte, tmcrypto.PubKey, error) {
|
||||
info, err := ks.Key(uid)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = kb.db.Set(keyring.Item{
|
||||
Key: string(addrHexKey(info.GetAddress())),
|
||||
Data: key,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
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
|
||||
}
|
||||
|
||||
func lkbToKeyringConfig(appName, dir string, buf io.Reader, test bool) keyring.Config {
|
||||
if test {
|
||||
return keyring.Config{
|
||||
AllowedBackends: []keyring.BackendType{keyring.FileBackend},
|
||||
ServiceName: appName,
|
||||
FileDir: filepath.Join(dir, fmt.Sprintf(testKeyringDirNameFmt, appName)),
|
||||
FilePasswordFunc: func(_ string) (string, error) {
|
||||
return "test", nil
|
||||
},
|
||||
func (ks keystore) SignByAddress(address sdk.Address, msg []byte) ([]byte, tmcrypto.PubKey, error) {
|
||||
key, err := ks.KeyByAddress(address)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return ks.Sign(key.GetName(), msg)
|
||||
}
|
||||
|
||||
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{
|
||||
ServiceName: appName,
|
||||
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 {
|
||||
return keyring.Config{
|
||||
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)
|
||||
return keyring.Config{
|
||||
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 {
|
||||
fileDir := filepath.Join(dir, fmt.Sprintf(keyringDirNameFmt, name))
|
||||
fileDir := filepath.Join(dir, keyringFileDirName)
|
||||
return keyring.Config{
|
||||
AllowedBackends: []keyring.BackendType{keyring.FileBackend},
|
||||
ServiceName: name,
|
||||
|
@ -586,6 +663,88 @@ func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func addrHexKey(address types.AccAddress) []byte {
|
||||
return []byte(fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix))
|
||||
func (ks keystore) writeLocalKey(name string, priv tmcrypto.PrivKey, algo hd.PubKeyType) (Info, error) {
|
||||
// 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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
@ -179,10 +180,53 @@ func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string,
|
|||
}
|
||||
|
||||
// Close the underlying storage.
|
||||
func (kb dbKeybase) Close() error {
|
||||
return kb.db.Close()
|
||||
func (kb dbKeybase) Close() error { 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 {
|
||||
return []byte(fmt.Sprintf("%s.%s", name, infoSuffix))
|
||||
type keyringMigrator struct {
|
||||
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
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
|
@ -38,7 +39,21 @@ func TestLegacyKeybase(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotEmpty(t, armor)
|
||||
|
||||
_, err = kb.ExportPrivKey(keys[0].GetName(), "12345678", "12345678")
|
||||
require.Error(t, err)
|
||||
|
||||
armoredInfo, err := kb.Export(keys[0].GetName())
|
||||
require.NoError(t, err)
|
||||
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
|
||||
|
||||
// SigningAlgo defines an algorithm to derive key-pairs which can be used for cryptographic signing.
|
||||
type SigningAlgo string
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
const (
|
||||
// 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")
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
)
|
||||
|
||||
// IsSupportedAlgorithm returns whether the signing algorithm is in the passed-in list of supported algorithms.
|
||||
func IsSupportedAlgorithm(supported []SigningAlgo, algo SigningAlgo) bool {
|
||||
for _, supportedAlgo := range supported {
|
||||
if algo == supportedAlgo {
|
||||
type SignatureAlgo interface {
|
||||
Name() hd.PubKeyType
|
||||
Derive() hd.DeriveFn
|
||||
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 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
|
||||
|
||||
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.
|
||||
// Currently, only english is supported though.
|
||||
|
@ -62,7 +66,7 @@ func (kt KeyType) String() string {
|
|||
|
||||
type (
|
||||
// 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 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/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"
|
||||
)
|
||||
|
||||
|
@ -16,7 +16,7 @@ func Test_writeReadLedgerInfo(t *testing.T) {
|
|||
bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A")
|
||||
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())
|
||||
|
||||
path, err := lInfo.GetPath()
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
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"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
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"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
|
|
@ -3,7 +3,9 @@ package server
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
"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
|
||||
// phrase to recover the private key.
|
||||
func GenerateCoinKey() (sdk.AccAddress, string, error) {
|
||||
|
||||
// generate a private key, with recovery phrase
|
||||
info, secret, err := keyring.NewInMemory().CreateMnemonic(
|
||||
"name", keyring.English, "pass", keyring.Secp256k1)
|
||||
info, secret, err := keyring.NewInMemory().NewMnemonic("name", keyring.English, types.FullFundraiserPath, hd.Secp256k1)
|
||||
if err != nil {
|
||||
return sdk.AccAddress([]byte{}), "", err
|
||||
}
|
||||
addr := info.GetPubKey().Address()
|
||||
return sdk.AccAddress(addr), secret, nil
|
||||
return sdk.AccAddress(info.GetPubKey().Address()), secret, nil
|
||||
}
|
||||
|
||||
// GenerateSaveCoinKey returns the address of a public key, along with the secret
|
||||
// phrase to recover the private key.
|
||||
func GenerateSaveCoinKey(keybase keyring.Keybase, keyName, keyPass string, overwrite bool) (sdk.AccAddress, string, error) {
|
||||
// ensure no overwrite
|
||||
if !overwrite {
|
||||
_, err := keybase.Get(keyName)
|
||||
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
|
||||
if !overwrite && exists {
|
||||
return sdk.AccAddress([]byte{}), "", fmt.Errorf(
|
||||
"key already exists, overwrite is disabled")
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return sdk.AccAddress([]byte{}), "", err
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestGenerateCoinKey(t *testing.T) {
|
||||
|
@ -16,7 +18,7 @@ func TestGenerateCoinKey(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// 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.Equal(t, addr, info.GetAddress())
|
||||
}
|
||||
|
@ -26,19 +28,19 @@ func TestGenerateSaveCoinKey(t *testing.T) {
|
|||
dir, cleanup := tests.NewTestCaseDir(t)
|
||||
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)
|
||||
|
||||
addr, mnemonic, err := server.GenerateSaveCoinKey(kb, "keyname", "012345678", false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test key was actually saved
|
||||
info, err := kb.Get("keyname")
|
||||
info, err := kb.Key("keyname")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, addr, info.GetAddress())
|
||||
|
||||
// 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.Equal(t, addr, info.GetAddress())
|
||||
}
|
||||
|
@ -48,7 +50,7 @@ func TestGenerateSaveCoinKeyOverwriteFlag(t *testing.T) {
|
|||
dir, cleanup := tests.NewTestCaseDir(t)
|
||||
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)
|
||||
|
||||
keyname := "justakey"
|
||||
|
|
|
@ -65,13 +65,13 @@ func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string)
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
multisigInfo, err := kb.Get(args[1])
|
||||
multisigInfo, err := kb.Key(args[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ func CompleteAndBroadcastTxCLI(txBldr authtypes.TxBuilder, cliCtx context.CLICon
|
|||
_, _ = fmt.Fprintf(os.Stderr, "%s\n\n", json)
|
||||
|
||||
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 {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction")
|
||||
return err
|
||||
|
@ -184,7 +184,7 @@ func SignStdTx(
|
|||
|
||||
var signedStdTx authtypes.StdTx
|
||||
|
||||
info, err := txBldr.Keybase().Get(name)
|
||||
info, err := txBldr.Keybase().Key(name)
|
||||
if err != nil {
|
||||
return signedStdTx, err
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
|
||||
func TestParamsEqual(t *testing.T) {
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
// TxBuilder implements a transaction context created in SDK modules.
|
||||
type TxBuilder struct {
|
||||
txEncoder sdk.TxEncoder
|
||||
keybase keyring.Keybase
|
||||
keybase keyring.Keyring
|
||||
accountNumber uint64
|
||||
sequence uint64
|
||||
gas uint64
|
||||
|
@ -53,7 +53,7 @@ func NewTxBuilder(
|
|||
// NewTxBuilderFromCLI returns a new initialized TxBuilder with parameters from
|
||||
// the command line using Viper.
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ func (bldr TxBuilder) Gas() uint64 { return bldr.gas }
|
|||
func (bldr TxBuilder) GasAdjustment() float64 { return bldr.gasAdjustment }
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
func (bldr TxBuilder) WithKeybase(keybase keyring.Keybase) TxBuilder {
|
||||
func (bldr TxBuilder) WithKeybase(keybase keyring.Keyring) TxBuilder {
|
||||
bldr.keybase = keybase
|
||||
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.
|
||||
func MakeSignature(keybase keyring.Keybase, name, passphrase string,
|
||||
func MakeSignature(keybase keyring.Keyring, name, passphrase string,
|
||||
msg StdSignMsg) (sig StdSignature, err error) {
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sigBytes, pubkey, err := keybase.Sign(name, passphrase, msg.Bytes())
|
||||
sigBytes, pubkey, err := keybase.Sign(name, msg.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -93,14 +93,14 @@ func GenTxCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, sm
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to initialize keybase")
|
||||
}
|
||||
|
||||
name := viper.GetString(flags.FlagName)
|
||||
key, err := kb.Get(name)
|
||||
key, err := kb.Key(name)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read from keybase")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue