2018-02-22 07:49:32 -08:00
package keys
import (
2019-06-22 02:24:59 -07:00
"bufio"
2019-01-16 09:30:57 -08:00
"bytes"
2019-06-22 02:24:59 -07:00
"errors"
2018-02-22 07:49:32 -08:00
"fmt"
2019-01-16 09:30:57 -08:00
"sort"
2021-09-20 05:02:15 -07:00
"github.com/cosmos/go-bip39"
2020-07-06 12:50:09 -07:00
"github.com/spf13/cobra"
2019-07-22 08:26:42 -07:00
2020-10-08 10:41:35 -07:00
"github.com/cosmos/cosmos-sdk/client"
2019-05-28 01:44:04 -07:00
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/input"
2020-04-08 02:38:28 -07:00
"github.com/cosmos/cosmos-sdk/crypto/hd"
2020-03-25 08:20:36 -07:00
"github.com/cosmos/cosmos-sdk/crypto/keyring"
2020-09-18 02:40:39 -07:00
"github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
2020-11-09 08:01:43 -08:00
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
2019-02-05 08:22:56 -08:00
sdk "github.com/cosmos/cosmos-sdk/types"
2018-02-22 07:49:32 -08:00
)
const (
2018-11-29 12:59:41 -08:00
flagInteractive = "interactive"
flagRecover = "recover"
flagNoBackup = "no-backup"
2020-04-08 02:38:28 -07:00
flagCoinType = "coin-type"
2018-11-29 12:59:41 -08:00
flagAccount = "account"
flagIndex = "index"
2019-01-16 09:30:57 -08:00
flagMultisig = "multisig"
flagNoSort = "nosort"
2020-01-14 07:40:10 -08:00
flagHDPath = "hd-path"
2019-05-28 01:44:04 -07:00
// DefaultKeyPass contains the default key password for genesis transactions
DefaultKeyPass = "12345678"
2018-02-22 07:49:32 -08:00
)
2019-12-12 13:52:24 -08:00
// AddKeyCommand defines a keys command to add a generated or recovered private key to keybase.
func AddKeyCommand ( ) * cobra . Command {
2018-02-22 07:49:32 -08:00
cmd := & cobra . Command {
Use : "add <name>" ,
2021-03-25 07:53:22 -07:00
Short : "Add an encrypted private key (either newly generated or recovered), encrypt it, and save to <name> file" ,
2018-11-29 12:59:41 -08:00
Long : ` Derive a new private key and encrypt to disk .
Optionally specify a BIP39 mnemonic , a BIP39 passphrase to further secure the mnemonic ,
2019-01-08 08:49:03 -08:00
and a bip32 HD path to derive a specific account . The key will be stored under the given name
2018-11-29 12:59:41 -08:00
and encrypted with the given password . The only input that is required is the encryption password .
If run with - i , it will prompt the user for BIP44 path , BIP39 mnemonic , and passphrase .
The flag -- recover allows one to recover a key from a seed passphrase .
2019-01-16 09:30:57 -08:00
If run with -- dry - run , a key would be generated ( or recovered ) but not stored to the
local keystore .
Use the -- pubkey flag to add arbitrary public keys to the keystore for constructing
multisig transactions .
2021-03-25 07:53:22 -07:00
You can create and store a multisig key by passing the list of key names stored in a keyring
and the minimum number of signatures required through -- multisig - threshold . The keys are
sorted by address , unless the flag -- nosort is set .
Example :
keys add mymultisig -- multisig "keyname1,keyname2,keyname3" -- multisig - threshold 2
2018-11-29 12:59:41 -08:00
` ,
Args : cobra . ExactArgs ( 1 ) ,
2021-03-25 07:53:22 -07:00
RunE : runAddCmdPrepare ,
2018-02-22 07:49:32 -08:00
}
2021-03-25 07:53:22 -07:00
f := cmd . Flags ( )
f . StringSlice ( flagMultisig , nil , "List of key names stored in keyring to construct a public legacy multisig key" )
f . Int ( flagMultiSigThreshold , 1 , "K out of N required signatures. For use in conjunction with --multisig" )
f . Bool ( flagNoSort , false , "Keys passed to --multisig are taken in the order they're supplied" )
f . String ( FlagPublicKey , "" , "Parse a public key in JSON format and saves key info to <name> file." )
f . BoolP ( flagInteractive , "i" , false , "Interactively prompt user for BIP39 passphrase and mnemonic" )
f . Bool ( flags . FlagUseLedger , false , "Store a local reference to a private key on a Ledger device" )
f . Bool ( flagRecover , false , "Provide seed phrase to recover existing key instead of creating" )
f . Bool ( flagNoBackup , false , "Don't print out seed phrase (if others are watching the terminal)" )
f . Bool ( flags . FlagDryRun , false , "Perform action, but don't add key to local keystore" )
f . String ( flagHDPath , "" , "Manual HD Path derivation (overrides BIP44 config)" )
f . Uint32 ( flagCoinType , sdk . GetConfig ( ) . GetCoinType ( ) , "coin type number for HD derivation" )
2021-12-13 02:32:01 -08:00
f . Uint32 ( flagAccount , 0 , "Account number for HD derivation (less than equal 2147483647)" )
f . Uint32 ( flagIndex , 0 , "Address index number for HD derivation (less than equal 2147483647)" )
2021-03-25 07:53:22 -07:00
f . String ( flags . FlagKeyAlgorithm , string ( hd . Secp256k1Type ) , "Key signing algorithm to generate keys for" )
2020-04-29 19:36:34 -07:00
2018-02-22 07:49:32 -08:00
return cmd
}
2021-03-25 07:53:22 -07:00
func runAddCmdPrepare ( cmd * cobra . Command , args [ ] string ) error {
2020-12-14 14:09:51 -08:00
clientCtx , err := client . GetClientQueryContext ( cmd )
if err != nil {
return err
}
2019-12-12 13:52:24 -08:00
2021-08-09 03:35:01 -07:00
buf := bufio . NewReader ( clientCtx . Input )
2021-06-25 04:55:58 -07:00
return runAddCmd ( clientCtx , cmd , args , buf )
2019-12-12 13:52:24 -08:00
}
2018-11-29 12:59:41 -08:00
/ *
input
- bip39 mnemonic
- bip39 passphrase
- bip44 path
- local encryption password
output
- armor encrypted private key ( saved to file )
* /
2021-06-25 04:55:58 -07:00
func runAddCmd ( ctx client . Context , cmd * cobra . Command , args [ ] string , inBuf * bufio . Reader ) error {
2018-02-22 07:49:32 -08:00
var err error
2018-11-29 12:59:41 -08:00
name := args [ 0 ]
2020-07-06 12:50:09 -07:00
interactive , _ := cmd . Flags ( ) . GetBool ( flagInteractive )
noBackup , _ := cmd . Flags ( ) . GetBool ( flagNoBackup )
showMnemonic := ! noBackup
2021-03-25 07:53:22 -07:00
kb := ctx . Keyring
2021-05-19 01:44:15 -07:00
outputFormat := ctx . OutputFormat
2019-01-08 08:49:03 -08:00
2020-06-11 07:30:42 -07:00
keyringAlgos , _ := kb . SupportedAlgorithms ( )
2020-08-12 01:34:10 -07:00
algoStr , _ := cmd . Flags ( ) . GetString ( flags . FlagKeyAlgorithm )
2020-07-06 12:50:09 -07:00
algo , err := keyring . NewSigningAlgoFromString ( algoStr , keyringAlgos )
2020-04-08 02:38:28 -07:00
if err != nil {
2020-06-11 07:30:42 -07:00
return err
2020-01-14 07:40:10 -08:00
}
2021-06-25 04:55:58 -07:00
if dryRun , _ := cmd . Flags ( ) . GetBool ( flags . FlagDryRun ) ; dryRun {
// use in memory keybase
2021-09-20 05:02:15 -07:00
kb = keyring . NewInMemory ( ctx . Codec )
2021-06-25 04:55:58 -07:00
} else {
2020-04-08 02:38:28 -07:00
_ , err = kb . Key ( name )
2018-03-24 23:52:51 -07:00
if err == nil {
// account exists, ask for user confirmation
2020-04-08 02:38:28 -07:00
response , err2 := input . GetConfirmation ( fmt . Sprintf ( "override the existing name %s" , name ) , inBuf , cmd . ErrOrStderr ( ) )
2019-06-18 02:46:51 -07:00
if err2 != nil {
2019-02-05 08:22:56 -08:00
return err2
2018-03-24 23:52:51 -07:00
}
2020-04-29 19:36:34 -07:00
2019-06-18 02:46:51 -07:00
if ! response {
return errors . New ( "aborted" )
}
2020-04-08 02:38:28 -07:00
err2 = kb . Delete ( name )
if err2 != nil {
return err2
}
2018-03-24 23:52:51 -07:00
}
2020-07-06 12:50:09 -07:00
multisigKeys , _ := cmd . Flags ( ) . GetStringSlice ( flagMultisig )
2019-01-16 09:30:57 -08:00
if len ( multisigKeys ) != 0 {
2021-06-25 04:55:58 -07:00
pks := make ( [ ] cryptotypes . PubKey , len ( multisigKeys ) )
2020-07-06 12:50:09 -07:00
multisigThreshold , _ := cmd . Flags ( ) . GetInt ( flagMultiSigThreshold )
2019-01-16 09:30:57 -08:00
if err := validateMultisigThreshold ( multisigThreshold , len ( multisigKeys ) ) ; err != nil {
return err
}
2021-06-25 04:55:58 -07:00
for i , keyname := range multisigKeys {
2020-04-08 02:38:28 -07:00
k , err := kb . Key ( keyname )
2019-01-16 09:30:57 -08:00
if err != nil {
return err
}
2020-04-29 19:36:34 -07:00
2021-09-20 05:02:15 -07:00
key , err := k . GetPubKey ( )
if err != nil {
return err
}
pks [ i ] = key
2019-01-16 09:30:57 -08:00
}
2020-07-06 12:50:09 -07:00
if noSort , _ := cmd . Flags ( ) . GetBool ( flagNoSort ) ; ! noSort {
2019-01-16 09:30:57 -08:00
sort . Slice ( pks , func ( i , j int ) bool {
return bytes . Compare ( pks [ i ] . Address ( ) , pks [ j ] . Address ( ) ) < 0
} )
}
2020-09-18 02:40:39 -07:00
pk := multisig . NewLegacyAminoPubKey ( multisigThreshold , pks )
2021-09-20 05:02:15 -07:00
k , err := kb . SaveMultisig ( name , pk )
2021-06-25 04:55:58 -07:00
if err != nil {
2019-01-16 09:30:57 -08:00
return err
}
2019-02-05 08:22:56 -08:00
2021-09-20 05:02:15 -07:00
return printCreate ( cmd , k , false , "" , outputFormat )
2019-01-16 09:30:57 -08:00
}
2018-02-22 07:49:32 -08:00
}
2020-07-06 12:50:09 -07:00
pubKey , _ := cmd . Flags ( ) . GetString ( FlagPublicKey )
if pubKey != "" {
2021-03-25 07:53:22 -07:00
var pk cryptotypes . PubKey
2021-09-20 05:02:15 -07:00
if err = ctx . Codec . UnmarshalInterfaceJSON ( [ ] byte ( pubKey ) , & pk ) ; err != nil {
2019-01-08 08:49:03 -08:00
return err
}
2021-06-25 04:55:58 -07:00
2021-09-20 05:02:15 -07:00
k , err := kb . SaveOfflineKey ( name , pk )
2021-06-25 04:55:58 -07:00
if err != nil {
return err
}
2021-09-20 05:02:15 -07:00
return printCreate ( cmd , k , false , "" , outputFormat )
2019-01-08 08:49:03 -08:00
}
2018-11-29 12:59:41 -08:00
2020-07-06 12:50:09 -07:00
coinType , _ := cmd . Flags ( ) . GetUint32 ( flagCoinType )
account , _ := cmd . Flags ( ) . GetUint32 ( flagAccount )
index , _ := cmd . Flags ( ) . GetUint32 ( flagIndex )
hdPath , _ := cmd . Flags ( ) . GetString ( flagHDPath )
useLedger , _ := cmd . Flags ( ) . GetBool ( flags . FlagUseLedger )
2018-11-29 12:59:41 -08:00
2020-04-14 08:24:27 -07:00
if len ( hdPath ) == 0 {
2020-04-08 02:38:28 -07:00
hdPath = hd . CreateHDPath ( coinType , account , index ) . String ( )
2020-07-06 12:50:09 -07:00
} else if useLedger {
2020-04-14 08:24:27 -07:00
return errors . New ( "cannot set custom bip32 path with ledger" )
2020-01-14 07:40:10 -08:00
}
2019-04-30 01:58:21 -07:00
// If we're using ledger, only thing we need is the path and the bech32 prefix.
2020-07-06 12:50:09 -07:00
if useLedger {
2019-04-30 01:58:21 -07:00
bech32PrefixAccAddr := sdk . GetConfig ( ) . GetBech32AccountAddrPrefix ( )
2021-09-20 05:02:15 -07:00
k , err := kb . SaveLedgerKey ( name , hd . Secp256k1 , bech32PrefixAccAddr , coinType , account , index )
2018-06-28 17:54:47 -07:00
if err != nil {
return err
}
2018-11-29 12:59:41 -08:00
2021-09-20 05:02:15 -07:00
return printCreate ( cmd , k , false , "" , outputFormat )
2018-11-29 12:59:41 -08:00
}
2019-02-05 08:22:56 -08:00
// Get bip39 mnemonic
2020-04-29 19:36:34 -07:00
var mnemonic , bip39Passphrase string
2019-02-05 08:22:56 -08:00
2020-07-06 12:50:09 -07:00
recover , _ := cmd . Flags ( ) . GetBool ( flagRecover )
2020-08-27 06:36:19 -07:00
if recover {
mnemonic , err = input . GetString ( "Enter your bip39 mnemonic" , inBuf )
if err != nil {
return err
2018-02-22 07:49:32 -08:00
}
2018-11-29 12:59:41 -08:00
2020-08-27 06:36:19 -07:00
if ! bip39 . IsMnemonicValid ( mnemonic ) {
return errors . New ( "invalid mnemonic" )
}
} else if interactive {
mnemonic , err = input . GetString ( "Enter your bip39 mnemonic, or hit enter to generate one." , inBuf )
2018-11-29 12:59:41 -08:00
if err != nil {
return err
}
2019-04-29 09:49:35 -07:00
2020-08-27 06:36:19 -07:00
if ! bip39 . IsMnemonicValid ( mnemonic ) && mnemonic != "" {
2019-04-29 09:49:35 -07:00
return errors . New ( "invalid mnemonic" )
}
2018-11-29 12:59:41 -08:00
}
if len ( mnemonic ) == 0 {
2020-11-09 08:01:43 -08:00
// read entropy seed straight from tmcrypto.Rand and convert to mnemonic
2018-11-29 12:59:41 -08:00
entropySeed , err := bip39 . NewEntropy ( mnemonicEntropySize )
if err != nil {
return err
}
2019-08-19 09:06:27 -07:00
mnemonic , err = bip39 . NewMnemonic ( entropySeed )
2018-02-22 07:49:32 -08:00
if err != nil {
return err
}
}
2018-11-29 12:59:41 -08:00
2019-02-05 08:22:56 -08:00
// override bip39 passphrase
2018-11-29 12:59:41 -08:00
if interactive {
2019-05-28 01:44:04 -07:00
bip39Passphrase , err = input . GetString (
2018-11-29 12:59:41 -08:00
"Enter your bip39 passphrase. This is combined with the mnemonic to derive the seed. " +
2019-06-22 02:24:59 -07:00
"Most users should just hit enter to use the default, \"\"" , inBuf )
2018-11-29 12:59:41 -08:00
if err != nil {
return err
}
// if they use one, make them re-enter it
if len ( bip39Passphrase ) != 0 {
2019-06-22 02:24:59 -07:00
p2 , err := input . GetString ( "Repeat the passphrase:" , inBuf )
2018-11-29 12:59:41 -08:00
if err != nil {
return err
}
if bip39Passphrase != p2 {
return errors . New ( "passphrases don't match" )
}
}
}
2021-09-20 05:02:15 -07:00
k , err := kb . NewAccount ( name , mnemonic , bip39Passphrase , hdPath , algo )
2018-11-29 12:59:41 -08:00
if err != nil {
return err
}
2019-02-05 08:22:56 -08:00
// Recover key from seed passphrase
2020-07-06 12:50:09 -07:00
if recover {
2019-02-05 08:22:56 -08:00
// Hide mnemonic from output
showMnemonic = false
mnemonic = ""
2018-11-29 12:59:41 -08:00
}
2021-09-20 05:02:15 -07:00
return printCreate ( cmd , k , showMnemonic , mnemonic , outputFormat )
2018-11-29 12:59:41 -08:00
}
2021-09-20 05:02:15 -07:00
func printCreate ( cmd * cobra . Command , k * keyring . Record , showMnemonic bool , mnemonic , outputFormat string ) error {
2021-05-19 01:44:15 -07:00
switch outputFormat {
2019-02-08 12:45:23 -08:00
case OutputFormatText :
2019-06-22 02:24:59 -07:00
cmd . PrintErrln ( )
2022-04-12 23:46:51 -07:00
if err := printKeyringRecord ( cmd . OutOrStdout ( ) , k , keyring . MkAccKeyOutput , outputFormat ) ; err != nil {
return err
}
2018-08-30 21:06:44 -07:00
2019-02-05 08:22:56 -08:00
// print mnemonic unless requested not to.
if showMnemonic {
2022-04-15 06:30:20 -07:00
if _ , err := fmt . Fprintln ( cmd . ErrOrStderr ( ) , fmt . Sprintf ( "\n**Important** write this mnemonic phrase in a safe place.\nIt is the only way to recover your account if you ever forget your password.\n\n%s\n" , mnemonic ) ) ; err != nil {
2022-04-12 23:46:51 -07:00
return fmt . Errorf ( "failed to print mnemonic: %v" , err )
}
2018-02-22 07:49:32 -08:00
}
2019-02-08 12:45:23 -08:00
case OutputFormatJSON :
2021-09-20 05:02:15 -07:00
out , err := keyring . MkAccKeyOutput ( k )
2018-06-05 21:53:04 -07:00
if err != nil {
2019-02-05 08:22:56 -08:00
return err
2018-06-05 21:53:04 -07:00
}
2019-02-05 08:22:56 -08:00
if showMnemonic {
out . Mnemonic = mnemonic
2018-02-22 07:49:32 -08:00
}
2019-02-05 08:22:56 -08:00
2020-06-30 13:59:21 -07:00
jsonString , err := KeysCdc . MarshalJSON ( out )
2018-02-22 07:49:32 -08:00
if err != nil {
2019-02-05 08:22:56 -08:00
return err
2018-02-22 07:49:32 -08:00
}
2020-04-29 19:36:34 -07:00
2020-07-05 04:22:49 -07:00
cmd . Println ( string ( jsonString ) )
2020-06-30 13:59:21 -07:00
2018-02-22 08:20:25 -08:00
default :
2021-05-19 01:44:15 -07:00
return fmt . Errorf ( "invalid output format %s" , outputFormat )
2018-02-22 07:49:32 -08:00
}
2019-02-05 08:22:56 -08:00
return nil
2018-02-22 07:49:32 -08:00
}