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