github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/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  	"encoding/hex"
    21  	"fmt"
    22  	"io/ioutil"
    23  
    24  	"github.com/ethereum/go-ethereum/accounts"
    25  	"github.com/ethereum/go-ethereum/accounts/keystore"
    26  	"github.com/ethereum/go-ethereum/cmd/utils"
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/console"
    29  	"github.com/ethereum/go-ethereum/crypto"
    30  	"github.com/ethereum/go-ethereum/log"
    31  	cli "gopkg.in/urfave/cli.v1"
    32  )
    33  
    34  var (
    35  	blsFlag = cli.BoolFlag{
    36  		Name:  "bls",
    37  		Usage: "Set to specify generation of proof-of-possession of a BLS key.",
    38  	}
    39  	walletCommand = cli.Command{
    40  		Name:      "wallet",
    41  		Usage:     "Manage Ethereum presale wallets",
    42  		ArgsUsage: "",
    43  		Category:  "ACCOUNT COMMANDS",
    44  		Description: `
    45      geth wallet import /path/to/my/presale.wallet
    46  
    47  will prompt for your password and imports your ether presale account.
    48  It can be used non-interactively with the --password option taking a
    49  passwordfile as argument containing the wallet password in plaintext.`,
    50  		Subcommands: []cli.Command{
    51  			{
    52  
    53  				Name:      "import",
    54  				Usage:     "Import Ethereum presale wallet",
    55  				ArgsUsage: "<keyFile>",
    56  				Action:    utils.MigrateFlags(importWallet),
    57  				Category:  "ACCOUNT COMMANDS",
    58  				Flags: []cli.Flag{
    59  					utils.DataDirFlag,
    60  					utils.KeyStoreDirFlag,
    61  					utils.PasswordFileFlag,
    62  					utils.LightKDFFlag,
    63  				},
    64  				Description: `
    65  	geth wallet [options] /path/to/my/presale.wallet
    66  
    67  will prompt for your password and imports your ether presale account.
    68  It can be used non-interactively with the --password option taking a
    69  passwordfile as argument containing the wallet password in plaintext.`,
    70  			},
    71  		},
    72  	}
    73  
    74  	accountCommand = cli.Command{
    75  		Name:     "account",
    76  		Usage:    "Manage accounts",
    77  		Category: "ACCOUNT COMMANDS",
    78  		Description: `
    79  
    80  Manage accounts, list all existing accounts, import a private key into a new
    81  account, create a new account or update an existing account.
    82  
    83  It supports interactive mode, when you are prompted for password as well as
    84  non-interactive mode where passwords are supplied via a given password file.
    85  Non-interactive mode is only meant for scripted use on test networks or known
    86  safe environments.
    87  
    88  Make sure you remember the password you gave when creating a new account (with
    89  either new or import). Without it you are not able to unlock your account.
    90  
    91  Note that exporting your key in unencrypted format is NOT supported.
    92  
    93  Keys are stored under <DATADIR>/keystore.
    94  It is safe to transfer the entire directory or the individual keys therein
    95  between ethereum nodes by simply copying.
    96  
    97  Make sure you backup your keys regularly.`,
    98  		Subcommands: []cli.Command{
    99  			{
   100  				Name:   "list",
   101  				Usage:  "Print summary of existing accounts",
   102  				Action: utils.MigrateFlags(accountList),
   103  				Flags: []cli.Flag{
   104  					utils.DataDirFlag,
   105  					utils.KeyStoreDirFlag,
   106  				},
   107  				Description: `
   108  Print a short summary of all accounts`,
   109  			},
   110  			{
   111  				Name:      "proof-of-possession",
   112  				Usage:     "Generate a proof-of-possession for the given account",
   113  				Action:    utils.MigrateFlags(accountProofOfPossession),
   114  				ArgsUsage: "<address> <address>",
   115  				Flags: []cli.Flag{
   116  					utils.DataDirFlag,
   117  					utils.KeyStoreDirFlag,
   118  					utils.PasswordFileFlag,
   119  					utils.LightKDFFlag,
   120  					blsFlag,
   121  				},
   122  				Description: `
   123  Print a proof-of-possession signature for the given account.
   124  `,
   125  			},
   126  			{
   127  				Name:   "new",
   128  				Usage:  "Create a new account",
   129  				Action: utils.MigrateFlags(accountCreate),
   130  				Flags: []cli.Flag{
   131  					utils.DataDirFlag,
   132  					utils.KeyStoreDirFlag,
   133  					utils.PasswordFileFlag,
   134  					utils.LightKDFFlag,
   135  				},
   136  				Description: `
   137      geth account new
   138  
   139  Creates a new account and prints the address.
   140  
   141  The account is saved in encrypted format, you are prompted for a passphrase.
   142  
   143  You must remember this passphrase to unlock your account in the future.
   144  
   145  For non-interactive use the passphrase can be specified with the --password flag:
   146  
   147  Note, this is meant to be used for testing only, it is a bad idea to save your
   148  password to file or expose in any other way.
   149  `,
   150  			},
   151  			{
   152  				Name:      "update",
   153  				Usage:     "Update an existing account",
   154  				Action:    utils.MigrateFlags(accountUpdate),
   155  				ArgsUsage: "<address>",
   156  				Flags: []cli.Flag{
   157  					utils.DataDirFlag,
   158  					utils.KeyStoreDirFlag,
   159  					utils.LightKDFFlag,
   160  				},
   161  				Description: `
   162      geth account update <address>
   163  
   164  Update an existing account.
   165  
   166  The account is saved in the newest version in encrypted format, you are prompted
   167  for a passphrase to unlock the account and another to save the updated file.
   168  
   169  This same command can therefore be used to migrate an account of a deprecated
   170  format to the newest format or change the password for an account.
   171  
   172  For non-interactive use the passphrase can be specified with the --password flag:
   173  
   174      geth account update [options] <address>
   175  
   176  Since only one password can be given, only format update can be performed,
   177  changing your password is only possible interactively.
   178  `,
   179  			},
   180  			{
   181  				Name:   "import",
   182  				Usage:  "Import a private key into a new account",
   183  				Action: utils.MigrateFlags(accountImport),
   184  				Flags: []cli.Flag{
   185  					utils.DataDirFlag,
   186  					utils.KeyStoreDirFlag,
   187  					utils.PasswordFileFlag,
   188  					utils.LightKDFFlag,
   189  				},
   190  				ArgsUsage: "<keyFile>",
   191  				Description: `
   192      geth account import <keyfile>
   193  
   194  Imports an unencrypted private key from <keyfile> and creates a new account.
   195  Prints the address.
   196  
   197  The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
   198  
   199  The account is saved in encrypted format, you are prompted for a passphrase.
   200  
   201  You must remember this passphrase to unlock your account in the future.
   202  
   203  For non-interactive use the passphrase can be specified with the -password flag:
   204  
   205      geth account import [options] <keyfile>
   206  
   207  Note:
   208  As you can directly copy your encrypted accounts to another ethereum instance,
   209  this import mechanism is not needed when you transfer an account between
   210  nodes.
   211  `,
   212  			},
   213  		},
   214  	}
   215  )
   216  
   217  func accountList(ctx *cli.Context) error {
   218  	stack, _ := makeConfigNode(ctx)
   219  	var index int
   220  	for _, wallet := range stack.AccountManager().Wallets() {
   221  		for _, account := range wallet.Accounts() {
   222  			fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL)
   223  			index++
   224  		}
   225  	}
   226  	return nil
   227  }
   228  
   229  func accountProofOfPossession(ctx *cli.Context) error {
   230  	if len(ctx.Args()) != 2 {
   231  		utils.Fatalf("Please specify the address to prove possession of and the address to sign as proof-of-possession.")
   232  	}
   233  
   234  	stack, _ := makeConfigNode(ctx)
   235  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   236  
   237  	signer := common.HexToAddress(ctx.Args()[0])
   238  	message := common.HexToAddress(ctx.Args()[1])
   239  	account, _ := unlockAccount(ctx, ks, signer.String(), 0, utils.MakePasswordList(ctx))
   240  	var key []byte
   241  	var pop []byte
   242  	var err error
   243  	keyType := "ECDSA"
   244  	if ctx.IsSet(blsFlag.Name) {
   245  		keyType = "BLS"
   246  		key, pop, err = ks.GenerateProofOfPossessionBLS(account, message)
   247  	} else {
   248  		key, pop, err = ks.GenerateProofOfPossession(account, message)
   249  	}
   250  	if err != nil {
   251  		return err
   252  	}
   253  
   254  	fmt.Printf("Account {%x}:\n  Signature: %s\n  %s Public Key: %s\n", account.Address, hex.EncodeToString(pop), keyType, hex.EncodeToString(key))
   255  
   256  	return nil
   257  }
   258  
   259  // tries unlocking the specified account a few times.
   260  func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) {
   261  	account, err := utils.MakeAddress(ks, address)
   262  	if err != nil {
   263  		utils.Fatalf("Could not list accounts: %v", err)
   264  	}
   265  	for trials := 0; trials < 3; trials++ {
   266  		prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
   267  		password := getPassPhrase(prompt, false, i, passwords)
   268  		err = ks.Unlock(account, password)
   269  		if err == nil {
   270  			log.Info("Unlocked account", "address", account.Address.Hex())
   271  			return account, password
   272  		}
   273  		if err, ok := err.(*keystore.AmbiguousAddrError); ok {
   274  			log.Info("Unlocked account", "address", account.Address.Hex())
   275  			return ambiguousAddrRecovery(ks, err, password), password
   276  		}
   277  		if err != keystore.ErrDecrypt {
   278  			// No need to prompt again if the error is not decryption-related.
   279  			break
   280  		}
   281  	}
   282  	// All trials expended to unlock account, bail out
   283  	utils.Fatalf("Failed to unlock account %s (%v)", address, err)
   284  
   285  	return accounts.Account{}, ""
   286  }
   287  
   288  // getPassPhrase retrieves the password associated with an account, either fetched
   289  // from a list of preloaded passphrases, or requested interactively from the user.
   290  func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
   291  	// If a list of passwords was supplied, retrieve from them
   292  	if len(passwords) > 0 {
   293  		if i < len(passwords) {
   294  			return passwords[i]
   295  		}
   296  		return passwords[len(passwords)-1]
   297  	}
   298  	// Otherwise prompt the user for the password
   299  	if prompt != "" {
   300  		fmt.Println(prompt)
   301  	}
   302  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   303  	if err != nil {
   304  		utils.Fatalf("Failed to read passphrase: %v", err)
   305  	}
   306  	if confirmation {
   307  		confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ")
   308  		if err != nil {
   309  			utils.Fatalf("Failed to read passphrase confirmation: %v", err)
   310  		}
   311  		if password != confirm {
   312  			utils.Fatalf("Passphrases do not match")
   313  		}
   314  	}
   315  	return password
   316  }
   317  
   318  func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
   319  	fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
   320  	for _, a := range err.Matches {
   321  		fmt.Println("  ", a.URL)
   322  	}
   323  	fmt.Println("Testing your passphrase against all of them...")
   324  	var match *accounts.Account
   325  	for _, a := range err.Matches {
   326  		if err := ks.Unlock(a, auth); err == nil {
   327  			match = &a
   328  			break
   329  		}
   330  	}
   331  	if match == nil {
   332  		utils.Fatalf("None of the listed files could be unlocked.")
   333  	}
   334  	fmt.Printf("Your passphrase unlocked %s\n", match.URL)
   335  	fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
   336  	for _, a := range err.Matches {
   337  		if a != *match {
   338  			fmt.Println("  ", a.URL)
   339  		}
   340  	}
   341  	return *match
   342  }
   343  
   344  // accountCreate creates a new account into the keystore defined by the CLI flags.
   345  func accountCreate(ctx *cli.Context) error {
   346  	cfg := gethConfig{Node: defaultNodeConfig()}
   347  	// Load config file.
   348  	if file := ctx.GlobalString(configFileFlag.Name); file != "" {
   349  		if err := loadConfig(file, &cfg); err != nil {
   350  			utils.Fatalf("%v", err)
   351  		}
   352  	}
   353  	utils.SetNodeConfig(ctx, &cfg.Node)
   354  	scryptN, scryptP, keydir, err := cfg.Node.AccountConfig()
   355  
   356  	if err != nil {
   357  		utils.Fatalf("Failed to read configuration: %v", err)
   358  	}
   359  
   360  	password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
   361  
   362  	address, err := keystore.StoreKey(keydir, password, scryptN, scryptP)
   363  
   364  	if err != nil {
   365  		utils.Fatalf("Failed to create account: %v", err)
   366  	}
   367  	fmt.Printf("Address: {%x}\n", address)
   368  	return nil
   369  }
   370  
   371  // accountUpdate transitions an account from a previous format to the current
   372  // one, also providing the possibility to change the pass-phrase.
   373  func accountUpdate(ctx *cli.Context) error {
   374  	if len(ctx.Args()) == 0 {
   375  		utils.Fatalf("No accounts specified to update")
   376  	}
   377  	stack, _ := makeConfigNode(ctx)
   378  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   379  
   380  	for _, addr := range ctx.Args() {
   381  		account, oldPassword := unlockAccount(ctx, ks, addr, 0, nil)
   382  		newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
   383  		if err := ks.Update(account, oldPassword, newPassword); err != nil {
   384  			utils.Fatalf("Could not update the account: %v", err)
   385  		}
   386  	}
   387  	return nil
   388  }
   389  
   390  func importWallet(ctx *cli.Context) error {
   391  	keyfile := ctx.Args().First()
   392  	if len(keyfile) == 0 {
   393  		utils.Fatalf("keyfile must be given as argument")
   394  	}
   395  	keyJSON, err := ioutil.ReadFile(keyfile)
   396  	if err != nil {
   397  		utils.Fatalf("Could not read wallet file: %v", err)
   398  	}
   399  
   400  	stack, _ := makeConfigNode(ctx)
   401  	passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
   402  
   403  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   404  	acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
   405  	if err != nil {
   406  		utils.Fatalf("%v", err)
   407  	}
   408  	fmt.Printf("Address: {%x}\n", acct.Address)
   409  	return nil
   410  }
   411  
   412  func accountImport(ctx *cli.Context) error {
   413  	keyfile := ctx.Args().First()
   414  	if len(keyfile) == 0 {
   415  		utils.Fatalf("keyfile must be given as argument")
   416  	}
   417  	key, err := crypto.LoadECDSA(keyfile)
   418  	if err != nil {
   419  		utils.Fatalf("Failed to load the private key: %v", err)
   420  	}
   421  	stack, _ := makeConfigNode(ctx)
   422  	passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
   423  
   424  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   425  	acct, err := ks.ImportECDSA(key, passphrase)
   426  	if err != nil {
   427  		utils.Fatalf("Could not create the account: %v", err)
   428  	}
   429  	fmt.Printf("Address: {%x}\n", acct.Address)
   430  	return nil
   431  }