laconicd/client/keys/add.go

290 lines
7.2 KiB
Go
Raw Normal View History

2021-04-17 10:00:07 +00:00
package keys
import (
"bufio"
"bytes"
"errors"
"fmt"
"sort"
etherminthd "github.com/cerc-io/laconicd/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 (
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"
flagNoSort = "nosort"
flagHDPath = "hd-path"
2021-04-17 10:00:07 +00:00
mnemonicEntropySize = 256
)
/*
2022-10-10 10:38:33 +00:00
RunAddCmd
2021-04-17 10:00:07 +00:00
input
2022-10-10 10:38:33 +00:00
- bip39 mnemonic
- bip39 passphrase
- bip44 path
- local encryption password
2021-04-17 10:00:07 +00:00
output
2022-10-10 10:38:33 +00:00
- armor encrypted private key (saved to file)
2021-04-17 10:00:07 +00:00
*/
func RunAddCmd(ctx client.Context, cmd *cobra.Command, args []string, inBuf *bufio.Reader) error {
2021-04-17 10:00:07 +00:00
var err error
name := args[0]
interactive, _ := cmd.Flags().GetBool(flagInteractive)
noBackup, _ := cmd.Flags().GetBool(flagNoBackup)
showMnemonic := !noBackup
kb := ctx.Keyring
outputFormat := ctx.OutputFormat
2021-04-17 10:00:07 +00:00
keyringAlgos, _ := kb.SupportedAlgorithms()
algoStr, _ := cmd.Flags().GetString(flags.FlagKeyAlgorithm)
algo, err := keyring.NewSigningAlgoFromString(algoStr, keyringAlgos)
if err != nil {
return err
}
if dryRun, _ := cmd.Flags().GetBool(flags.FlagDryRun); dryRun {
// use in memory keybase
kb = keyring.NewInMemory(ctx.Codec, etherminthd.EthSecp256k1Option())
} 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 {
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
}
for i, keyname := range multisigKeys {
2021-04-17 10:00:07 +00:00
k, err := kb.Key(keyname)
if err != nil {
return err
}
2022-10-10 10:38:33 +00:00
key, err := k.GetPubKey()
if err != nil {
2022-05-18 08:08:56 +00:00
return err
}
2022-10-10 10:38:33 +00:00
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-10-10 10:38:33 +00:00
k, err := kb.SaveMultisig(name, pk)
if err != nil {
2021-04-17 10:00:07 +00:00
return err
}
2022-10-10 10:38:33 +00:00
return printCreate(cmd, k, false, "", outputFormat)
2021-04-17 10:00:07 +00:00
}
}
pubKey, _ := cmd.Flags().GetString(keys.FlagPublicKey)
if pubKey != "" {
var pk cryptotypes.PubKey
2022-10-10 10:38:33 +00:00
if err = ctx.Codec.UnmarshalInterfaceJSON([]byte(pubKey), &pk); err != nil {
2021-04-17 10:00:07 +00:00
return err
}
2022-10-10 10:38:33 +00:00
k, err := kb.SaveOfflineKey(name, pk)
if err != nil {
2021-04-17 10:00:07 +00:00
return err
}
2022-10-10 10:38:33 +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)
useLedger, _ := cmd.Flags().GetBool(flags.FlagUseLedger)
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-10-10 10:38:33 +00:00
k, err := kb.SaveLedgerKey(name, hd.Secp256k1, bech32PrefixAccAddr, coinType, account, index)
2021-04-17 10:00:07 +00:00
if err != nil {
return err
}
2022-10-10 10:38:33 +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-10-10 10:38:33 +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-10-10 10:38:33 +00:00
return printCreate(cmd, k, showMnemonic, mnemonic, outputFormat)
2021-04-17 10:00:07 +00:00
}
2022-10-10 10:38:33 +00:00
func printCreate(cmd *cobra.Command, k *keyring.Record, showMnemonic bool, mnemonic, outputFormat string) error {
switch outputFormat {
case OutputFormatText:
2021-04-17 10:00:07 +00:00
cmd.PrintErrln()
2022-10-10 10:38:33 +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-10-10 10:38:33 +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 {
return fmt.Errorf("failed to print mnemonic: %v", err)
}
2021-04-17 10:00:07 +00:00
}
case OutputFormatJSON:
2022-10-10 10:38:33 +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:
return fmt.Errorf("invalid output format %s", outputFormat)
2021-04-17 10:00:07 +00:00
}
return nil
}
2022-10-10 10:38:33 +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
}