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