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