github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/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  	"io/ioutil"
    22  
    23  	"github.com/ethereum/go-ethereum/accounts"
    24  	"github.com/ethereum/go-ethereum/cmd/utils"
    25  	"github.com/ethereum/go-ethereum/console"
    26  	"github.com/ethereum/go-ethereum/crypto"
    27  	"github.com/ethereum/go-ethereum/logger"
    28  	"github.com/ethereum/go-ethereum/logger/glog"
    29  	"gopkg.in/urfave/cli.v1"
    30  )
    31  
    32  var (
    33  	walletCommand = cli.Command{
    34  		Name:      "wallet",
    35  		Usage:     "Manage Ethereum presale wallets",
    36  		ArgsUsage: "",
    37  		Category:  "ACCOUNT COMMANDS",
    38  		Description: `
    39      geth wallet import /path/to/my/presale.wallet
    40  
    41  will prompt for your password and imports your ether presale account.
    42  It can be used non-interactively with the --password option taking a
    43  passwordfile as argument containing the wallet password in plaintext.
    44  
    45  `,
    46  		Subcommands: []cli.Command{
    47  			{
    48  				Action:    importWallet,
    49  				Name:      "import",
    50  				Usage:     "Import Ethereum presale wallet",
    51  				ArgsUsage: "<keyFile>",
    52  				Description: `
    53  TODO: Please write this
    54  `,
    55  			},
    56  		},
    57  	}
    58  	accountCommand = cli.Command{
    59  		Action:    accountList,
    60  		Name:      "account",
    61  		Usage:     "Manage accounts",
    62  		ArgsUsage: "",
    63  		Category:  "ACCOUNT COMMANDS",
    64  		Description: `
    65  Manage accounts lets you create new accounts, list all existing accounts,
    66  import a private key into a new account.
    67  
    68  '            help' shows a list of subcommands or help for one subcommand.
    69  
    70  It supports interactive mode, when you are prompted for password as well as
    71  non-interactive mode where passwords are supplied via a given password file.
    72  Non-interactive mode is only meant for scripted use on test networks or known
    73  safe environments.
    74  
    75  Make sure you remember the password you gave when creating a new account (with
    76  either new or import). Without it you are not able to unlock your account.
    77  
    78  Note that exporting your key in unencrypted format is NOT supported.
    79  
    80  Keys are stored under <DATADIR>/keystore.
    81  It is safe to transfer the entire directory or the individual keys therein
    82  between ethereum nodes by simply copying.
    83  Make sure you backup your keys regularly.
    84  
    85  In order to use your account to send transactions, you need to unlock them using
    86  the '--unlock' option. The argument is a space separated list of addresses or
    87  indexes. If used non-interactively with a passwordfile, the file should contain
    88  the respective passwords one per line. If you unlock n accounts and the password
    89  file contains less than n entries, then the last password is meant to apply to
    90  all remaining accounts.
    91  
    92  And finally. DO NOT FORGET YOUR PASSWORD.
    93  `,
    94  		Subcommands: []cli.Command{
    95  			{
    96  				Action:    accountList,
    97  				Name:      "list",
    98  				Usage:     "Print account addresses",
    99  				ArgsUsage: " ",
   100  				Description: `
   101  TODO: Please write this
   102  `,
   103  			},
   104  			{
   105  				Action:    accountCreate,
   106  				Name:      "new",
   107  				Usage:     "Create a new account",
   108  				ArgsUsage: " ",
   109  				Description: `
   110      geth account new
   111  
   112  Creates a new account. Prints the address.
   113  
   114  The account is saved in encrypted format, you are prompted for a passphrase.
   115  
   116  You must remember this passphrase to unlock your account in the future.
   117  
   118  For non-interactive use the passphrase can be specified with the --password flag:
   119  
   120      geth --password <passwordfile> account new
   121  
   122  Note, this is meant to be used for testing only, it is a bad idea to save your
   123  password to file or expose in any other way.
   124  `,
   125  			},
   126  			{
   127  				Action:    accountUpdate,
   128  				Name:      "update",
   129  				Usage:     "Update an existing account",
   130  				ArgsUsage: "<address>",
   131  				Description: `
   132      geth account update <address>
   133  
   134  Update an existing account.
   135  
   136  The account is saved in the newest version in encrypted format, you are prompted
   137  for a passphrase to unlock the account and another to save the updated file.
   138  
   139  This same command can therefore be used to migrate an account of a deprecated
   140  format to the newest format or change the password for an account.
   141  
   142  For non-interactive use the passphrase can be specified with the --password flag:
   143  
   144      geth --password <passwordfile> account update <address>
   145  
   146  Since only one password can be given, only format update can be performed,
   147  changing your password is only possible interactively.
   148  `,
   149  			},
   150  			{
   151  				Action:    accountImport,
   152  				Name:      "import",
   153  				Usage:     "Import a private key into a new account",
   154  				ArgsUsage: "<keyFile>",
   155  				Description: `
   156      geth account import <keyfile>
   157  
   158  Imports an unencrypted private key from <keyfile> and creates a new account.
   159  Prints the address.
   160  
   161  The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
   162  
   163  The account is saved in encrypted format, you are prompted for a passphrase.
   164  
   165  You must remember this passphrase to unlock your account in the future.
   166  
   167  For non-interactive use the passphrase can be specified with the -password flag:
   168  
   169      geth --password <passwordfile> account import <keyfile>
   170  
   171  Note:
   172  As you can directly copy your encrypted accounts to another ethereum instance,
   173  this import mechanism is not needed when you transfer an account between
   174  nodes.
   175  `,
   176  			},
   177  		},
   178  	}
   179  )
   180  
   181  func accountList(ctx *cli.Context) error {
   182  	stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
   183  	for i, acct := range stack.AccountManager().Accounts() {
   184  		fmt.Printf("Account #%d: {%x} %s\n", i, acct.Address, acct.File)
   185  	}
   186  	return nil
   187  }
   188  
   189  // tries unlocking the specified account a few times.
   190  func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (accounts.Account, string) {
   191  	account, err := utils.MakeAddress(accman, address)
   192  	if err != nil {
   193  		utils.Fatalf("Could not list accounts: %v", err)
   194  	}
   195  	for trials := 0; trials < 3; trials++ {
   196  		prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
   197  		password := getPassPhrase(prompt, false, i, passwords)
   198  		err = accman.Unlock(account, password)
   199  		if err == nil {
   200  			glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
   201  			return account, password
   202  		}
   203  		if err, ok := err.(*accounts.AmbiguousAddrError); ok {
   204  			glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
   205  			return ambiguousAddrRecovery(accman, err, password), password
   206  		}
   207  		if err != accounts.ErrDecrypt {
   208  			// No need to prompt again if the error is not decryption-related.
   209  			break
   210  		}
   211  	}
   212  	// All trials expended to unlock account, bail out
   213  	utils.Fatalf("Failed to unlock account %s (%v)", address, err)
   214  	return accounts.Account{}, ""
   215  }
   216  
   217  // getPassPhrase retrieves the passwor associated with an account, either fetched
   218  // from a list of preloaded passphrases, or requested interactively from the user.
   219  func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
   220  	// If a list of passwords was supplied, retrieve from them
   221  	if len(passwords) > 0 {
   222  		if i < len(passwords) {
   223  			return passwords[i]
   224  		}
   225  		return passwords[len(passwords)-1]
   226  	}
   227  	// Otherwise prompt the user for the password
   228  	if prompt != "" {
   229  		fmt.Println(prompt)
   230  	}
   231  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   232  	if err != nil {
   233  		utils.Fatalf("Failed to read passphrase: %v", err)
   234  	}
   235  	if confirmation {
   236  		confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ")
   237  		if err != nil {
   238  			utils.Fatalf("Failed to read passphrase confirmation: %v", err)
   239  		}
   240  		if password != confirm {
   241  			utils.Fatalf("Passphrases do not match")
   242  		}
   243  	}
   244  	return password
   245  }
   246  
   247  func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrError, auth string) accounts.Account {
   248  	fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
   249  	for _, a := range err.Matches {
   250  		fmt.Println("  ", a.File)
   251  	}
   252  	fmt.Println("Testing your passphrase against all of them...")
   253  	var match *accounts.Account
   254  	for _, a := range err.Matches {
   255  		if err := am.Unlock(a, auth); err == nil {
   256  			match = &a
   257  			break
   258  		}
   259  	}
   260  	if match == nil {
   261  		utils.Fatalf("None of the listed files could be unlocked.")
   262  	}
   263  	fmt.Printf("Your passphrase unlocked %s\n", match.File)
   264  	fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
   265  	for _, a := range err.Matches {
   266  		if a != *match {
   267  			fmt.Println("  ", a.File)
   268  		}
   269  	}
   270  	return *match
   271  }
   272  
   273  // accountCreate creates a new account into the keystore defined by the CLI flags.
   274  func accountCreate(ctx *cli.Context) error {
   275  	stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
   276  	password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
   277  
   278  	account, err := stack.AccountManager().NewAccount(password)
   279  	if err != nil {
   280  		utils.Fatalf("Failed to create account: %v", err)
   281  	}
   282  	fmt.Printf("Address: {%x}\n", account.Address)
   283  	return nil
   284  }
   285  
   286  // accountUpdate transitions an account from a previous format to the current
   287  // one, also providing the possibility to change the pass-phrase.
   288  func accountUpdate(ctx *cli.Context) error {
   289  	if len(ctx.Args()) == 0 {
   290  		utils.Fatalf("No accounts specified to update")
   291  	}
   292  	stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
   293  	account, oldPassword := unlockAccount(ctx, stack.AccountManager(), ctx.Args().First(), 0, nil)
   294  	newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
   295  	if err := stack.AccountManager().Update(account, oldPassword, newPassword); err != nil {
   296  		utils.Fatalf("Could not update the account: %v", err)
   297  	}
   298  	return nil
   299  }
   300  
   301  func importWallet(ctx *cli.Context) error {
   302  	keyfile := ctx.Args().First()
   303  	if len(keyfile) == 0 {
   304  		utils.Fatalf("keyfile must be given as argument")
   305  	}
   306  	keyJson, err := ioutil.ReadFile(keyfile)
   307  	if err != nil {
   308  		utils.Fatalf("Could not read wallet file: %v", err)
   309  	}
   310  
   311  	stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
   312  	passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
   313  	acct, err := stack.AccountManager().ImportPreSaleKey(keyJson, passphrase)
   314  	if err != nil {
   315  		utils.Fatalf("%v", err)
   316  	}
   317  	fmt.Printf("Address: {%x}\n", acct.Address)
   318  	return nil
   319  }
   320  
   321  func accountImport(ctx *cli.Context) error {
   322  	keyfile := ctx.Args().First()
   323  	if len(keyfile) == 0 {
   324  		utils.Fatalf("keyfile must be given as argument")
   325  	}
   326  	key, err := crypto.LoadECDSA(keyfile)
   327  	if err != nil {
   328  		utils.Fatalf("Failed to load the private key: %v", err)
   329  	}
   330  	stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
   331  	passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
   332  	acct, err := stack.AccountManager().ImportECDSA(key, passphrase)
   333  	if err != nil {
   334  		utils.Fatalf("Could not create the account: %v", err)
   335  	}
   336  	fmt.Printf("Address: {%x}\n", acct.Address)
   337  	return nil
   338  }