github.com/ethxdao/go-ethereum@v0.0.0-20221218102228-5ae34a9cc189/cmd/geth/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  	"os"
    22  
    23  	"github.com/ethxdao/go-ethereum/accounts"
    24  	"github.com/ethxdao/go-ethereum/accounts/keystore"
    25  	"github.com/ethxdao/go-ethereum/cmd/utils"
    26  	"github.com/ethxdao/go-ethereum/crypto"
    27  	"github.com/ethxdao/go-ethereum/log"
    28  )
    29  
    30  var (
    31  	walletCommand = &cli.Command{
    32  		Name:      "wallet",
    33  		Usage:     "Manage Ethereum presale wallets",
    34  		ArgsUsage: "",
    35  		Description: `
    36      geth wallet import /path/to/my/presale.wallet
    37  
    38  will prompt for your password and imports your ether presale account.
    39  It can be used non-interactively with the --password option taking a
    40  passwordfile as argument containing the wallet password in plaintext.`,
    41  		Subcommands: []*cli.Command{
    42  			{
    43  
    44  				Name:      "import",
    45  				Usage:     "Import Ethereum presale wallet",
    46  				ArgsUsage: "<keyFile>",
    47  				Action:    importWallet,
    48  				Flags: []cli.Flag{
    49  					utils.DataDirFlag,
    50  					utils.KeyStoreDirFlag,
    51  					utils.PasswordFileFlag,
    52  					utils.LightKDFFlag,
    53  				},
    54  				Description: `
    55  	geth wallet [options] /path/to/my/presale.wallet
    56  
    57  will prompt for your password and imports your ether presale account.
    58  It can be used non-interactively with the --password option taking a
    59  passwordfile as argument containing the wallet password in plaintext.`,
    60  			},
    61  		},
    62  	}
    63  
    64  	accountCommand = &cli.Command{
    65  		Name:  "account",
    66  		Usage: "Manage accounts",
    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 ethereum 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: 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: 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 password.
   115  
   116  You must remember this password to unlock your account in the future.
   117  
   118  For non-interactive use the password 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:    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 password 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 password 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: 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 password.
   173  
   174  You must remember this password to unlock your account in the future.
   175  
   176  For non-interactive use the password 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 ethereum 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(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 := utils.GetPassPhraseWithList(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  func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
   232  	fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
   233  	for _, a := range err.Matches {
   234  		fmt.Println("  ", a.URL)
   235  	}
   236  	fmt.Println("Testing your password against all of them...")
   237  	var match *accounts.Account
   238  	for i, a := range err.Matches {
   239  		if e := ks.Unlock(a, auth); e == nil {
   240  			match = &err.Matches[i]
   241  			break
   242  		}
   243  	}
   244  	if match == nil {
   245  		utils.Fatalf("None of the listed files could be unlocked.")
   246  		return accounts.Account{}
   247  	}
   248  	fmt.Printf("Your password unlocked %s\n", match.URL)
   249  	fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
   250  	for _, a := range err.Matches {
   251  		if a != *match {
   252  			fmt.Println("  ", a.URL)
   253  		}
   254  	}
   255  	return *match
   256  }
   257  
   258  // accountCreate creates a new account into the keystore defined by the CLI flags.
   259  func accountCreate(ctx *cli.Context) error {
   260  	cfg := gethConfig{Node: defaultNodeConfig()}
   261  	// Load config file.
   262  	if file := ctx.String(configFileFlag.Name); file != "" {
   263  		if err := loadConfig(file, &cfg); err != nil {
   264  			utils.Fatalf("%v", err)
   265  		}
   266  	}
   267  	utils.SetNodeConfig(ctx, &cfg.Node)
   268  	keydir, err := cfg.Node.KeyDirConfig()
   269  	if err != nil {
   270  		utils.Fatalf("Failed to read configuration: %v", err)
   271  	}
   272  	scryptN := keystore.StandardScryptN
   273  	scryptP := keystore.StandardScryptP
   274  	if cfg.Node.UseLightweightKDF {
   275  		scryptN = keystore.LightScryptN
   276  		scryptP = keystore.LightScryptP
   277  	}
   278  
   279  	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))
   280  
   281  	account, err := keystore.StoreKey(keydir, password, scryptN, scryptP)
   282  
   283  	if err != nil {
   284  		utils.Fatalf("Failed to create account: %v", err)
   285  	}
   286  	fmt.Printf("\nYour new key was generated\n\n")
   287  	fmt.Printf("Public address of the key:   %s\n", account.Address.Hex())
   288  	fmt.Printf("Path of the secret key file: %s\n\n", account.URL.Path)
   289  	fmt.Printf("- You can share your public address with anyone. Others need it to interact with you.\n")
   290  	fmt.Printf("- You must NEVER share the secret key with anyone! The key controls access to your funds!\n")
   291  	fmt.Printf("- You must BACKUP your key file! Without the key, it's impossible to access account funds!\n")
   292  	fmt.Printf("- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!\n\n")
   293  	return nil
   294  }
   295  
   296  // accountUpdate transitions an account from a previous format to the current
   297  // one, also providing the possibility to change the pass-phrase.
   298  func accountUpdate(ctx *cli.Context) error {
   299  	if ctx.Args().Len() == 0 {
   300  		utils.Fatalf("No accounts specified to update")
   301  	}
   302  	stack, _ := makeConfigNode(ctx)
   303  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   304  
   305  	for _, addr := range ctx.Args().Slice() {
   306  		account, oldPassword := unlockAccount(ks, addr, 0, nil)
   307  		newPassword := utils.GetPassPhraseWithList("Please give a new password. Do not forget this password.", true, 0, nil)
   308  		if err := ks.Update(account, oldPassword, newPassword); err != nil {
   309  			utils.Fatalf("Could not update the account: %v", err)
   310  		}
   311  	}
   312  	return nil
   313  }
   314  
   315  func importWallet(ctx *cli.Context) error {
   316  	if ctx.Args().Len() != 1 {
   317  		utils.Fatalf("keyfile must be given as the only argument")
   318  	}
   319  	keyfile := ctx.Args().First()
   320  	keyJSON, err := os.ReadFile(keyfile)
   321  	if err != nil {
   322  		utils.Fatalf("Could not read wallet file: %v", err)
   323  	}
   324  
   325  	stack, _ := makeConfigNode(ctx)
   326  	passphrase := utils.GetPassPhraseWithList("", false, 0, utils.MakePasswordList(ctx))
   327  
   328  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   329  	acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
   330  	if err != nil {
   331  		utils.Fatalf("%v", err)
   332  	}
   333  	fmt.Printf("Address: {%x}\n", acct.Address)
   334  	return nil
   335  }
   336  
   337  func accountImport(ctx *cli.Context) error {
   338  	if ctx.Args().Len() != 1 {
   339  		utils.Fatalf("keyfile must be given as the only argument")
   340  	}
   341  	keyfile := ctx.Args().First()
   342  	key, err := crypto.LoadECDSA(keyfile)
   343  	if err != nil {
   344  		utils.Fatalf("Failed to load the private key: %v", err)
   345  	}
   346  	stack, _ := makeConfigNode(ctx)
   347  	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))
   348  
   349  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   350  	acct, err := ks.ImportECDSA(key, passphrase)
   351  	if err != nil {
   352  		utils.Fatalf("Could not create the account: %v", err)
   353  	}
   354  	fmt.Printf("Address: {%x}\n", acct.Address)
   355  	return nil
   356  }