mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
wip
This commit is contained in:
parent
c79192e67e
commit
a5470a6ab5
14 changed files with 57 additions and 1107 deletions
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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: "<keyFile>",
|
||||
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 <DATADIR>/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: "<address>",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.LightKDFFlag,
|
||||
},
|
||||
Description: `
|
||||
geth account update <address>
|
||||
|
||||
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] <address>
|
||||
|
||||
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: "<keyFile>",
|
||||
Description: `
|
||||
geth account import <keyfile>
|
||||
|
||||
Imports an unencrypted private key from <keyfile> 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] <keyfile>
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
40
node/node.go
40
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{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue