diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go deleted file mode 100644 index 071be85397..0000000000 --- a/cmd/geth/accountcmd.go +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package main - -import ( - "fmt" - "io/ioutil" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/console" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "gopkg.in/urfave/cli.v1" -) - -var ( - walletCommand = cli.Command{ - Name: "wallet", - Usage: "Manage Ethereum presale wallets", - ArgsUsage: "", - Category: "ACCOUNT COMMANDS", - Description: ` - geth wallet import /path/to/my/presale.wallet - -will prompt for your password and imports your ether presale account. -It can be used non-interactively with the --password option taking a -passwordfile as argument containing the wallet password in plaintext.`, - Subcommands: []cli.Command{ - { - - Name: "import", - Usage: "Import Ethereum presale wallet", - ArgsUsage: "", - Action: utils.MigrateFlags(importWallet), - Category: "ACCOUNT COMMANDS", - Flags: []cli.Flag{ - utils.DataDirFlag, - utils.KeyStoreDirFlag, - utils.PasswordFileFlag, - utils.LightKDFFlag, - }, - Description: ` - geth wallet [options] /path/to/my/presale.wallet - -will prompt for your password and imports your ether presale account. -It can be used non-interactively with the --password option taking a -passwordfile as argument containing the wallet password in plaintext.`, - }, - }, - } - - accountCommand = cli.Command{ - Name: "account", - Usage: "Manage accounts", - Category: "ACCOUNT COMMANDS", - Description: ` - -Manage accounts, list all existing accounts, import a private key into a new -account, create a new account or update an existing account. - -It supports interactive mode, when you are prompted for password as well as -non-interactive mode where passwords are supplied via a given password file. -Non-interactive mode is only meant for scripted use on test networks or known -safe environments. - -Make sure you remember the password you gave when creating a new account (with -either new or import). Without it you are not able to unlock your account. - -Note that exporting your key in unencrypted format is NOT supported. - -Keys are stored under /keystore. -It is safe to transfer the entire directory or the individual keys therein -between ethereum nodes by simply copying. - -Make sure you backup your keys regularly.`, - Subcommands: []cli.Command{ - { - Name: "list", - Usage: "Print summary of existing accounts", - Action: utils.MigrateFlags(accountList), - Flags: []cli.Flag{ - utils.DataDirFlag, - utils.KeyStoreDirFlag, - }, - Description: ` -Print a short summary of all accounts`, - }, - { - Name: "new", - Usage: "Create a new account", - Action: utils.MigrateFlags(accountCreate), - Flags: []cli.Flag{ - utils.DataDirFlag, - utils.KeyStoreDirFlag, - utils.PasswordFileFlag, - utils.LightKDFFlag, - }, - Description: ` - geth account new - -Creates a new account and prints the address. - -The account is saved in encrypted format, you are prompted for a passphrase. - -You must remember this passphrase to unlock your account in the future. - -For non-interactive use the passphrase can be specified with the --password flag: - -Note, this is meant to be used for testing only, it is a bad idea to save your -password to file or expose in any other way. -`, - }, - { - Name: "update", - Usage: "Update an existing account", - Action: utils.MigrateFlags(accountUpdate), - ArgsUsage: "
", - Flags: []cli.Flag{ - utils.DataDirFlag, - utils.KeyStoreDirFlag, - utils.LightKDFFlag, - }, - Description: ` - geth account update
- -Update an existing account. - -The account is saved in the newest version in encrypted format, you are prompted -for a passphrase to unlock the account and another to save the updated file. - -This same command can therefore be used to migrate an account of a deprecated -format to the newest format or change the password for an account. - -For non-interactive use the passphrase can be specified with the --password flag: - - geth account update [options]
- -Since only one password can be given, only format update can be performed, -changing your password is only possible interactively. -`, - }, - { - Name: "import", - Usage: "Import a private key into a new account", - Action: utils.MigrateFlags(accountImport), - Flags: []cli.Flag{ - utils.DataDirFlag, - utils.KeyStoreDirFlag, - utils.PasswordFileFlag, - utils.LightKDFFlag, - }, - ArgsUsage: "", - Description: ` - geth account import - -Imports an unencrypted private key from and creates a new account. -Prints the address. - -The keyfile is assumed to contain an unencrypted private key in hexadecimal format. - -The account is saved in encrypted format, you are prompted for a passphrase. - -You must remember this passphrase to unlock your account in the future. - -For non-interactive use the passphrase can be specified with the -password flag: - - geth account import [options] - -Note: -As you can directly copy your encrypted accounts to another ethereum instance, -this import mechanism is not needed when you transfer an account between -nodes. -`, - }, - }, - } -) - -func accountList(ctx *cli.Context) error { - stack, _ := makeConfigNode(ctx) - var index int - for _, wallet := range stack.AccountManager().Wallets() { - for _, account := range wallet.Accounts() { - fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL) - index++ - } - } - return nil -} - -// tries unlocking the specified account a few times. -func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) { - account, err := utils.MakeAddress(ks, address) - if err != nil { - utils.Fatalf("Could not list accounts: %v", err) - } - for trials := 0; trials < 3; trials++ { - prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3) - password := getPassPhrase(prompt, false, i, passwords) - err = ks.Unlock(account, password) - if err == nil { - log.Info("Unlocked account", "address", account.Address.Hex()) - return account, password - } - if err, ok := err.(*keystore.AmbiguousAddrError); ok { - log.Info("Unlocked account", "address", account.Address.Hex()) - return ambiguousAddrRecovery(ks, err, password), password - } - if err != keystore.ErrDecrypt { - // No need to prompt again if the error is not decryption-related. - break - } - } - // All trials expended to unlock account, bail out - utils.Fatalf("Failed to unlock account %s (%v)", address, err) - - return accounts.Account{}, "" -} - -// getPassPhrase retrieves the password associated with an account, either fetched -// from a list of preloaded passphrases, or requested interactively from the user. -func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string { - // If a list of passwords was supplied, retrieve from them - if len(passwords) > 0 { - if i < len(passwords) { - return passwords[i] - } - return passwords[len(passwords)-1] - } - // Otherwise prompt the user for the password - if prompt != "" { - fmt.Println(prompt) - } - password, err := console.Stdin.PromptPassword("Passphrase: ") - if err != nil { - utils.Fatalf("Failed to read passphrase: %v", err) - } - if confirmation { - confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") - if err != nil { - utils.Fatalf("Failed to read passphrase confirmation: %v", err) - } - if password != confirm { - utils.Fatalf("Passphrases do not match") - } - } - return password -} - -func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account { - fmt.Printf("Multiple key files exist for address %x:\n", err.Addr) - for _, a := range err.Matches { - fmt.Println(" ", a.URL) - } - fmt.Println("Testing your passphrase against all of them...") - var match *accounts.Account - for _, a := range err.Matches { - if err := ks.Unlock(a, auth); err == nil { - match = &a - break - } - } - if match == nil { - utils.Fatalf("None of the listed files could be unlocked.") - } - fmt.Printf("Your passphrase unlocked %s\n", match.URL) - fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:") - for _, a := range err.Matches { - if a != *match { - fmt.Println(" ", a.URL) - } - } - return *match -} - -// accountCreate creates a new account into the keystore defined by the CLI flags. -func accountCreate(ctx *cli.Context) error { - cfg := gethConfig{Node: defaultNodeConfig()} - // Load config file. - if file := ctx.GlobalString(configFileFlag.Name); file != "" { - if err := loadConfig(file, &cfg); err != nil { - utils.Fatalf("%v", err) - } - } - utils.SetNodeConfig(ctx, &cfg.Node) - scryptN, scryptP, keydir, err := cfg.Node.AccountConfig() - - if err != nil { - utils.Fatalf("Failed to read configuration: %v", err) - } - - password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) - - address, err := keystore.StoreKey(keydir, password, scryptN, scryptP) - - if err != nil { - utils.Fatalf("Failed to create account: %v", err) - } - fmt.Printf("Address: {%x}\n", address) - return nil -} - -// accountUpdate transitions an account from a previous format to the current -// one, also providing the possibility to change the pass-phrase. -func accountUpdate(ctx *cli.Context) error { - if len(ctx.Args()) == 0 { - utils.Fatalf("No accounts specified to update") - } - stack, _ := makeConfigNode(ctx) - ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - - for _, addr := range ctx.Args() { - account, oldPassword := unlockAccount(ctx, ks, addr, 0, nil) - newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil) - if err := ks.Update(account, oldPassword, newPassword); err != nil { - utils.Fatalf("Could not update the account: %v", err) - } - } - return nil -} - -func importWallet(ctx *cli.Context) error { - keyfile := ctx.Args().First() - if len(keyfile) == 0 { - utils.Fatalf("keyfile must be given as argument") - } - keyJSON, err := ioutil.ReadFile(keyfile) - if err != nil { - utils.Fatalf("Could not read wallet file: %v", err) - } - - stack, _ := makeConfigNode(ctx) - passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx)) - - ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - acct, err := ks.ImportPreSaleKey(keyJSON, passphrase) - if err != nil { - utils.Fatalf("%v", err) - } - fmt.Printf("Address: {%x}\n", acct.Address) - return nil -} - -func accountImport(ctx *cli.Context) error { - keyfile := ctx.Args().First() - if len(keyfile) == 0 { - utils.Fatalf("keyfile must be given as argument") - } - key, err := crypto.LoadECDSA(keyfile) - if err != nil { - utils.Fatalf("Failed to load the private key: %v", err) - } - stack, _ := makeConfigNode(ctx) - passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) - - ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - acct, err := ks.ImportECDSA(key, passphrase) - if err != nil { - utils.Fatalf("Could not create the account: %v", err) - } - fmt.Printf("Address: {%x}\n", acct.Address) - return nil -} diff --git a/cmd/geth/accountcmd_test.go b/cmd/geth/accountcmd_test.go deleted file mode 100644 index 3ea22ccfab..0000000000 --- a/cmd/geth/accountcmd_test.go +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package main - -import ( - "io/ioutil" - "path/filepath" - "runtime" - "strings" - "testing" - - "github.com/cespare/cp" -) - -// These tests are 'smoke tests' for the account related -// subcommands and flags. -// -// For most tests, the test files from package accounts -// are copied into a temporary keystore directory. - -func tmpDatadirWithKeystore(t *testing.T) string { - datadir := tmpdir(t) - keystore := filepath.Join(datadir, "keystore") - source := filepath.Join("..", "..", "accounts", "keystore", "testdata", "keystore") - if err := cp.CopyAll(keystore, source); err != nil { - t.Fatal(err) - } - return datadir -} - -func TestAccountListEmpty(t *testing.T) { - geth := runGeth(t, "account", "list") - geth.ExpectExit() -} - -func TestAccountList(t *testing.T) { - datadir := tmpDatadirWithKeystore(t) - geth := runGeth(t, "account", "list", "--datadir", datadir) - defer geth.ExpectExit() - if runtime.GOOS == "windows" { - geth.Expect(` -Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}\keystore\UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 -Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}\keystore\aaa -Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}\keystore\zzz -`) - } else { - geth.Expect(` -Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 -Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}/keystore/aaa -Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/keystore/zzz -`) - } -} - -func TestAccountNew(t *testing.T) { - geth := runGeth(t, "account", "new", "--lightkdf") - defer geth.ExpectExit() - geth.Expect(` -Your new account is locked with a password. Please give a password. Do not forget this password. -!! Unsupported terminal, password will be echoed. -Passphrase: {{.InputLine "foobar"}} -Repeat passphrase: {{.InputLine "foobar"}} -`) - geth.ExpectRegexp(`Address: \{[0-9a-f]{40}\}\n`) -} - -func TestAccountNewBadRepeat(t *testing.T) { - geth := runGeth(t, "account", "new", "--lightkdf") - defer geth.ExpectExit() - geth.Expect(` -Your new account is locked with a password. Please give a password. Do not forget this password. -!! Unsupported terminal, password will be echoed. -Passphrase: {{.InputLine "something"}} -Repeat passphrase: {{.InputLine "something else"}} -Fatal: Passphrases do not match -`) -} - -func TestAccountUpdate(t *testing.T) { - datadir := tmpDatadirWithKeystore(t) - geth := runGeth(t, "account", "update", - "--datadir", datadir, "--lightkdf", - "f466859ead1932d743d622cb74fc058882e8648a") - defer geth.ExpectExit() - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Passphrase: {{.InputLine "foobar"}} -Please give a new password. Do not forget this password. -Passphrase: {{.InputLine "foobar2"}} -Repeat passphrase: {{.InputLine "foobar2"}} -`) -} - -func TestWalletImport(t *testing.T) { - geth := runGeth(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json") - defer geth.ExpectExit() - geth.Expect(` -!! Unsupported terminal, password will be echoed. -Passphrase: {{.InputLine "foo"}} -Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f} -`) - - files, err := ioutil.ReadDir(filepath.Join(geth.Datadir, "keystore")) - if len(files) != 1 { - t.Errorf("expected one key file in keystore directory, found %d files (error: %v)", len(files), err) - } -} - -func TestWalletImportBadPassword(t *testing.T) { - geth := runGeth(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json") - defer geth.ExpectExit() - geth.Expect(` -!! Unsupported terminal, password will be echoed. -Passphrase: {{.InputLine "wrong"}} -Fatal: could not decrypt key with given passphrase -`) -} - -func TestUnlockFlag(t *testing.T) { - datadir := tmpDatadirWithKeystore(t) - geth := runGeth(t, - "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", - "js", "testdata/empty.js") - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Passphrase: {{.InputLine "foobar"}} -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0xf466859eAD1932D743d622CB74FC058882E8648A", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagWrongPassword(t *testing.T) { - datadir := tmpDatadirWithKeystore(t) - geth := runGeth(t, - "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a") - defer geth.ExpectExit() - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Passphrase: {{.InputLine "wrong1"}} -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3 -Passphrase: {{.InputLine "wrong2"}} -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3 -Passphrase: {{.InputLine "wrong3"}} -Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could not decrypt key with given passphrase) -`) -} - -// https://github.com/ethereum/go-ethereum/issues/1785 -func TestUnlockFlagMultiIndex(t *testing.T) { - datadir := tmpDatadirWithKeystore(t) - geth := runGeth(t, - "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--unlock", "0,2", - "js", "testdata/empty.js") - geth.Expect(` -Unlocking account 0 | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Passphrase: {{.InputLine "foobar"}} -Unlocking account 2 | Attempt 1/3 -Passphrase: {{.InputLine "foobar"}} -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0x7EF5A6135f1FD6a02593eEdC869c6D41D934aef8", - "=0x289d485D9771714CCe91D3393D764E1311907ACc", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagPasswordFile(t *testing.T) { - datadir := tmpDatadirWithKeystore(t) - geth := runGeth(t, - "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--password", "testdata/passwords.txt", "--unlock", "0,2", - "js", "testdata/empty.js") - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0x7EF5A6135f1FD6a02593eEdC869c6D41D934aef8", - "=0x289d485D9771714CCe91D3393D764E1311907ACc", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) { - datadir := tmpDatadirWithKeystore(t) - geth := runGeth(t, - "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--password", "testdata/wrong-passwords.txt", "--unlock", "0,2") - defer geth.ExpectExit() - geth.Expect(` -Fatal: Failed to unlock account 0 (could not decrypt key with given passphrase) -`) -} - -func TestUnlockFlagAmbiguous(t *testing.T) { - store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes") - geth := runGeth(t, - "--keystore", store, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", - "js", "testdata/empty.js") - defer geth.ExpectExit() - - // Helper for the expect template, returns absolute keystore path. - geth.SetTemplateFunc("keypath", func(file string) string { - abs, _ := filepath.Abs(filepath.Join(store, file)) - return abs - }) - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Passphrase: {{.InputLine "foobar"}} -Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a: - keystore://{{keypath "1"}} - keystore://{{keypath "2"}} -Testing your passphrase against all of them... -Your passphrase unlocked keystore://{{keypath "1"}} -In order to avoid this warning, you need to remove the following duplicate key files: - keystore://{{keypath "2"}} -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0xf466859eAD1932D743d622CB74FC058882E8648A", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) { - store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes") - geth := runGeth(t, - "--keystore", store, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a") - defer geth.ExpectExit() - - // Helper for the expect template, returns absolute keystore path. - geth.SetTemplateFunc("keypath", func(file string) string { - abs, _ := filepath.Abs(filepath.Join(store, file)) - return abs - }) - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Passphrase: {{.InputLine "wrong"}} -Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a: - keystore://{{keypath "1"}} - keystore://{{keypath "2"}} -Testing your passphrase against all of them... -Fatal: None of the listed files could be unlocked. -`) - geth.ExpectExit() -} diff --git a/cmd/geth/main.go b/cmd/geth/main.go index a45009e6cb..199b980eb9 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -25,21 +25,18 @@ import ( godebug "runtime/debug" "sort" "strconv" - "strings" "time" "github.com/elastic/gosigar" - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "gopkg.in/urfave/cli.v1" + "github.com/ethereum/go-ethereum/internal/ethapi" ) const ( @@ -173,9 +170,6 @@ func init() { dumpCommand, // See monitorcmd.go: monitorCommand, - // See accountcmd.go: - accountCommand, - walletCommand, // See consolecmd.go: consoleCommand, attachCommand, @@ -261,59 +255,6 @@ func geth(ctx *cli.Context) error { return nil } -func startInternalAccountManagement(ctx *cli.Context, stack *node.Node) { - // Unlock any account specifically requested - ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - - passwords := utils.MakePasswordList(ctx) - unlocks := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",") - for i, account := range unlocks { - if trimmed := strings.TrimSpace(account); trimmed != "" { - unlockAccount(ctx, ks, trimmed, i, passwords) - } - } - // Register wallet event handlers to open and auto-derive wallets - events := make(chan accounts.WalletEvent, 16) - stack.AccountManager().Subscribe(events) - go func() { - // Create a chain state reader for self-derivation - rpcClient, err := stack.Attach() - if err != nil { - utils.Fatalf("Failed to attach to self: %v", err) - } - stateReader := ethclient.NewClient(rpcClient) - - // Open any wallets already attached - for _, wallet := range stack.AccountManager().Wallets() { - if err := wallet.Open(""); err != nil { - log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err) - } - } - // Listen for wallet event till termination - for event := range events { - switch event.Kind { - case accounts.WalletArrived: - if err := event.Wallet.Open(""); err != nil { - log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err) - } - case accounts.WalletOpened: - status, _ := event.Wallet.Status() - log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status) - - derivationPath := accounts.DefaultBaseDerivationPath - if event.Wallet.URL().Scheme == "ledger" { - derivationPath = accounts.DefaultLedgerBaseDerivationPath - } - event.Wallet.SelfDerive(derivationPath, stateReader) - - case accounts.WalletDropped: - log.Info("Old wallet dropped", "url", event.Wallet.URL()) - event.Wallet.Close() - } - } - }() -} - // startNode boots up the system node and all registered protocols, after which // it unlocks any requested accounts, and starts the RPC/IPC interfaces and the // miner. @@ -325,12 +266,14 @@ func startNode(ctx *cli.Context, stack *node.Node) { // Start account management if ctx.IsSet(utils.ExternalSignerFlag.Name){ - extApi := ctx.GlobalString(utils.ExternalSignerFlag.Name) - - }else{ - startInternalAccountManagement(ctx, stack) + extEndpoint := ctx.GlobalString(utils.ExternalSignerFlag.Name) + extApi, err := ethapi.NewExternalSigner(extEndpoint) + if err != nil{ + utils.Fatalf("Could not connect to external signer at %v: %v" , extEndpoint, err) + } + log.Info("External signer connected", "url", extEndpoint) + stack.SetExternalAPI(extApi) } - // Start auxiliary services if enabled if ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) { // Mining only makes sense if a full Ethereum node is running diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 475dd909fd..c7532dde1e 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -24,12 +24,10 @@ import ( "math/big" "os" "path/filepath" - "strconv" "strings" "time" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/consensus" @@ -792,34 +790,23 @@ func makeDatabaseHandles() int { // MakeAddress converts an account specified directly as a hex encoded string or // a key index in the key store to an internal account representation. -func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error) { +func MakeAddress(account string) (accounts.Account, error) { // If the specified account is a valid address, return it - if common.IsHexAddress(account) { - return accounts.Account{Address: common.HexToAddress(account)}, nil + ma, err := common.NewMixedcaseAddressFromString(account); + if err != nil { + return accounts.Account{}, fmt.Errorf("Not valid hex address (%v): %v", account, err) } - // Otherwise try to interpret the account as a keystore index - index, err := strconv.Atoi(account) - if err != nil || index < 0 { - return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account) + if !ma.ValidChecksum(){ + return accounts.Account{}, fmt.Errorf("Invalid checksum on address %v", account) } - log.Warn("-------------------------------------------------------------------") - log.Warn("Referring to accounts by order in the keystore folder is dangerous!") - log.Warn("This functionality is deprecated and will be removed in the future!") - log.Warn("Please use explicit addresses! (can search via `geth account list`)") - log.Warn("-------------------------------------------------------------------") - - accs := ks.Accounts() - if len(accs) <= index { - return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs)) - } - return accs[index], nil + return accounts.Account{Address: common.HexToAddress(account)}, nil } // setEtherbase retrieves the etherbase either from the directly specified // command line flags or from the keystore if CLI indexed. -func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) { +func setEtherbase(ctx *cli.Context, cfg *eth.Config) { if ctx.GlobalIsSet(EtherbaseFlag.Name) { - account, err := MakeAddress(ks, ctx.GlobalString(EtherbaseFlag.Name)) + account, err := MakeAddress(ctx.GlobalString(EtherbaseFlag.Name)) if err != nil { Fatalf("Option %q: %v", EtherbaseFlag.Name, err) } @@ -1061,8 +1048,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { checkExclusive(ctx, LightServFlag, LightModeFlag) checkExclusive(ctx, LightServFlag, SyncModeFlag, "light") - ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - setEtherbase(ctx, ks, cfg) + setEtherbase(ctx, cfg) setGPO(ctx, &cfg.GPO) setTxPool(ctx, &cfg.TxPool) setEthash(ctx, cfg) @@ -1135,24 +1121,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { cfg.NetworkId = 1337 } // Create new developer account or reuse existing one - var ( - developer accounts.Account - err error - ) - if accs := ks.Accounts(); len(accs) > 0 { - developer = ks.Accounts()[0] - } else { - developer, err = ks.NewAccount("") - if err != nil { - Fatalf("Failed to create developer account: %v", err) - } - } - if err := ks.Unlock(developer, ""); err != nil { - Fatalf("Failed to unlock developer account: %v", err) - } - log.Info("Using developer account", "address", developer.Address) - - cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address) + // TODO @holiman + Fatalf("Failed to create developer account: not implemented", ) if !ctx.GlobalIsSet(GasPriceFlag.Name) { cfg.GasPrice = big.NewInt(1) } diff --git a/eth/api_backend.go b/eth/api_backend.go index 03f6012d78..5b5f81cfa5 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -20,7 +20,6 @@ import ( "context" "math/big" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core" @@ -217,10 +216,6 @@ func (b *EthAPIBackend) EventMux() *event.TypeMux { return b.eth.EventMux() } -func (b *EthAPIBackend) AccountManager() *accounts.Manager { - return b.eth.AccountManager() -} - func (b *EthAPIBackend) BloomStatus() (uint64, uint64) { sections, _, _ := b.eth.bloomIndexer.Sections() return params.BloomBitsBlocks, sections @@ -231,3 +226,8 @@ func (b *EthAPIBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests) } } +func (b *EthAPIBackend) ExternalSigner() string { + return b.eth.externalSigner +} + + diff --git a/eth/backend.go b/eth/backend.go index a0212b9f1e..76fe24053d 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -25,7 +25,6 @@ import ( "sync" "sync/atomic" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" @@ -77,7 +76,6 @@ type Ethereum struct { eventMux *event.TypeMux engine consensus.Engine - accountManager *accounts.Manager externalSigner string bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests @@ -124,7 +122,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { chainDb: chainDb, chainConfig: chainConfig, eventMux: ctx.EventMux, - accountManager: ctx.AccountManager, engine: CreateConsensusEngine(ctx, chainConfig, &config.Ethash, config.MinerNotify, chainDb), shutdownChan: make(chan bool), networkID: config.NetworkId, @@ -310,18 +307,6 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) { if etherbase != (common.Address{}) { return etherbase, nil } - if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { - if accounts := wallets[0].Accounts(); len(accounts) > 0 { - etherbase := accounts[0].Address - - s.lock.Lock() - s.etherbase = etherbase - s.lock.Unlock() - - log.Info("Etherbase automatically configured", "address", etherbase) - return etherbase, nil - } - } return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") } @@ -340,13 +325,13 @@ func (s *Ethereum) StartMining(local bool) error { log.Error("Cannot start mining without etherbase", "err", err) return fmt.Errorf("etherbase missing: %v", err) } - if clique, ok := s.engine.(*clique.Clique); ok { - wallet, err := s.accountManager.Find(accounts.Account{Address: eb}) - if wallet == nil || err != nil { - log.Error("Etherbase account unavailable locally", "err", err) - return fmt.Errorf("signer missing: %v", err) - } - clique.Authorize(eb, wallet.SignHash) + if _, ok := s.engine.(*clique.Clique); ok { + + //TODO @holiman + log.Error("Etherbase account unavailable locally", "err", err) + return fmt.Errorf("signer not configured: %v", err) + + } if local { // If local (CPU) mining is started, we can disable the transaction rejection @@ -363,7 +348,6 @@ func (s *Ethereum) StopMining() { s.miner.Stop() } func (s *Ethereum) IsMining() bool { return s.miner.Mining() } func (s *Ethereum) Miner() *miner.Miner { return s.miner } -func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f70bbda97c..02245128bf 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -177,33 +177,10 @@ func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string]string { return content } -// PublicAccountAPI provides an API to access accounts managed by this node. -// It offers only methods that can retrieve accounts. -type PublicAccountAPI struct { - am *accounts.Manager -} - -// NewPublicAccountAPI creates a new PublicAccountAPI. -func NewPublicAccountAPI(am *accounts.Manager) *PublicAccountAPI { - return &PublicAccountAPI{am: am} -} - -// Accounts returns the collection of accounts this node manages -func (s *PublicAccountAPI) Accounts() []common.Address { - addresses := make([]common.Address, 0) // return [] instead of nil if empty - for _, wallet := range s.am.Wallets() { - for _, account := range wallet.Accounts() { - addresses = append(addresses, account.Address) - } - } - return addresses -} - // PrivateAccountAPI provides an API to access accounts managed by this node. // It offers methods to create, (un)lock en list accounts. Some methods accept // passwords and are therefore considered private by default. type PrivateAccountAPI struct { - am *accounts.Manager nonceLock *AddrLocker b Backend extapi *ExternalSignerAPI // external signer, nil if not used @@ -212,7 +189,6 @@ type PrivateAccountAPI struct { // NewPrivateAccountAPI create a new PrivateAccountAPI. func NewPrivateAccountAPI(b Backend, nonceLock *AddrLocker) *PrivateAccountAPI { p := &PrivateAccountAPI{ - am: b.AccountManager(), nonceLock: nonceLock, b: b, } @@ -236,11 +212,6 @@ func (s *PrivateAccountAPI) ListAccounts() []common.Address { } return addresses } - for _, wallet := range s.am.Wallets() { - for _, account := range wallet.Accounts() { - addresses = append(addresses, account.Address) - } - } return addresses } @@ -253,78 +224,9 @@ type rawWallet struct { Accounts []accounts.Account `json:"accounts,omitempty"` } -// ListWallets will return a list of wallets this node manages. -func (s *PrivateAccountAPI) ListWallets() []rawWallet { - wallets := make([]rawWallet, 0) // return [] instead of nil if empty - if s.extapi != nil { - return wallets - } - for _, wallet := range s.am.Wallets() { - status, failure := wallet.Status() - - raw := rawWallet{ - URL: wallet.URL().String(), - Status: status, - Accounts: wallet.Accounts(), - } - if failure != nil { - raw.Failure = failure.Error() - } - wallets = append(wallets, raw) - } - return wallets -} - -// OpenWallet initiates a hardware wallet opening procedure, establishing a USB -// connection and attempting to authenticate via the provided passphrase. Note, -// the method may return an extra challenge requiring a second open (e.g. the -// Trezor PIN matrix challenge). -func (s *PrivateAccountAPI) OpenWallet(url string, passphrase *string) error { - if s.extapi != nil { - return errors.New("openWallet disabled when external signer is used") - } - - wallet, err := s.am.Wallet(url) - if err != nil { - return err - } - pass := "" - if passphrase != nil { - pass = *passphrase - } - return wallet.Open(pass) -} - -// DeriveAccount requests a HD wallet to derive a new account, optionally pinning -// it for later reuse. -func (s *PrivateAccountAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) { - if s.extapi != nil { - return accounts.Account{}, errors.New("deriveAccount disabled when external signer is used") - } - wallet, err := s.am.Wallet(url) - if err != nil { - return accounts.Account{}, err - } - derivPath, err := accounts.ParseDerivationPath(path) - if err != nil { - return accounts.Account{}, err - } - if pin == nil { - pin = new(bool) - } - return wallet.Derive(derivPath, *pin) -} - // NewAccount will create a new account and returns the address for the new account. func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) { - if s.extapi != nil { - return s.extapi.newAccount() - } - acc, err := fetchKeystore(s.am).NewAccount(password) - if err == nil { - return acc.Address, nil - } - return common.Address{}, err + return s.extapi.newAccount() } // fetchKeystore retrives the encrypted keystore from the account manager. @@ -335,44 +237,19 @@ func fetchKeystore(am *accounts.Manager) *keystore.KeyStore { // ImportRawKey stores the given hex encoded ECDSA key into the key directory, // encrypting it with the passphrase. func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) { - if s.extapi != nil { - return common.Address{}, errors.New("Importing of raw keys is disabled when external signer is used.") - } - - key, err := crypto.HexToECDSA(privkey) - if err != nil { - return common.Address{}, err - } - acc, err := fetchKeystore(s.am).ImportECDSA(key, password) - return acc.Address, err + return common.Address{}, errors.New("Importing of raw keys is disabled when external signer is used.") } // UnlockAccount will unlock the account associated with the given address with // the given password for duration seconds. If duration is nil it will use a // default of 300 seconds. It returns an indication if the account was unlocked. func (s *PrivateAccountAPI) UnlockAccount(addr common.Address, password string, duration *uint64) (bool, error) { - if s.extapi != nil { - return false, errors.New("Unlock is disabled when external signer is used.") - } - const max = uint64(time.Duration(math.MaxInt64) / time.Second) - var d time.Duration - if duration == nil { - d = 300 * time.Second - } else if *duration > max { - return false, errors.New("unlock duration too large") - } else { - d = time.Duration(*duration) * time.Second - } - err := fetchKeystore(s.am).TimedUnlock(accounts.Account{Address: addr}, password, d) - return err == nil, err + return false, errors.New("Unlock is disabled when external signer is used.") } // LockAccount will lock the account associated with the given address when it's unlocked. func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool { - if s.extapi != nil { - return false - } - return fetchKeystore(s.am).Lock(addr) == nil + return false } // signTransactions sets defaults and signs the given transaction @@ -383,24 +260,7 @@ func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args SendTxArgs if err := args.setDefaults(ctx, s.b); err != nil { return nil, err } - if s.extapi != nil { - return s.extapi.signTransaction(ctx, args) - } - - // Look up the wallet containing the requested signer - account := accounts.Account{Address: args.From} - wallet, err := s.am.Find(account) - if err != nil { - return nil, err - } - // Assemble the transaction and sign with the wallet - tx := args.toTransaction() - - var chainID *big.Int - if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) { - chainID = config.ChainID - } - return wallet.SignTxWithPassphrase(account, passwd, tx, chainID) + return s.extapi.signTransaction(ctx, args) } // SendTransaction will create a transaction from the given arguments and @@ -459,32 +319,6 @@ func signHash(data []byte) []byte { return crypto.Keccak256([]byte(msg)) } -// Sign calculates an Ethereum ECDSA signature for: -// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message)) -// -// Note, the produced signature conforms to the secp256k1 curve R, S and V values, -// where the V value will be 27 or 28 for legacy reasons. -// -// The key used to calculate the signature is decrypted with the given password. -// -// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign -func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) { - // Look up the wallet containing the requested signer - account := accounts.Account{Address: addr} - - wallet, err := s.b.AccountManager().Find(account) - if err != nil { - return nil, err - } - // Assemble sign the data with the wallet - signature, err := wallet.SignHashWithPassphrase(account, passwd, signHash(data)) - if err != nil { - return nil, err - } - signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper - return signature, nil -} - // EcRecover returns the address for the account that was used to create the signature. // Note, this function is compatible with eth_sign and personal_sign. As such it recovers // the address of: @@ -514,7 +348,7 @@ func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Byt // SignAndSendTransaction was renamed to SendTransaction. This method is deprecated // and will be removed in the future. It primary goal is to give clients time to update. func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { - return s.SendTransaction(ctx, args, passwd) + return common.Hash{}, errors.New("SignAndSend is deprecated, please use sendTransaction instead") } // PublicBlockChainAPI provides an API to access the Ethereum blockchain. @@ -661,15 +495,8 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr if state == nil || err != nil { return nil, 0, false, err } - // Set sender address or use a default if none specified + // Set sender address addr := args.From - if addr == (common.Address{}) { - if wallets := s.b.AccountManager().Wallets(); len(wallets) > 0 { - if accts := wallets[0].Accounts(); len(accts) > 0 { - addr = accts[0].Address - } - } - } // Set default gas & gas price if none were set gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt() if gas == 0 { @@ -1145,23 +972,6 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha return fields, nil } -// sign is a helper function that signs a transaction with the private key of the given address. -func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { - // Look up the wallet containing the requested signer - account := accounts.Account{Address: addr} - - wallet, err := s.b.AccountManager().Find(account) - if err != nil { - return nil, err - } - // Request the wallet to sign the transaction - var chainID *big.Int - if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) { - chainID = config.ChainID - } - return wallet.SignTx(account, tx, chainID) -} - // SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool. type SendTxArgs struct { From common.Address `json:"from"` @@ -1292,19 +1102,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod // // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data hexutil.Bytes) (hexutil.Bytes, error) { - // Look up the wallet containing the requested signer - account := accounts.Account{Address: addr} - - wallet, err := s.b.AccountManager().Find(account) - if err != nil { - return nil, err - } - // Sign the requested hash with the wallet - signature, err := wallet.SignHash(account, signHash(data)) - if err == nil { - signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper - } - return signature, err + return nil, errors.New("Sign data not implemented") } // SignTransactionResult represents a RLP encoded signed transaction. @@ -1333,11 +1131,9 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sen tx *types.Transaction err error ) - if s.extapi != nil { - tx, err = s.extapi.signTransaction(ctx, args) - } else { - tx, err = s.sign(args.From, args.toTransaction()) - } + + tx, err = s.extapi.signTransaction(ctx, args) + if err != nil { return nil, err } @@ -1351,28 +1147,9 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sen // PendingTransactions returns the transactions that are in the transaction pool // and have a from address that is one of the accounts this node manages. func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, error) { - pending, err := s.b.GetPoolTransactions() - if err != nil { - return nil, err - } - accounts := make(map[common.Address]struct{}) - for _, wallet := range s.b.AccountManager().Wallets() { - for _, account := range wallet.Accounts() { - accounts[account.Address] = struct{}{} - } - } - transactions := make([]*RPCTransaction, 0, len(pending)) - for _, tx := range pending { - var signer types.Signer = types.HomesteadSigner{} - if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) - } - from, _ := types.Sender(signer, tx) - if _, exists := accounts[from]; exists { - transactions = append(transactions, newRPCPendingTransaction(tx)) - } - } - return transactions, nil + return nil, errors.New("Not implemented") + // TODO @holiman, make this call take a list of addresses, and show the penading transactions that are from that + // set of addresses } // Resend accepts an existing transaction and a new gas price and limit. It will remove @@ -1407,11 +1184,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr if gasLimit != nil && *gasLimit != 0 { sendArgs.Gas = gasLimit } - if s.extapi != nil { - signedTx, err = s.extapi.signTransaction(ctx, sendArgs) - } else { - signedTx, err = s.sign(sendArgs.From, sendArgs.toTransaction()) - } + signedTx, err = s.extapi.signTransaction(ctx, sendArgs) if err != nil { return common.Hash{}, err } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index f7946b367f..6d7ca3efe4 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -21,7 +21,6 @@ import ( "context" "math/big" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" @@ -43,7 +42,6 @@ type Backend interface { SuggestPrice(ctx context.Context) (*big.Int, error) ChainDb() ethdb.Database EventMux() *event.TypeMux - AccountManager() *accounts.Manager ExternalSigner() string // BlockChain API @@ -104,12 +102,7 @@ func GetAPIs(apiBackend Backend) []rpc.API { Namespace: "debug", Version: "1.0", Service: NewPrivateDebugAPI(apiBackend), - }, { - Namespace: "eth", - Version: "1.0", - Service: NewPublicAccountAPI(apiBackend.AccountManager()), - Public: true, - }, { + },{ Namespace: "personal", Version: "1.0", Service: NewPrivateAccountAPI(apiBackend, nonceLock), diff --git a/les/api_backend.go b/les/api_backend.go index bd5a9b873b..136d41850e 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -20,7 +20,6 @@ import ( "context" "math/big" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core" @@ -183,10 +182,6 @@ func (b *LesApiBackend) EventMux() *event.TypeMux { return b.eth.eventMux } -func (b *LesApiBackend) AccountManager() *accounts.Manager { - return b.eth.accountManager -} - func (b *LesApiBackend) ExternalSigner() string { return b.eth.externalSigner } diff --git a/les/backend.go b/les/backend.go index 3aaafa4c88..df455537af 100644 --- a/les/backend.go +++ b/les/backend.go @@ -22,7 +22,6 @@ import ( "sync" "time" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" @@ -72,7 +71,6 @@ type LightEthereum struct { eventMux *event.TypeMux engine consensus.Engine - accountManager *accounts.Manager externalSigner string networkId uint64 @@ -102,7 +100,6 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { eventMux: ctx.EventMux, peers: peers, reqDist: newRequestDistributor(peers, quitSync), - accountManager: ctx.AccountManager, engine: eth.CreateConsensusEngine(ctx, chainConfig, &config.Ethash, nil, chainDb), shutdownChan: make(chan bool), networkId: config.NetworkId, diff --git a/node/node.go b/node/node.go index 429cec89d1..faf6787255 100644 --- a/node/node.go +++ b/node/node.go @@ -26,7 +26,6 @@ import ( "strings" "sync" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/debug" @@ -34,16 +33,15 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" "github.com/prometheus/prometheus/util/flock" + "github.com/ethereum/go-ethereum/internal/ethapi" ) // Node is a container on which services can be registered. type Node struct { eventmux *event.TypeMux // Event multiplexer used between the services of a stack config *Config - accman *accounts.Manager - extapi string // If non-empty, the ipc-path or http(s)-url to an external signer + extapi *ethapi.ExternalSignerAPI// If non-empty, the ipc-path or http(s)-url to an external signer - ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop instanceDirLock flock.Releaser // prevents concurrent use of instance directory serverConfig p2p.Config @@ -98,27 +96,12 @@ func New(conf *Config) (*Node, error) { if strings.HasSuffix(conf.Name, ".ipc") { return nil, errors.New(`Config.Name cannot end in ".ipc"`) } - // Ensure that the AccountManager method works before the node has started. - // We rely on this in cmd/geth. - var ( - am *accounts.Manager - ephemeralKeystore string - ) - if conf.ExternalSigner != "" { - var err error - if am, ephemeralKeystore, err = makeAccountManager(conf); err != nil { - return nil, err - } - } if conf.Logger == nil { conf.Logger = log.New() } // Note: any interaction with Config that would create/touch files // in the data directory or instance directory is delayed until Start. return &Node{ - accman: am, - extapi: conf.ExternalSigner, - ephemeralKeystore: ephemeralKeystore, config: conf, serviceFuncs: []ServiceConstructor{}, ipcEndpoint: conf.IPCEndpoint(), @@ -181,7 +164,6 @@ func (n *Node) Start() error { config: n.config, services: make(map[reflect.Type]Service), EventMux: n.eventmux, - AccountManager: n.accman, } for kind, s := range services { // copy needed for threaded access ctx.services[kind] = s @@ -443,18 +425,9 @@ func (n *Node) Stop() error { // unblock n.Wait close(n.stop) - // Remove the keystore if it was created ephemerally. - var keystoreErr error - if n.ephemeralKeystore != "" { - keystoreErr = os.RemoveAll(n.ephemeralKeystore) - } - if len(failure.Services) > 0 { return failure } - if keystoreErr != nil { - return keystoreErr - } return nil } @@ -545,11 +518,6 @@ func (n *Node) InstanceDir() string { return n.config.instanceDir() } -// AccountManager retrieves the account manager used by the protocol stack. -func (n *Node) AccountManager() *accounts.Manager { - return n.accman -} - // IPCEndpoint retrieves the current IPC endpoint used by the protocol stack. func (n *Node) IPCEndpoint() string { return n.ipcEndpoint @@ -586,6 +554,10 @@ func (n *Node) ResolvePath(x string) string { return n.config.ResolvePath(x) } +func (n *Node) SetExternalAPI(api *ethapi.ExternalSignerAPI){ + n.extapi = api +} + // apis returns the collection of RPC descriptors this node offers. func (n *Node) apis() []rpc.API { return []rpc.API{ diff --git a/node/service.go b/node/service.go index 6a96d9b1e1..e2e38c5542 100644 --- a/node/service.go +++ b/node/service.go @@ -19,7 +19,6 @@ package node import ( "reflect" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" @@ -33,7 +32,6 @@ type ServiceContext struct { config *Config services map[reflect.Type]Service // Index of the already constructed services EventMux *event.TypeMux // Event multiplexer used for decoupled notifications - AccountManager *accounts.Manager // Account manager created by the node. } // OpenDatabase opens an existing database with the given name (or creates one diff --git a/signer/core/cliui.go b/signer/core/cliui.go index 3cbbe57959..c8e05f1f2f 100644 --- a/signer/core/cliui.go +++ b/signer/core/cliui.go @@ -190,7 +190,7 @@ func (ui *CommandlineUI) ApproveImport(request *ImportRequest) (ImportResponse, // ApproveListing prompt the user for confirmation to list accounts // the list of accounts to list can be modified by the UI -func (ui *CommandlineUI) ApproveListing(request *ListRequest) (ListAccountsResponse, error) { +func (ui *CommandlineUI) ApproveListing(request *ListAccountsRequest) (ListAccountsResponse, error) { ui.mu.Lock() defer ui.mu.Unlock() @@ -199,7 +199,7 @@ func (ui *CommandlineUI) ApproveListing(request *ListRequest) (ListAccountsRespo fmt.Printf("A request has been made to list all accounts. \n") fmt.Printf("You can select which accounts the caller can see\n") for _, account := range request.Accounts { - fmt.Printf("\t[x] %v\n", account.Address.Hex()) + fmt.Printf("\t[x] %v\n", account.Hex()) } fmt.Printf("-------------------------------------------\n") showMetadata(request.Meta) diff --git a/signer/core/stdioui.go b/signer/core/stdioui.go index d240e46267..1193f997d8 100644 --- a/signer/core/stdioui.go +++ b/signer/core/stdioui.go @@ -73,7 +73,7 @@ func (ui *StdIOUI) ApproveImport(request *ImportRequest) (ImportResponse, error) return result, err } -func (ui *StdIOUI) ApproveListing(request *ListRequest) (ListAccountsResponse, error) { +func (ui *StdIOUI) ApproveListing(request *ListAccountsRequest) (ListAccountsResponse, error) { var result ListAccountsResponse err := ui.dispatch("ApproveListing", request, &result) return result, err