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