forked from evmos/ethermint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
add.go
331 lines (278 loc) · 9.76 KB
/
add.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
package keys
import (
"bufio"
"bytes"
"errors"
"fmt"
"sort"
etherminthd "github.com/evoblockchain/ethermint/crypto/hd"
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"
flagMultiSigThreshold = "multisig-threshold"
flagNoSort = "nosort"
flagHDPath = "hd-path"
mnemonicEntropySize = 256
)
// AddKeyCommand defines a keys command to add a generated or recovered private key to keybase.
func AddKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "add <name>",
Short: "Add an encrypted private key (either newly generated or recovered), encrypt it, and save to <name> file",
Long: `Derive a new private key and encrypt to disk.
Optionally specify a BIP39 mnemonic, a BIP39 passphrase to further secure the mnemonic,
and a bip32 HD path to derive a specific account. The key will be stored under the given name
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.
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.
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
`,
Args: cobra.ExactArgs(1),
RunE: runAddCmdPrepare,
}
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(keys.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")
f.Uint32(flagAccount, 0, "Account number for HD derivation")
f.Uint32(flagIndex, 0, "Address index number for HD derivation")
f.String(flags.FlagKeyAlgorithm, string(etherminthd.EthSecp256k1Type), "Key signing algorithm to generate keys for")
return cmd
}
func runAddCmdPrepare(cmd *cobra.Command, args []string) error {
buf := bufio.NewReader(cmd.InOrStdin())
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
return RunAddCmd(clientCtx, cmd, args, buf)
}
/*
=======
/*
RunAddCmd
input
- bip39 mnemonic
- bip39 passphrase
- bip44 path
- local encryption password
output
- armor encrypted private key (saved to file)
*/
func RunAddCmd(ctx client.Context, cmd *cobra.Command, args []string, inBuf *bufio.Reader) error {
var err error
name := args[0]
interactive, _ := cmd.Flags().GetBool(flagInteractive)
noBackup, _ := cmd.Flags().GetBool(flagNoBackup)
showMnemonic := !noBackup
kb := ctx.Keyring
outputFormat := ctx.OutputFormat
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(etherminthd.EthSecp256k1Option())
} else {
_, 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))
multisigThreshold, _ := cmd.Flags().GetInt(flagMultiSigThreshold)
if err := validateMultisigThreshold(multisigThreshold, len(multisigKeys)); err != nil {
return err
}
for i, keyname := range multisigKeys {
k, err := kb.Key(keyname)
if err != nil {
return err
}
pks[i] = k.GetPubKey()
}
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)
info, err := kb.SaveMultisig(name, pk)
if err != nil {
return err
}
return printCreate(cmd, info, false, "", outputFormat)
}
}
pubKey, _ := cmd.Flags().GetString(keys.FlagPublicKey)
if pubKey != "" {
var pk cryptotypes.PubKey
err = ctx.Codec.UnmarshalInterfaceJSON([]byte(pubKey), &pk)
if err != nil {
return err
}
info, err := kb.SavePubKey(name, pk, algo.Name())
if err != nil {
return err
}
return printCreate(cmd, info, false, "", outputFormat)
}
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()
info, err := kb.SaveLedgerKey(name, algo, bech32PrefixAccAddr, coinType, account, index)
if err != nil {
return err
}
return printCreate(cmd, info, false, "", outputFormat)
}
// 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")
}
}
}
info, err := kb.NewAccount(name, mnemonic, bip39Passphrase, hdPath, algo)
if err != nil {
return err
}
// Recover key from seed passphrase
if recover {
// Hide mnemonic from output
showMnemonic = false
mnemonic = ""
}
return printCreate(cmd, info, showMnemonic, mnemonic, outputFormat)
}
func printCreate(cmd *cobra.Command, info keyring.Info, showMnemonic bool, mnemonic, outputFormat string) error {
switch outputFormat {
case OutputFormatText:
cmd.PrintErrln()
printKeyInfo(cmd.OutOrStdout(), info, keyring.MkAccKeyOutput, outputFormat)
// print mnemonic unless requested not to.
if showMnemonic {
fmt.Fprintln(cmd.ErrOrStderr(), "\n**Important** write this mnemonic phrase in a safe place.")
fmt.Fprintln(cmd.ErrOrStderr(), "It is the only way to recover your account if you ever forget your password.")
fmt.Fprintln(cmd.ErrOrStderr(), "")
fmt.Fprintln(cmd.ErrOrStderr(), mnemonic)
}
case OutputFormatJSON:
out, err := keyring.MkAccKeyOutput(info)
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)
}
return nil
}