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