2022-12-16 09:48:38 +00:00
// Copyright 2021 Evmos Foundation
// This file is part of Evmos' Ethermint library.
//
// The Ethermint library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The Ethermint library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE
2021-04-17 10:00:07 +00:00
package keys
import (
"bufio"
"bytes"
"errors"
"fmt"
"sort"
2022-06-19 09:43:41 +00:00
etherminthd "github.com/evmos/ethermint/crypto/hd"
2021-04-17 10:00:07 +00:00
bip39 "github.com/cosmos/go-bip39"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/input"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
2021-06-29 17:02:21 +00:00
flagInteractive = "interactive"
flagRecover = "recover"
flagNoBackup = "no-backup"
flagCoinType = "coin-type"
flagAccount = "account"
flagIndex = "index"
flagMultisig = "multisig"
2021-04-17 10:00:07 +00:00
flagMultiSigThreshold = "multisig-threshold"
2021-06-29 17:02:21 +00:00
flagNoSort = "nosort"
flagHDPath = "hd-path"
2021-04-17 10:00:07 +00:00
mnemonicEntropySize = 256
)
2022-08-08 08:17:10 +00:00
/ *
RunAddCmd
2021-04-17 10:00:07 +00:00
input
2022-08-08 08:17:10 +00:00
- bip39 mnemonic
- bip39 passphrase
- bip44 path
- local encryption password
2021-04-17 10:00:07 +00:00
output
2022-08-08 08:17:10 +00:00
- armor encrypted private key ( saved to file )
2021-04-17 10:00:07 +00:00
* /
2021-06-29 17:02:21 +00:00
func RunAddCmd ( ctx client . Context , cmd * cobra . Command , args [ ] string , inBuf * bufio . Reader ) error {
2022-12-22 09:11:19 +00:00
var (
algo keyring . SignatureAlgo
err error
)
2021-04-17 10:00:07 +00:00
name := args [ 0 ]
2022-12-22 09:11:19 +00:00
2021-04-17 10:00:07 +00:00
interactive , _ := cmd . Flags ( ) . GetBool ( flagInteractive )
noBackup , _ := cmd . Flags ( ) . GetBool ( flagNoBackup )
2022-12-22 09:11:19 +00:00
useLedger , _ := cmd . Flags ( ) . GetBool ( flags . FlagUseLedger )
algoStr , _ := cmd . Flags ( ) . GetString ( flags . FlagKeyAlgorithm )
2021-04-17 10:00:07 +00:00
showMnemonic := ! noBackup
2021-06-29 17:02:21 +00:00
kb := ctx . Keyring
outputFormat := ctx . OutputFormat
2021-04-17 10:00:07 +00:00
2022-12-22 09:11:19 +00:00
keyringAlgos , ledgerAlgos := kb . SupportedAlgorithms ( )
// check if the provided signing algorithm is supported by the keyring or
// ledger
if useLedger {
algo , err = keyring . NewSigningAlgoFromString ( algoStr , ledgerAlgos )
} else {
algo , err = keyring . NewSigningAlgoFromString ( algoStr , keyringAlgos )
}
2021-04-17 10:00:07 +00:00
if err != nil {
return err
}
2021-06-29 17:02:21 +00:00
if dryRun , _ := cmd . Flags ( ) . GetBool ( flags . FlagDryRun ) ; dryRun {
// use in memory keybase
2022-07-28 13:43:49 +00:00
kb = keyring . NewInMemory ( ctx . Codec , etherminthd . EthSecp256k1Option ( ) )
2021-06-29 17:02:21 +00:00
} else {
2021-04-17 10:00:07 +00:00
_ , 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 , cmd . ErrOrStderr ( ) )
if err2 != nil {
return err2
}
if ! response {
return errors . New ( "aborted" )
}
err2 = kb . Delete ( name )
if err2 != nil {
return err2
}
}
multisigKeys , _ := cmd . Flags ( ) . GetStringSlice ( flagMultisig )
if len ( multisigKeys ) != 0 {
2021-06-29 17:02:21 +00:00
pks := make ( [ ] cryptotypes . PubKey , len ( multisigKeys ) )
2021-04-17 10:00:07 +00:00
multisigThreshold , _ := cmd . Flags ( ) . GetInt ( flagMultiSigThreshold )
if err := validateMultisigThreshold ( multisigThreshold , len ( multisigKeys ) ) ; err != nil {
return err
}
2021-06-29 17:02:21 +00:00
for i , keyname := range multisigKeys {
2021-04-17 10:00:07 +00:00
k , err := kb . Key ( keyname )
if err != nil {
return err
}
2022-07-28 13:43:49 +00:00
key , err := k . GetPubKey ( )
if err != nil {
return err
}
pks [ i ] = key
2021-04-17 10:00:07 +00:00
}
if noSort , _ := cmd . Flags ( ) . GetBool ( flagNoSort ) ; ! noSort {
sort . Slice ( pks , func ( i , j int ) bool {
return bytes . Compare ( pks [ i ] . Address ( ) , pks [ j ] . Address ( ) ) < 0
} )
}
pk := multisig . NewLegacyAminoPubKey ( multisigThreshold , pks )
2022-07-28 13:43:49 +00:00
k , err := kb . SaveMultisig ( name , pk )
2021-06-29 17:02:21 +00:00
if err != nil {
2021-04-17 10:00:07 +00:00
return err
}
2022-07-28 13:43:49 +00:00
return printCreate ( cmd , k , false , "" , outputFormat )
2021-04-17 10:00:07 +00:00
}
}
pubKey , _ := cmd . Flags ( ) . GetString ( keys . FlagPublicKey )
if pubKey != "" {
2021-06-29 17:02:21 +00:00
var pk cryptotypes . PubKey
2022-07-28 13:43:49 +00:00
if err = ctx . Codec . UnmarshalInterfaceJSON ( [ ] byte ( pubKey ) , & pk ) ; err != nil {
2021-04-17 10:00:07 +00:00
return err
}
2022-07-28 13:43:49 +00:00
k , err := kb . SaveOfflineKey ( name , pk )
2021-06-29 17:02:21 +00:00
if err != nil {
2021-04-17 10:00:07 +00:00
return err
}
2022-07-28 13:43:49 +00:00
return printCreate ( cmd , k , false , "" , outputFormat )
2021-04-17 10:00:07 +00:00
}
coinType , _ := cmd . Flags ( ) . GetUint32 ( flagCoinType )
account , _ := cmd . Flags ( ) . GetUint32 ( flagAccount )
index , _ := cmd . Flags ( ) . GetUint32 ( flagIndex )
hdPath , _ := cmd . Flags ( ) . GetString ( flagHDPath )
if len ( hdPath ) == 0 {
hdPath = hd . CreateHDPath ( coinType , account , index ) . String ( )
} else if useLedger {
return errors . New ( "cannot set custom bip32 path with ledger" )
}
// If we're using ledger, only thing we need is the path and the bech32 prefix.
if useLedger {
bech32PrefixAccAddr := sdk . GetConfig ( ) . GetBech32AccountAddrPrefix ( )
2022-12-22 09:11:19 +00:00
// use the provided algo to save the ledger key
k , err := kb . SaveLedgerKey ( name , algo , bech32PrefixAccAddr , coinType , account , index )
2021-04-17 10:00:07 +00:00
if err != nil {
return err
}
2022-07-28 13:43:49 +00:00
return printCreate ( cmd , k , false , "" , outputFormat )
2021-04-17 10:00:07 +00:00
}
// Get bip39 mnemonic
var mnemonic , bip39Passphrase string
recover , _ := cmd . Flags ( ) . GetBool ( flagRecover )
if recover {
mnemonic , err = input . GetString ( "Enter your bip39 mnemonic" , inBuf )
if err != nil {
return err
}
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 )
if err != nil {
return err
}
if ! bip39 . IsMnemonicValid ( mnemonic ) && mnemonic != "" {
return errors . New ( "invalid mnemonic" )
}
}
if len ( mnemonic ) == 0 {
// read entropy seed straight from tmcrypto.Rand and convert to mnemonic
entropySeed , err := bip39 . NewEntropy ( mnemonicEntropySize )
if err != nil {
return err
}
mnemonic , err = bip39 . NewMnemonic ( entropySeed )
if err != nil {
return err
}
}
// override bip39 passphrase
if interactive {
bip39Passphrase , err = input . GetString (
"Enter your bip39 passphrase. This is combined with the mnemonic to derive the seed. " +
"Most users should just hit enter to use the default, \"\"" , inBuf )
if err != nil {
return err
}
// if they use one, make them re-enter it
if len ( bip39Passphrase ) != 0 {
p2 , err := input . GetString ( "Repeat the passphrase:" , inBuf )
if err != nil {
return err
}
if bip39Passphrase != p2 {
return errors . New ( "passphrases don't match" )
}
}
}
2022-07-28 13:43:49 +00:00
k , err := kb . NewAccount ( name , mnemonic , bip39Passphrase , hdPath , algo )
2021-04-17 10:00:07 +00:00
if err != nil {
return err
}
// Recover key from seed passphrase
if recover {
// Hide mnemonic from output
showMnemonic = false
mnemonic = ""
}
2022-07-28 13:43:49 +00:00
return printCreate ( cmd , k , showMnemonic , mnemonic , outputFormat )
2021-04-17 10:00:07 +00:00
}
2022-07-28 13:43:49 +00:00
func printCreate ( cmd * cobra . Command , k * keyring . Record , showMnemonic bool , mnemonic , outputFormat string ) error {
2021-06-29 17:02:21 +00:00
switch outputFormat {
case OutputFormatText :
2021-04-17 10:00:07 +00:00
cmd . PrintErrln ( )
2022-07-28 13:43:49 +00:00
if err := printKeyringRecord ( cmd . OutOrStdout ( ) , k , keyring . MkAccKeyOutput , outputFormat ) ; err != nil {
return err
}
2021-04-17 10:00:07 +00:00
// print mnemonic unless requested not to.
if showMnemonic {
2022-08-26 10:30:55 +00:00
if _ , err := fmt . Fprintf ( cmd . ErrOrStderr ( ) ,
"\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\n" , //nolint:lll
mnemonic ) ; err != nil {
2022-07-28 13:43:49 +00:00
return fmt . Errorf ( "failed to print mnemonic: %v" , err )
}
2021-04-17 10:00:07 +00:00
}
2021-06-29 17:02:21 +00:00
case OutputFormatJSON :
2022-07-28 13:43:49 +00:00
out , err := keyring . MkAccKeyOutput ( k )
2021-04-17 10:00:07 +00:00
if err != nil {
return err
}
if showMnemonic {
out . Mnemonic = mnemonic
}
jsonString , err := keys . KeysCdc . MarshalJSON ( out )
if err != nil {
return err
}
cmd . Println ( string ( jsonString ) )
default :
2021-06-29 17:02:21 +00:00
return fmt . Errorf ( "invalid output format %s" , outputFormat )
2021-04-17 10:00:07 +00:00
}
return nil
}
2022-07-28 13:43:49 +00:00
func validateMultisigThreshold ( k , nKeys int ) error {
if k <= 0 {
return fmt . Errorf ( "threshold must be a positive integer" )
}
if nKeys < k {
return fmt . Errorf (
"threshold k of n multisignature: %d < %d" , nKeys , k )
}
return nil
}