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