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