github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/cmd/geth/accountcmd.go (about)

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