github.com/core-coin/go-core/v2@v2.1.9/cmd/gocore/accountcmd.go (about)

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