github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/cmd/geth/accountcmd.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "encoding/hex" 21 "fmt" 22 "io/ioutil" 23 24 "github.com/ethereum/go-ethereum/accounts" 25 "github.com/ethereum/go-ethereum/accounts/keystore" 26 "github.com/ethereum/go-ethereum/cmd/utils" 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/console" 29 "github.com/ethereum/go-ethereum/crypto" 30 "github.com/ethereum/go-ethereum/log" 31 cli "gopkg.in/urfave/cli.v1" 32 ) 33 34 var ( 35 blsFlag = cli.BoolFlag{ 36 Name: "bls", 37 Usage: "Set to specify generation of proof-of-possession of a BLS key.", 38 } 39 walletCommand = cli.Command{ 40 Name: "wallet", 41 Usage: "Manage Ethereum presale wallets", 42 ArgsUsage: "", 43 Category: "ACCOUNT COMMANDS", 44 Description: ` 45 geth wallet import /path/to/my/presale.wallet 46 47 will prompt for your password and imports your ether presale account. 48 It can be used non-interactively with the --password option taking a 49 passwordfile as argument containing the wallet password in plaintext.`, 50 Subcommands: []cli.Command{ 51 { 52 53 Name: "import", 54 Usage: "Import Ethereum presale wallet", 55 ArgsUsage: "<keyFile>", 56 Action: utils.MigrateFlags(importWallet), 57 Category: "ACCOUNT COMMANDS", 58 Flags: []cli.Flag{ 59 utils.DataDirFlag, 60 utils.KeyStoreDirFlag, 61 utils.PasswordFileFlag, 62 utils.LightKDFFlag, 63 }, 64 Description: ` 65 geth wallet [options] /path/to/my/presale.wallet 66 67 will prompt for your password and imports your ether presale account. 68 It can be used non-interactively with the --password option taking a 69 passwordfile as argument containing the wallet password in plaintext.`, 70 }, 71 }, 72 } 73 74 accountCommand = cli.Command{ 75 Name: "account", 76 Usage: "Manage accounts", 77 Category: "ACCOUNT COMMANDS", 78 Description: ` 79 80 Manage accounts, list all existing accounts, import a private key into a new 81 account, create a new account or update an existing account. 82 83 It supports interactive mode, when you are prompted for password as well as 84 non-interactive mode where passwords are supplied via a given password file. 85 Non-interactive mode is only meant for scripted use on test networks or known 86 safe environments. 87 88 Make sure you remember the password you gave when creating a new account (with 89 either new or import). Without it you are not able to unlock your account. 90 91 Note that exporting your key in unencrypted format is NOT supported. 92 93 Keys are stored under <DATADIR>/keystore. 94 It is safe to transfer the entire directory or the individual keys therein 95 between ethereum nodes by simply copying. 96 97 Make sure you backup your keys regularly.`, 98 Subcommands: []cli.Command{ 99 { 100 Name: "list", 101 Usage: "Print summary of existing accounts", 102 Action: utils.MigrateFlags(accountList), 103 Flags: []cli.Flag{ 104 utils.DataDirFlag, 105 utils.KeyStoreDirFlag, 106 }, 107 Description: ` 108 Print a short summary of all accounts`, 109 }, 110 { 111 Name: "proof-of-possession", 112 Usage: "Generate a proof-of-possession for the given account", 113 Action: utils.MigrateFlags(accountProofOfPossession), 114 ArgsUsage: "<address> <address>", 115 Flags: []cli.Flag{ 116 utils.DataDirFlag, 117 utils.KeyStoreDirFlag, 118 utils.PasswordFileFlag, 119 utils.LightKDFFlag, 120 blsFlag, 121 }, 122 Description: ` 123 Print a proof-of-possession signature for the given account. 124 `, 125 }, 126 { 127 Name: "new", 128 Usage: "Create a new account", 129 Action: utils.MigrateFlags(accountCreate), 130 Flags: []cli.Flag{ 131 utils.DataDirFlag, 132 utils.KeyStoreDirFlag, 133 utils.PasswordFileFlag, 134 utils.LightKDFFlag, 135 }, 136 Description: ` 137 geth account new 138 139 Creates a new account and prints the address. 140 141 The account is saved in encrypted format, you are prompted for a passphrase. 142 143 You must remember this passphrase to unlock your account in the future. 144 145 For non-interactive use the passphrase can be specified with the --password flag: 146 147 Note, this is meant to be used for testing only, it is a bad idea to save your 148 password to file or expose in any other way. 149 `, 150 }, 151 { 152 Name: "update", 153 Usage: "Update an existing account", 154 Action: utils.MigrateFlags(accountUpdate), 155 ArgsUsage: "<address>", 156 Flags: []cli.Flag{ 157 utils.DataDirFlag, 158 utils.KeyStoreDirFlag, 159 utils.LightKDFFlag, 160 }, 161 Description: ` 162 geth account update <address> 163 164 Update an existing account. 165 166 The account is saved in the newest version in encrypted format, you are prompted 167 for a passphrase to unlock the account and another to save the updated file. 168 169 This same command can therefore be used to migrate an account of a deprecated 170 format to the newest format or change the password for an account. 171 172 For non-interactive use the passphrase can be specified with the --password flag: 173 174 geth account update [options] <address> 175 176 Since only one password can be given, only format update can be performed, 177 changing your password is only possible interactively. 178 `, 179 }, 180 { 181 Name: "import", 182 Usage: "Import a private key into a new account", 183 Action: utils.MigrateFlags(accountImport), 184 Flags: []cli.Flag{ 185 utils.DataDirFlag, 186 utils.KeyStoreDirFlag, 187 utils.PasswordFileFlag, 188 utils.LightKDFFlag, 189 }, 190 ArgsUsage: "<keyFile>", 191 Description: ` 192 geth account import <keyfile> 193 194 Imports an unencrypted private key from <keyfile> and creates a new account. 195 Prints the address. 196 197 The keyfile is assumed to contain an unencrypted private key in hexadecimal format. 198 199 The account is saved in encrypted format, you are prompted for a passphrase. 200 201 You must remember this passphrase to unlock your account in the future. 202 203 For non-interactive use the passphrase can be specified with the -password flag: 204 205 geth account import [options] <keyfile> 206 207 Note: 208 As you can directly copy your encrypted accounts to another ethereum instance, 209 this import mechanism is not needed when you transfer an account between 210 nodes. 211 `, 212 }, 213 }, 214 } 215 ) 216 217 func accountList(ctx *cli.Context) error { 218 stack, _ := makeConfigNode(ctx) 219 var index int 220 for _, wallet := range stack.AccountManager().Wallets() { 221 for _, account := range wallet.Accounts() { 222 fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL) 223 index++ 224 } 225 } 226 return nil 227 } 228 229 func accountProofOfPossession(ctx *cli.Context) error { 230 if len(ctx.Args()) != 2 { 231 utils.Fatalf("Please specify the address to prove possession of and the address to sign as proof-of-possession.") 232 } 233 234 stack, _ := makeConfigNode(ctx) 235 ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 236 237 signer := common.HexToAddress(ctx.Args()[0]) 238 message := common.HexToAddress(ctx.Args()[1]) 239 account, _ := unlockAccount(ctx, ks, signer.String(), 0, utils.MakePasswordList(ctx)) 240 var key []byte 241 var pop []byte 242 var err error 243 keyType := "ECDSA" 244 if ctx.IsSet(blsFlag.Name) { 245 keyType = "BLS" 246 key, pop, err = ks.GenerateProofOfPossessionBLS(account, message) 247 } else { 248 key, pop, err = ks.GenerateProofOfPossession(account, message) 249 } 250 if err != nil { 251 return err 252 } 253 254 fmt.Printf("Account {%x}:\n Signature: %s\n %s Public Key: %s\n", account.Address, hex.EncodeToString(pop), keyType, hex.EncodeToString(key)) 255 256 return nil 257 } 258 259 // tries unlocking the specified account a few times. 260 func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) { 261 account, err := utils.MakeAddress(ks, address) 262 if err != nil { 263 utils.Fatalf("Could not list accounts: %v", err) 264 } 265 for trials := 0; trials < 3; trials++ { 266 prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3) 267 password := getPassPhrase(prompt, false, i, passwords) 268 err = ks.Unlock(account, password) 269 if err == nil { 270 log.Info("Unlocked account", "address", account.Address.Hex()) 271 return account, password 272 } 273 if err, ok := err.(*keystore.AmbiguousAddrError); ok { 274 log.Info("Unlocked account", "address", account.Address.Hex()) 275 return ambiguousAddrRecovery(ks, err, password), password 276 } 277 if err != keystore.ErrDecrypt { 278 // No need to prompt again if the error is not decryption-related. 279 break 280 } 281 } 282 // All trials expended to unlock account, bail out 283 utils.Fatalf("Failed to unlock account %s (%v)", address, err) 284 285 return accounts.Account{}, "" 286 } 287 288 // getPassPhrase retrieves the password associated with an account, either fetched 289 // from a list of preloaded passphrases, or requested interactively from the user. 290 func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string { 291 // If a list of passwords was supplied, retrieve from them 292 if len(passwords) > 0 { 293 if i < len(passwords) { 294 return passwords[i] 295 } 296 return passwords[len(passwords)-1] 297 } 298 // Otherwise prompt the user for the password 299 if prompt != "" { 300 fmt.Println(prompt) 301 } 302 password, err := console.Stdin.PromptPassword("Passphrase: ") 303 if err != nil { 304 utils.Fatalf("Failed to read passphrase: %v", err) 305 } 306 if confirmation { 307 confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") 308 if err != nil { 309 utils.Fatalf("Failed to read passphrase confirmation: %v", err) 310 } 311 if password != confirm { 312 utils.Fatalf("Passphrases do not match") 313 } 314 } 315 return password 316 } 317 318 func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account { 319 fmt.Printf("Multiple key files exist for address %x:\n", err.Addr) 320 for _, a := range err.Matches { 321 fmt.Println(" ", a.URL) 322 } 323 fmt.Println("Testing your passphrase against all of them...") 324 var match *accounts.Account 325 for _, a := range err.Matches { 326 if err := ks.Unlock(a, auth); err == nil { 327 match = &a 328 break 329 } 330 } 331 if match == nil { 332 utils.Fatalf("None of the listed files could be unlocked.") 333 } 334 fmt.Printf("Your passphrase unlocked %s\n", match.URL) 335 fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:") 336 for _, a := range err.Matches { 337 if a != *match { 338 fmt.Println(" ", a.URL) 339 } 340 } 341 return *match 342 } 343 344 // accountCreate creates a new account into the keystore defined by the CLI flags. 345 func accountCreate(ctx *cli.Context) error { 346 cfg := gethConfig{Node: defaultNodeConfig()} 347 // Load config file. 348 if file := ctx.GlobalString(configFileFlag.Name); file != "" { 349 if err := loadConfig(file, &cfg); err != nil { 350 utils.Fatalf("%v", err) 351 } 352 } 353 utils.SetNodeConfig(ctx, &cfg.Node) 354 scryptN, scryptP, keydir, err := cfg.Node.AccountConfig() 355 356 if err != nil { 357 utils.Fatalf("Failed to read configuration: %v", err) 358 } 359 360 password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) 361 362 address, err := keystore.StoreKey(keydir, password, scryptN, scryptP) 363 364 if err != nil { 365 utils.Fatalf("Failed to create account: %v", err) 366 } 367 fmt.Printf("Address: {%x}\n", address) 368 return nil 369 } 370 371 // accountUpdate transitions an account from a previous format to the current 372 // one, also providing the possibility to change the pass-phrase. 373 func accountUpdate(ctx *cli.Context) error { 374 if len(ctx.Args()) == 0 { 375 utils.Fatalf("No accounts specified to update") 376 } 377 stack, _ := makeConfigNode(ctx) 378 ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 379 380 for _, addr := range ctx.Args() { 381 account, oldPassword := unlockAccount(ctx, ks, addr, 0, nil) 382 newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil) 383 if err := ks.Update(account, oldPassword, newPassword); err != nil { 384 utils.Fatalf("Could not update the account: %v", err) 385 } 386 } 387 return nil 388 } 389 390 func importWallet(ctx *cli.Context) error { 391 keyfile := ctx.Args().First() 392 if len(keyfile) == 0 { 393 utils.Fatalf("keyfile must be given as argument") 394 } 395 keyJSON, err := ioutil.ReadFile(keyfile) 396 if err != nil { 397 utils.Fatalf("Could not read wallet file: %v", err) 398 } 399 400 stack, _ := makeConfigNode(ctx) 401 passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx)) 402 403 ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 404 acct, err := ks.ImportPreSaleKey(keyJSON, passphrase) 405 if err != nil { 406 utils.Fatalf("%v", err) 407 } 408 fmt.Printf("Address: {%x}\n", acct.Address) 409 return nil 410 } 411 412 func accountImport(ctx *cli.Context) error { 413 keyfile := ctx.Args().First() 414 if len(keyfile) == 0 { 415 utils.Fatalf("keyfile must be given as argument") 416 } 417 key, err := crypto.LoadECDSA(keyfile) 418 if err != nil { 419 utils.Fatalf("Failed to load the private key: %v", err) 420 } 421 stack, _ := makeConfigNode(ctx) 422 passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) 423 424 ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 425 acct, err := ks.ImportECDSA(key, passphrase) 426 if err != nil { 427 utils.Fatalf("Could not create the account: %v", err) 428 } 429 fmt.Printf("Address: {%x}\n", acct.Address) 430 return nil 431 }