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