github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/cmd/quickchain/accountcmd.go (about)

     1  
     2  package main
     3  
     4  import (
     5  	"fmt"
     6  	"io/ioutil"
     7  
     8  	"github.com/quickchainproject/quickchain/accounts"
     9  	"github.com/quickchainproject/quickchain/accounts/keystore"
    10  	"github.com/quickchainproject/quickchain/cmd/utils"
    11  	"github.com/quickchainproject/quickchain/console"
    12  	"github.com/quickchainproject/quickchain/crypto"
    13  	"github.com/quickchainproject/quickchain/log"
    14  	"gopkg.in/urfave/cli.v1"
    15  )
    16  
    17  var (
    18  	walletCommand = cli.Command{
    19  		Name:      "wallet",
    20  		Usage:     "Manage Ethereum presale wallets",
    21  		ArgsUsage: "",
    22  		Category:  "ACCOUNT COMMANDS",
    23  		Description: `
    24      geth wallet import /path/to/my/presale.wallet
    25  
    26  will prompt for your password and imports your ether presale account.
    27  It can be used non-interactively with the --password option taking a
    28  passwordfile as argument containing the wallet password in plaintext.`,
    29  		Subcommands: []cli.Command{
    30  			{
    31  
    32  				Name:      "import",
    33  				Usage:     "Import Ethereum presale wallet",
    34  				ArgsUsage: "<keyFile>",
    35  				Action:    utils.MigrateFlags(importWallet),
    36  				Category:  "ACCOUNT COMMANDS",
    37  				Flags: []cli.Flag{
    38  					utils.DataDirFlag,
    39  					utils.KeyStoreDirFlag,
    40  					utils.PasswordFileFlag,
    41  					utils.LightKDFFlag,
    42  				},
    43  				Description: `
    44  	geth 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 quickchain 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:   "create",
    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  					utils.LightKDFFlag,
    98  				},
    99  				Description: `
   100      geth account new
   101  
   102  Creates a new account and prints the address.
   103  
   104  The account is saved in encrypted format, you are prompted for a passphrase.
   105  
   106  You must remember this passphrase to unlock your account in the future.
   107  
   108  For non-interactive use the passphrase can be specified with the --password flag:
   109  
   110  Note, this is meant to be used for testing only, it is a bad idea to save your
   111  password to file or expose in any other way.
   112  `,
   113  			},
   114  			{
   115  				Name:      "update",
   116  				Usage:     "Update an existing account",
   117  				Action:    utils.MigrateFlags(accountUpdate),
   118  				ArgsUsage: "<address>",
   119  				Flags: []cli.Flag{
   120  					utils.DataDirFlag,
   121  					utils.KeyStoreDirFlag,
   122  					utils.LightKDFFlag,
   123  				},
   124  				Description: `
   125      quickchain account update <address>
   126  
   127  Update an existing account.
   128  
   129  The account is saved in the newest version in encrypted format, you are prompted
   130  for a passphrase to unlock the account and another to save the updated file.
   131  
   132  This same command can therefore be used to migrate an account of a deprecated
   133  format to the newest format or change the password for an account.
   134  
   135  For non-interactive use the passphrase can be specified with the --password flag:
   136  
   137      quickchain account update [options] <address>
   138  
   139  Since only one password can be given, only format update can be performed,
   140  changing your password is only possible interactively.
   141  `,
   142  			},
   143  			{
   144  				Name:   "import",
   145  				Usage:  "Import a private key into a new account",
   146  				Action: utils.MigrateFlags(accountImport),
   147  				Flags: []cli.Flag{
   148  					utils.DataDirFlag,
   149  					utils.KeyStoreDirFlag,
   150  					utils.PasswordFileFlag,
   151  					utils.LightKDFFlag,
   152  				},
   153  				ArgsUsage: "<keyFile>",
   154  				Description: `
   155      quickchain account import <keyfile>
   156  
   157  Imports an unencrypted private key from <keyfile> and creates a new account.
   158  Prints the address.
   159  
   160  The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
   161  
   162  The account is saved in encrypted format, you are prompted for a passphrase.
   163  
   164  You must remember this passphrase to unlock your account in the future.
   165  
   166  For non-interactive use the passphrase can be specified with the -password flag:
   167  
   168      quickchain account import [options] <keyfile>
   169  
   170  Note:
   171  As you can directly copy your encrypted accounts to another quickchain instance,
   172  this import mechanism is not needed when you transfer an account between
   173  nodes.
   174  `,
   175  			},
   176  		},
   177  	}
   178  )
   179  
   180  func accountList(ctx *cli.Context) error {
   181  	stack, _ := makeConfigNode(ctx)
   182  	var index int
   183  	for _, wallet := range stack.AccountManager().Wallets() {
   184  		for _, account := range wallet.Accounts() {
   185  			fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL)
   186  			index++
   187  		}
   188  	}
   189  	return nil
   190  }
   191  
   192  // tries unlocking the specified account a few times.
   193  func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) {
   194  	account, err := utils.MakeAddress(ks, address)
   195  	if err != nil {
   196  		utils.Fatalf("Could not list accounts: %v", err)
   197  	}
   198  	for trials := 0; trials < 3; trials++ {
   199  		prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
   200  		password := getPassPhrase(prompt, false, i, passwords)
   201  		err = ks.Unlock(account, password)
   202  		if err == nil {
   203  			log.Info("Unlocked account", "address", account.Address.Hex())
   204  			return account, password
   205  		}
   206  		if err, ok := err.(*keystore.AmbiguousAddrError); ok {
   207  			log.Info("Unlocked account", "address", account.Address.Hex())
   208  			return ambiguousAddrRecovery(ks, err, password), password
   209  		}
   210  		if err != keystore.ErrDecrypt {
   211  			// No need to prompt again if the error is not decryption-related.
   212  			break
   213  		}
   214  	}
   215  	// All trials expended to unlock account, bail out
   216  	utils.Fatalf("Failed to unlock account %s (%v)", address, err)
   217  
   218  	return accounts.Account{}, ""
   219  }
   220  
   221  // getPassPhrase retrieves the password associated with an account, either fetched
   222  // from a list of preloaded passphrases, or requested interactively from the user.
   223  func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
   224  	// If a list of passwords was supplied, retrieve from them
   225  	if len(passwords) > 0 {
   226  		if i < len(passwords) {
   227  			return passwords[i]
   228  		}
   229  		return passwords[len(passwords)-1]
   230  	}
   231  	// Otherwise prompt the user for the password
   232  	if prompt != "" {
   233  		fmt.Println(prompt)
   234  	}
   235  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   236  	if err != nil {
   237  		utils.Fatalf("Failed to read passphrase: %v", err)
   238  	}
   239  	if confirmation {
   240  		confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ")
   241  		if err != nil {
   242  			utils.Fatalf("Failed to read passphrase confirmation: %v", err)
   243  		}
   244  		if password != confirm {
   245  			utils.Fatalf("Passphrases do not match")
   246  		}
   247  	}
   248  	return password
   249  }
   250  
   251  func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
   252  	fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
   253  	for _, a := range err.Matches {
   254  		fmt.Println("  ", a.URL)
   255  	}
   256  	fmt.Println("Testing your passphrase against all of them...")
   257  	var match *accounts.Account
   258  	for _, a := range err.Matches {
   259  		if err := ks.Unlock(a, auth); err == nil {
   260  			match = &a
   261  			break
   262  		}
   263  	}
   264  	if match == nil {
   265  		utils.Fatalf("None of the listed files could be unlocked.")
   266  	}
   267  	fmt.Printf("Your passphrase unlocked %s\n", match.URL)
   268  	fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
   269  	for _, a := range err.Matches {
   270  		if a != *match {
   271  			fmt.Println("  ", a.URL)
   272  		}
   273  	}
   274  	return *match
   275  }
   276  
   277  // accountCreate creates a new account into the keystore defined by the CLI flags.
   278  func accountCreate(ctx *cli.Context) error {
   279  	cfg := gethConfig{Node: defaultNodeConfig()}
   280  	// Load config file.
   281  	if file := ctx.GlobalString(configFileFlag.Name); file != "" {
   282  		if err := loadConfig(file, &cfg); err != nil {
   283  			utils.Fatalf("%v", err)
   284  		}
   285  	}
   286  	utils.SetNodeConfig(ctx, &cfg.Node)
   287  	scryptN, scryptP, keydir, err := cfg.Node.AccountConfig()
   288  
   289  	if err != nil {
   290  		utils.Fatalf("Failed to read configuration: %v", err)
   291  	}
   292  
   293  	password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
   294  
   295  	address, err := keystore.StoreKey(keydir, password, scryptN, scryptP)
   296  
   297  	if err != nil {
   298  		utils.Fatalf("Failed to create account: %v", err)
   299  	}
   300  	fmt.Printf("Address: {%x}\n", address)
   301  	return nil
   302  }
   303  
   304  // accountUpdate transitions an account from a previous format to the current
   305  // one, also providing the possibility to change the pass-phrase.
   306  func accountUpdate(ctx *cli.Context) error {
   307  	if len(ctx.Args()) == 0 {
   308  		utils.Fatalf("No accounts specified to update")
   309  	}
   310  	stack, _ := makeConfigNode(ctx)
   311  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   312  
   313  	for _, addr := range ctx.Args() {
   314  		account, oldPassword := unlockAccount(ctx, ks, addr, 0, nil)
   315  		newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
   316  		if err := ks.Update(account, oldPassword, newPassword); err != nil {
   317  			utils.Fatalf("Could not update the account: %v", err)
   318  		}
   319  	}
   320  	return nil
   321  }
   322  
   323  func importWallet(ctx *cli.Context) error {
   324  	keyfile := ctx.Args().First()
   325  	if len(keyfile) == 0 {
   326  		utils.Fatalf("keyfile must be given as argument")
   327  	}
   328  	keyJson, err := ioutil.ReadFile(keyfile)
   329  	if err != nil {
   330  		utils.Fatalf("Could not read wallet file: %v", err)
   331  	}
   332  
   333  	stack, _ := makeConfigNode(ctx)
   334  	passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
   335  
   336  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   337  	acct, err := ks.ImportPreSaleKey(keyJson, passphrase)
   338  	if err != nil {
   339  		utils.Fatalf("%v", err)
   340  	}
   341  	fmt.Printf("Address: {%x}\n", acct.Address)
   342  	return nil
   343  }
   344  
   345  func accountImport(ctx *cli.Context) error {
   346  	keyfile := ctx.Args().First()
   347  	if len(keyfile) == 0 {
   348  		utils.Fatalf("keyfile must be given as argument")
   349  	}
   350  	key, err := crypto.LoadECDSA(keyfile)
   351  	if err != nil {
   352  		utils.Fatalf("Failed to load the private key: %v", err)
   353  	}
   354  	stack, _ := makeConfigNode(ctx)
   355  	passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
   356  
   357  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   358  	acct, err := ks.ImportECDSA(key, passphrase)
   359  	if err != nil {
   360  		utils.Fatalf("Could not create the account: %v", err)
   361  	}
   362  	fmt.Printf("Address: {%x}\n", acct.Address)
   363  	return nil
   364  }