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/config.go b/cmd/geth/config.go
index b0749d2329..c0842c4798 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -152,9 +152,7 @@ func enableWhisper(ctx *cli.Context) bool {
func makeFullNode(ctx *cli.Context) *node.Node {
stack, cfg := makeConfigNode(ctx)
-
utils.RegisterEthService(stack, &cfg.Eth)
-
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
utils.RegisterDashboardService(stack, &cfg.Dashboard, gitCommit)
}
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index fae4b57181..07e8b5bda6 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 (
@@ -54,13 +51,10 @@ var (
// flags that configure the node
nodeFlags = []cli.Flag{
utils.IdentityFlag,
- utils.UnlockedAccountFlag,
- utils.PasswordFileFlag,
utils.BootnodesFlag,
utils.BootnodesV4Flag,
utils.BootnodesV5Flag,
utils.DataDirFlag,
- utils.KeyStoreDirFlag,
utils.NoUSBFlag,
utils.DashboardEnabledFlag,
utils.DashboardAddrFlag,
@@ -73,6 +67,7 @@ var (
utils.EthashDatasetsInMemoryFlag,
utils.EthashDatasetsOnDiskFlag,
utils.TxPoolLocalsFlag,
+ utils.ExternalSignerFlag,
utils.TxPoolNoLocalsFlag,
utils.TxPoolJournalFlag,
utils.TxPoolRejournalFlag,
@@ -183,9 +178,6 @@ func init() {
dumpCommand,
// See monitorcmd.go:
monitorCommand,
- // See accountcmd.go:
- accountCommand,
- walletCommand,
// See consolecmd.go:
consoleCommand,
attachCommand,
@@ -276,60 +268,19 @@ func geth(ctx *cli.Context) error {
func startNode(ctx *cli.Context, stack *node.Node) {
debug.Memsize.Add("node", stack)
+ // Start account management
+ if extEndpoint := ctx.GlobalString(utils.ExternalSignerFlag.Name); extEndpoint != ""{
+ 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 up the node itself
utils.StartNode(stack)
- // 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()
- }
- }
- }()
// 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/geth/usage.go b/cmd/geth/usage.go
index 8b0491ce3c..2c6fa3829a 100644
--- a/cmd/geth/usage.go
+++ b/cmd/geth/usage.go
@@ -69,7 +69,6 @@ var AppHelpFlagGroups = []flagGroup{
Flags: []cli.Flag{
configFileFlag,
utils.DataDirFlag,
- utils.KeyStoreDirFlag,
utils.NoUSBFlag,
utils.NetworkIdFlag,
utils.TestnetFlag,
@@ -136,13 +135,6 @@ var AppHelpFlagGroups = []flagGroup{
utils.TrieCacheGenFlag,
},
},
- {
- Name: "ACCOUNT",
- Flags: []cli.Flag{
- utils.UnlockedAccountFlag,
- utils.PasswordFileFlag,
- },
- },
{
Name: "API AND CONSOLE",
Flags: []cli.Flag{
diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go
index 88e6b0b0be..28e78f920b 100644
--- a/cmd/swarm/main.go
+++ b/cmd/swarm/main.go
@@ -565,7 +565,6 @@ pv(1) tool to get a progress bar:
utils.NATFlag,
utils.IPCDisabledFlag,
utils.IPCPathFlag,
- utils.PasswordFileFlag,
// bzzd-specific flags
CorsStringFlag,
EnsAPIFlag,
@@ -722,11 +721,8 @@ func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.Pr
log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
return key
}
- // Otherwise try getting it from the keystore.
- am := stack.AccountManager()
- ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
-
- return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx))
+ utils.Fatalf(SWARM_ERR_NO_BZZACCOUNT)
+ return nil
}
// getPrivKey returns the private key of the specified bzzaccount
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 429c2bbb9b..ebeca0bbab 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -20,16 +20,13 @@ package utils
import (
"crypto/ecdsa"
"fmt"
- "io/ioutil"
"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"
@@ -379,17 +376,15 @@ var (
Usage: "Disable remote sealing verification",
}
// Account settings
- UnlockedAccountFlag = cli.StringFlag{
- Name: "unlock",
- Usage: "Comma separated list of accounts to unlock",
- Value: "",
- }
PasswordFileFlag = cli.StringFlag{
Name: "password",
Usage: "Password file to use for non-interactive password input",
+ }
+ ExternalSignerFlag = cli.StringFlag{
+ Name: "signer",
+ Usage: "External signer (url or path to ipc file)",
Value: "",
}
-
VMEnableDebugFlag = cli.BoolFlag{
Name: "vmdebug",
Usage: "Record information useful for VM and contract debugging",
@@ -836,32 +831,21 @@ 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) {
// Extract the current etherbase, new flag overriding legacy one
var etherbase string
if ctx.GlobalIsSet(MinerLegacyEtherbaseFlag.Name) {
@@ -872,7 +856,7 @@ func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) {
}
// Convert the etherbase into an address and configure it
if etherbase != "" {
- account, err := MakeAddress(ks, etherbase)
+ account, err := MakeAddress(etherbase)
if err != nil {
Fatalf("Invalid miner etherbase: %v", err)
}
@@ -880,24 +864,6 @@ func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) {
}
}
-// MakePasswordList reads password lines from the file specified by the global --password flag.
-func MakePasswordList(ctx *cli.Context) []string {
- path := ctx.GlobalString(PasswordFileFlag.Name)
- if path == "" {
- return nil
- }
- text, err := ioutil.ReadFile(path)
- if err != nil {
- Fatalf("Failed to read password file: %v", err)
- }
- lines := strings.Split(string(text), "\n")
- // Sanitise DOS line endings.
- for i := range lines {
- lines[i] = strings.TrimRight(lines[i], "\r")
- }
- return lines
-}
-
func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
setNodeKey(ctx, cfg)
setNAT(ctx, cfg)
@@ -1128,8 +1094,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
checkExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag)
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)
@@ -1223,24 +1188,9 @@ 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")
+ //cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address)
if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) && !ctx.GlobalIsSet(MinerLegacyGasPriceFlag.Name) {
cfg.MinerGasPrice = big.NewInt(1)
}
@@ -1267,7 +1217,7 @@ func RegisterEthService(stack *node.Node, cfg *eth.Config) {
})
} else {
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
- fullNode, err := eth.New(ctx, cfg)
+ fullNode, err := eth.New(ctx, cfg, stack.ExternalSignerAPI())
if fullNode != nil && cfg.LightServ > 0 {
ls, _ := les.NewLesServer(fullNode, cfg)
fullNode.AddLesServer(ls)
diff --git a/console/console.go b/console/console.go
index 3c397f8006..c0fe72fe68 100644
--- a/console/console.go
+++ b/console/console.go
@@ -159,37 +159,6 @@ func (c *Console) init(preload []string) error {
// Initialize the global name register (disabled for now)
//c.jsre.Run(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`)
- // If the console is in interactive mode, instrument password related methods to query the user
- if c.prompter != nil {
- // Retrieve the account management object to instrument
- personal, err := c.jsre.Get("personal")
- if err != nil {
- return err
- }
- // Override the openWallet, unlockAccount, newAccount and sign methods since
- // these require user interaction. Assign these method in the Console the
- // original web3 callbacks. These will be called by the jeth.* methods after
- // they got the password from the user and send the original web3 request to
- // the backend.
- if obj := personal.Object(); obj != nil { // make sure the personal api is enabled over the interface
- if _, err = c.jsre.Run(`jeth.openWallet = personal.openWallet;`); err != nil {
- return fmt.Errorf("personal.openWallet: %v", err)
- }
- if _, err = c.jsre.Run(`jeth.unlockAccount = personal.unlockAccount;`); err != nil {
- return fmt.Errorf("personal.unlockAccount: %v", err)
- }
- if _, err = c.jsre.Run(`jeth.newAccount = personal.newAccount;`); err != nil {
- return fmt.Errorf("personal.newAccount: %v", err)
- }
- if _, err = c.jsre.Run(`jeth.sign = personal.sign;`); err != nil {
- return fmt.Errorf("personal.sign: %v", err)
- }
- obj.Set("openWallet", bridge.OpenWallet)
- obj.Set("unlockAccount", bridge.UnlockAccount)
- obj.Set("newAccount", bridge.NewAccount)
- obj.Set("sign", bridge.Sign)
- }
- }
// The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer.
admin, err := c.jsre.Get("admin")
if err != nil {
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 8748d444fa..6609d6badf 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"
@@ -34,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
)
// EthAPIBackend implements ethapi.Backend for full nodes
@@ -209,10 +209,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
@@ -223,3 +219,8 @@ func (b *EthAPIBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma
go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests)
}
}
+func (b *EthAPIBackend) ExternalSigner() *ethapi.ExternalSignerAPI {
+ return b.eth.externalSigner
+}
+
+
diff --git a/eth/backend.go b/eth/backend.go
index b555b064ad..f53c0c8c0d 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,7 @@ type Ethereum struct {
eventMux *event.TypeMux
engine consensus.Engine
- accountManager *accounts.Manager
+ externalSigner *ethapi.ExternalSignerAPI
bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports
@@ -101,7 +100,7 @@ func (s *Ethereum) AddLesServer(ls LesServer) {
// New creates a new Ethereum object (including the
// initialisation of the common Ethereum object)
-func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
+func New(ctx *node.ServiceContext, config *Config, api *ethapi.ExternalSignerAPI) (*Ethereum, error) {
// Ensure configuration values are compatible and sane
if config.SyncMode == downloader.LightSync {
return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum")
@@ -126,10 +125,10 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
eth := &Ethereum{
config: config,
+ externalSigner: api,
chainDb: chainDb,
chainConfig: chainConfig,
eventMux: ctx.EventMux,
- accountManager: ctx.AccountManager,
engine: CreateConsensusEngine(ctx, chainConfig, &config.Ethash, config.MinerNotify, config.MinerNoverify, chainDb),
shutdownChan: make(chan bool),
networkID: config.NetworkId,
@@ -319,18 +318,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")
}
@@ -426,13 +413,10 @@ func (s *Ethereum) StartMining(threads int) 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 mining is started, we can disable the transaction rejection mechanism
// introduced to speed sync times.
@@ -460,16 +444,16 @@ func (s *Ethereum) StopMining() {
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 }
-func (s *Ethereum) Engine() consensus.Engine { return s.engine }
-func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
-func (s *Ethereum) IsListening() bool { return true } // Always listening
-func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
-func (s *Ethereum) NetVersion() uint64 { return s.networkID }
-func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
+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 }
+func (s *Ethereum) Engine() consensus.Engine { return s.engine }
+func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
+func (s *Ethereum) IsListening() bool { return true } // Always listening
+func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
+func (s *Ethereum) NetVersion() uint64 { return s.networkID }
+func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
+func (s *Ethereum) ExternalSigner() *ethapi.ExternalSignerAPI { return s.externalSigner }
// Protocols implements node.Service, returning all the currently configured
// network protocols to start.
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 8016ebe3d7..4242a8b106 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -177,53 +177,33 @@ 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
}
// NewPrivateAccountAPI create a new PrivateAccountAPI.
func NewPrivateAccountAPI(b Backend, nonceLock *AddrLocker) *PrivateAccountAPI {
- return &PrivateAccountAPI{
- am: b.AccountManager(),
+ p := &PrivateAccountAPI{
nonceLock: nonceLock,
b: b,
+ extapi: b.ExternalSigner(),
}
+ return p
}
// ListAccounts will return a list of addresses for accounts this node manages.
func (s *PrivateAccountAPI) ListAccounts() []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)
+ if s.extapi != nil {
+ if accounts, err := s.extapi.listAccounts(); err == nil {
+ return accounts
}
+ return addresses
}
return addresses
}
@@ -237,65 +217,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
- 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 {
- 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) {
- 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) {
- 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.
@@ -306,58 +230,30 @@ 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) {
- 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) {
- 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 {
- return fetchKeystore(s.am).Lock(addr) == nil
+ return false
}
// signTransactions sets defaults and signs the given transaction
// NOTE: the caller needs to ensure that the nonceLock is held, if applicable,
// and release it after the transaction has been submitted to the tx pool
func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args SendTxArgs, passwd string) (*types.Transaction, error) {
- // 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
- }
// Set some sanity defaults and terminate on failure
if err := args.setDefaults(ctx, s.b); 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
@@ -416,32 +312,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:
@@ -471,7 +341,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.
@@ -618,15 +488,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 accounts := wallets[0].Accounts(); len(accounts) > 0 {
- addr = accounts[0].Address
- }
- }
- }
// Set default gas & gas price if none were set
gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt()
if gas == 0 {
@@ -943,11 +806,12 @@ func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransa
type PublicTransactionPoolAPI struct {
b Backend
nonceLock *AddrLocker
+ extapi *ExternalSignerAPI // external signer, nil if not used
}
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransactionPoolAPI {
- return &PublicTransactionPoolAPI{b, nonceLock}
+ return &PublicTransactionPoolAPI{b, nonceLock, b.ExternalSigner()}
}
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
@@ -1090,23 +954,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"`
@@ -1197,34 +1044,20 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c
// SendTransaction creates a transaction for the given argument, sign it and submit it to the
// transaction pool.
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
-
- // Look up the wallet containing the requested signer
- account := accounts.Account{Address: args.From}
-
- wallet, err := s.b.AccountManager().Find(account)
- if err != nil {
- return common.Hash{}, err
+ if s.extapi == nil {
+ return common.Hash{}, errors.New("No external signer configured")
}
-
if args.Nonce == nil {
// Hold the addresse's mutex around signing to prevent concurrent assignment of
// the same nonce to multiple accounts.
s.nonceLock.LockAddr(args.From)
defer s.nonceLock.UnlockAddr(args.From)
}
-
// Set some sanity defaults and terminate on failure
if err := args.setDefaults(ctx, s.b); err != nil {
return common.Hash{}, 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
- }
- signed, err := wallet.SignTx(account, tx, chainID)
+ signed, err := s.extapi.signTransaction(ctx, args)
if err != nil {
return common.Hash{}, err
}
@@ -1251,19 +1084,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.
@@ -1288,7 +1109,13 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sen
if err := args.setDefaults(ctx, s.b); err != nil {
return nil, err
}
- tx, err := s.sign(args.From, args.toTransaction())
+ var (
+ tx *types.Transaction
+ err error
+ )
+
+ tx, err = s.extapi.signTransaction(ctx, args)
+
if err != nil {
return nil, err
}
@@ -1302,28 +1129,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
@@ -1340,7 +1148,9 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
if err != nil {
return common.Hash{}, err
}
-
+ var (
+ signedTx *types.Transaction
+ )
for _, p := range pending {
var signer types.Signer = types.HomesteadSigner{}
if p.Protected() {
@@ -1356,7 +1166,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
if gasLimit != nil && *gasLimit != 0 {
sendArgs.Gas = gasLimit
}
- signedTx, err := s.sign(sendArgs.From, sendArgs.toTransaction())
+ signedTx, err = s.extapi.signTransaction(ctx, sendArgs)
if err != nil {
return common.Hash{}, err
}
@@ -1489,3 +1299,53 @@ func (s *PublicNetAPI) PeerCount() hexutil.Uint {
func (s *PublicNetAPI) Version() string {
return fmt.Sprintf("%d", s.networkVersion)
}
+
+// ExternalSignerAPI provides an API to interact with an external signer (clef)
+// It proxies request to the external signer while forwarding relevant
+// request headers
+type ExternalSignerAPI struct {
+ client *rpc.Client
+}
+
+func NewExternalSigner(endpoint string) (*ExternalSignerAPI, error) {
+ client, err := rpc.DialHTTP(endpoint)
+ if err != nil {
+ return nil, err
+ }
+ return &ExternalSignerAPI{
+ client: client,
+ }, nil
+}
+
+func (api *ExternalSignerAPI) signTransaction(ctx context.Context, args SendTxArgs) (*types.Transaction, error) {
+ if api == nil{
+ return nil, errors.New("External API not initialized")
+ }
+ res := SignTransactionResult{}
+ if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
+ return nil, err
+ }
+ return res.Tx, nil
+}
+func (api *ExternalSignerAPI) listAccounts() ([]common.Address, error) {
+ if api == nil{
+ return []common.Address{}, errors.New("External API not initialized")
+ }
+ var res []common.Address
+ if err := api.client.Call(&res, "account_listAccounts"); err != nil {
+ return nil, err
+ }
+ return res, nil
+}
+func (api *ExternalSignerAPI) newAccount() (common.Address, error) {
+ if api == nil{
+ return common.Address{}, errors.New("External API not initialized")
+ }
+ var res accounts.Account
+
+ if err := api.client.Call(&res, "account_new"); err != nil {
+ return res.Address, nil
+ }
+ return common.Address{}, nil
+
+}
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index c9ffe230c6..2e785b853c 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,8 +42,8 @@ type Backend interface {
SuggestPrice(ctx context.Context) (*big.Int, error)
ChainDb() ethdb.Database
EventMux() *event.TypeMux
- AccountManager() *accounts.Manager
-
+ ExternalSigner() *ExternalSignerAPI
+
// BlockChain API
SetHead(number uint64)
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
@@ -103,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 aa748a4ea1..2e1dfc5342 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"
@@ -36,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
)
type LesApiBackend struct {
@@ -183,8 +183,8 @@ func (b *LesApiBackend) EventMux() *event.TypeMux {
return b.eth.eventMux
}
-func (b *LesApiBackend) AccountManager() *accounts.Manager {
- return b.eth.accountManager
+func (b *LesApiBackend) ExternalSigner() *ethapi.ExternalSignerAPI{
+ return b.eth.externalSigner
}
func (b *LesApiBackend) BloomStatus() (uint64, uint64) {
diff --git a/les/backend.go b/les/backend.go
index a3474a6830..5ebcf977da 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"
@@ -69,7 +68,7 @@ type LightEthereum struct {
eventMux *event.TypeMux
engine consensus.Engine
- accountManager *accounts.Manager
+ externalSigner *ethapi.ExternalSignerAPI
networkId uint64
netRPCService *ethapi.PublicNetAPI
@@ -101,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, false, chainDb),
shutdownChan: make(chan bool),
networkId: config.NetworkId,
diff --git a/node/config.go b/node/config.go
index 8f10f4f613..88cbe530c3 100644
--- a/node/config.go
+++ b/node/config.go
@@ -86,6 +86,10 @@ type Config struct {
// NoUSB disables hardware wallet monitoring and connectivity.
NoUSB bool `toml:",omitempty"`
+ // ExternalSigner path/url to external signer. Implicitly disables native account
+ // management
+ ExternalSigner string `toml:",omitempty"`
+
// IPCPath is the requested location to place the IPC endpoint. If the path is
// a simple file name, it is placed inside the data directory (or on the root
// pipe path on Windows), whereas if it's a resolvable path name (absolute or
diff --git a/node/node.go b/node/node.go
index ada3837217..60923b2c07 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,15 +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 *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
@@ -97,20 +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.
- am, ephemeralKeystore, err := makeAccountManager(conf)
- if 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,
- ephemeralKeystore: ephemeralKeystore,
config: conf,
serviceFuncs: []ServiceConstructor{},
ipcEndpoint: conf.IPCEndpoint(),
@@ -173,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
@@ -435,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
}
@@ -537,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
@@ -578,6 +554,13 @@ func (n *Node) ResolvePath(x string) string {
return n.config.ResolvePath(x)
}
+func (n *Node) SetExternalAPI(api *ethapi.ExternalSignerAPI){
+ n.extapi = api
+}
+
+func (n *Node) ExternalSignerAPI() *ethapi.ExternalSignerAPI{
+ return n.extapi
+}
// 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