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