Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
205ea958
Unverified
Commit
205ea958
authored
Feb 08, 2017
by
Péter Szilágyi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
accounts, cmd, internal, node: implement HD wallet self-derivation
parent
c5215fdd
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
383 additions
and
136 deletions
+383
-136
accounts.go
accounts/accounts.go
+14
-1
hd.go
accounts/hd.go
+130
-0
hd_test.go
accounts/hd_test.go
+79
-0
keystore_wallet.go
accounts/keystore/keystore_wallet.go
+6
-1
ledger_test.go
accounts/usbwallet/ledger_test.go
+0
-77
ledger_wallet.go
accounts/usbwallet/ledger_wallet.go
+118
-36
main.go
cmd/geth/main.go
+30
-2
api.go
internal/ethapi/api.go
+5
-1
config.go
node/config.go
+1
-18
No files found.
accounts/accounts.go
View file @
205ea958
...
...
@@ -20,6 +20,7 @@ package accounts
import
(
"math/big"
ethereum
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
...
...
@@ -71,7 +72,19 @@ type Wallet interface {
// Derive attempts to explicitly derive a hierarchical deterministic account at
// the specified derivation path. If requested, the derived account will be added
// to the wallet's tracked account list.
Derive
(
path
string
,
pin
bool
)
(
Account
,
error
)
Derive
(
path
DerivationPath
,
pin
bool
)
(
Account
,
error
)
// SelfDerive sets a base account derivation path from which the wallet attempts
// to discover non zero accounts and automatically add them to list of tracked
// accounts.
//
// Note, self derivaton will increment the last component of the specified path
// opposed to decending into a child path to allow discovering accounts starting
// from non zero components.
//
// You can disable automatic account discovery by calling SelfDerive with a nil
// chain state reader.
SelfDerive
(
base
DerivationPath
,
chain
ethereum
.
ChainStateReader
)
// SignHash requests the wallet to sign the given hash.
//
...
...
accounts/hd.go
0 → 100644
View file @
205ea958
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
accounts
import
(
"errors"
"fmt"
"math"
"math/big"
"strings"
)
// DefaultRootDerivationPath is the root path to which custom derivation endpoints
// are appended. As such, the first account will be at m/44'/60'/0'/0, the second
// at m/44'/60'/0'/1, etc.
var
DefaultRootDerivationPath
=
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
}
// DefaultBaseDerivationPath is the base path from which custom derivation endpoints
// are incremented. As such, the first account will be at m/44'/60'/0'/0, the second
// at m/44'/60'/0'/1, etc.
var
DefaultBaseDerivationPath
=
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0
}
// DerivationPath represents the computer friendly version of a hierarchical
// deterministic wallet account derivaion path.
//
// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
// defines derivation paths to be of the form:
//
// m / purpose' / coin_type' / account' / change / address_index
//
// The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
// defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and
// SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns
// the `coin_type` 60' (or 0x8000003C) to Ethereum.
//
// The root path for Ethereum is m/44'/60'/0'/0 according to the specification
// from https://github.com/ethereum/EIPs/issues/84, albeit it's not set in stone
// yet whether accounts should increment the last component or the children of
// that. We will go with the simpler approach of incrementing the last component.
type
DerivationPath
[]
uint32
// ParseDerivationPath converts a user specified derivation path string to the
// internal binary representation.
//
// Full derivation paths need to start with the `m/` prefix, relative derivation
// paths (which will get appended to the default root path) must not have prefixes
// in front of the first element. Whitespace is ignored.
func
ParseDerivationPath
(
path
string
)
(
DerivationPath
,
error
)
{
var
result
DerivationPath
// Handle absolute or relative paths
components
:=
strings
.
Split
(
path
,
"/"
)
switch
{
case
len
(
components
)
==
0
:
return
nil
,
errors
.
New
(
"empty derivation path"
)
case
strings
.
TrimSpace
(
components
[
0
])
==
""
:
return
nil
,
errors
.
New
(
"ambiguous path: use 'm/' prefix for absolute paths, or no leading '/' for relative ones"
)
case
strings
.
TrimSpace
(
components
[
0
])
==
"m"
:
components
=
components
[
1
:
]
default
:
result
=
append
(
result
,
DefaultRootDerivationPath
...
)
}
// All remaining components are relative, append one by one
if
len
(
components
)
==
0
{
return
nil
,
errors
.
New
(
"empty derivation path"
)
// Empty relative paths
}
for
_
,
component
:=
range
components
{
// Ignore any user added whitespace
component
=
strings
.
TrimSpace
(
component
)
var
value
uint32
// Handle hardened paths
if
strings
.
HasSuffix
(
component
,
"'"
)
{
value
=
0x80000000
component
=
strings
.
TrimSpace
(
strings
.
TrimSuffix
(
component
,
"'"
))
}
// Handle the non hardened component
bigval
,
ok
:=
new
(
big
.
Int
)
.
SetString
(
component
,
0
)
if
!
ok
{
return
nil
,
fmt
.
Errorf
(
"invalid component: %s"
,
component
)
}
max
:=
math
.
MaxUint32
-
value
if
bigval
.
Sign
()
<
0
||
bigval
.
Cmp
(
big
.
NewInt
(
int64
(
max
)))
>
0
{
if
value
==
0
{
return
nil
,
fmt
.
Errorf
(
"component %v out of allowed range [0, %d]"
,
bigval
,
max
)
}
return
nil
,
fmt
.
Errorf
(
"component %v out of allowed hardened range [0, %d]"
,
bigval
,
max
)
}
value
+=
uint32
(
bigval
.
Uint64
())
// Append and repeat
result
=
append
(
result
,
value
)
}
return
result
,
nil
}
// String implements the stringer interface, converting a binary derivation path
// to its canonical representation.
func
(
path
DerivationPath
)
String
()
string
{
result
:=
"m"
for
_
,
component
:=
range
path
{
var
hardened
bool
if
component
>=
0x80000000
{
component
-=
0x80000000
hardened
=
true
}
result
=
fmt
.
Sprintf
(
"%s/%d"
,
result
,
component
)
if
hardened
{
result
+=
"'"
}
}
return
result
}
accounts/hd_test.go
0 → 100644
View file @
205ea958
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
accounts
import
(
"reflect"
"testing"
)
// Tests that HD derivation paths can be correctly parsed into our internal binary
// representation.
func
TestHDPathParsing
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
input
string
output
DerivationPath
}{
// Plain absolute derivation paths
{
"m/44'/60'/0'/0"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0
}},
{
"m/44'/60'/0'/128"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
128
}},
{
"m/44'/60'/0'/0'"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
0
}},
{
"m/44'/60'/0'/128'"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
128
}},
{
"m/2147483692/2147483708/2147483648/0"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0
}},
{
"m/2147483692/2147483708/2147483648/2147483648"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
0
}},
// Plain relative derivation paths
{
"0"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0
}},
{
"128"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
128
}},
{
"0'"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
0
}},
{
"128'"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
128
}},
{
"2147483648"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
0
}},
// Hexadecimal absolute derivation paths
{
"m/0x2C'/0x3c'/0x00'/0x00"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0
}},
{
"m/0x2C'/0x3c'/0x00'/0x80"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
128
}},
{
"m/0x2C'/0x3c'/0x00'/0x00'"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
0
}},
{
"m/0x2C'/0x3c'/0x00'/0x80'"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
128
}},
{
"m/0x8000002C/0x8000003c/0x80000000/0x00"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0
}},
{
"m/0x8000002C/0x8000003c/0x80000000/0x80000000"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
0
}},
// Hexadecimal relative derivation paths
{
"0x00"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0
}},
{
"0x80"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
128
}},
{
"0x00'"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
0
}},
{
"0x80'"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
128
}},
{
"0x80000000"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0x80000000
+
0
}},
// Weird inputs just to ensure they work
{
" m / 44 '
\n
/
\n
60
\n\n\t
' /
\n
0 ' /
\t\t
0"
,
DerivationPath
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0
}},
// Invaid derivation paths
{
""
,
nil
},
// Empty relative derivation path
{
"m"
,
nil
},
// Empty absolute derivation path
{
"m/"
,
nil
},
// Missing last derivation component
{
"/44'/60'/0'/0"
,
nil
},
// Absolute path without m prefix, might be user error
{
"m/2147483648'"
,
nil
},
// Overflows 32 bit integer
{
"m/-1'"
,
nil
},
// Cannot contain negative number
}
for
i
,
tt
:=
range
tests
{
if
path
,
err
:=
ParseDerivationPath
(
tt
.
input
);
!
reflect
.
DeepEqual
(
path
,
tt
.
output
)
{
t
.
Errorf
(
"test %d: parse mismatch: have %v (%v), want %v"
,
i
,
path
,
err
,
tt
.
output
)
}
else
if
path
==
nil
&&
err
==
nil
{
t
.
Errorf
(
"test %d: nil path and error: %v"
,
i
,
err
)
}
}
}
accounts/keystore/keystore_wallet.go
View file @
205ea958
...
...
@@ -19,6 +19,7 @@ package keystore
import
(
"math/big"
ethereum
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/core/types"
)
...
...
@@ -69,10 +70,14 @@ func (w *keystoreWallet) Contains(account accounts.Account) bool {
// Derive implements accounts.Wallet, but is a noop for plain wallets since there
// is no notion of hierarchical account derivation for plain keystore accounts.
func
(
w
*
keystoreWallet
)
Derive
(
path
string
,
pin
bool
)
(
accounts
.
Account
,
error
)
{
func
(
w
*
keystoreWallet
)
Derive
(
path
accounts
.
DerivationPath
,
pin
bool
)
(
accounts
.
Account
,
error
)
{
return
accounts
.
Account
{},
accounts
.
ErrNotSupported
}
// SelfDerive implements accounts.Wallet, but is a noop for plain wallets since
// there is no notion of hierarchical account derivation for plain keystore accounts.
func
(
w
*
keystoreWallet
)
SelfDerive
(
base
accounts
.
DerivationPath
,
chain
ethereum
.
ChainStateReader
)
{}
// SignHash implements accounts.Wallet, attempting to sign the given hash with
// the given account. If the wallet does not wrap this particular account, an
// error is returned to avoid account leakage (even though in theory we may be
...
...
accounts/usbwallet/ledger_test.go
deleted
100644 → 0
View file @
c5215fdd
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build !ios
package
usbwallet
/*
func TestLedgerHub(t *testing.T) {
glog.SetV(6)
glog.SetToStderr(true)
// Create a USB hub watching for Ledger devices
hub, err := NewLedgerHub()
if err != nil {
t.Fatalf("Failed to create Ledger hub: %v", err)
}
defer hub.Close()
// Wait for events :P
time.Sleep(time.Minute)
}
*/
/*
func TestLedger(t *testing.T) {
// Create a USB context to access devices through
ctx, err := usb.NewContext()
defer ctx.Close()
ctx.Debug(6)
// List all of the Ledger wallets
wallets, err := findLedgerWallets(ctx)
if err != nil {
t.Fatalf("Failed to list Ledger wallets: %v", err)
}
// Retrieve the address from every one of them
for _, wallet := range wallets {
// Retrieve the version of the wallet app
ver, err := wallet.Version()
if err != nil {
t.Fatalf("Failed to retrieve wallet version: %v", err)
}
fmt.Printf("Ledger version: %s\n", ver)
// Retrieve the address of the wallet
addr, err := wallet.Address()
if err != nil {
t.Fatalf("Failed to retrieve wallet address: %v", err)
}
fmt.Printf("Ledger address: %x\n", addr)
// Try to sign a transaction with the wallet
unsigned := types.NewTransaction(1, common.HexToAddress("0xbabababababababababababababababababababa"), common.Ether, big.NewInt(20000), common.Shannon, nil)
signed, err := wallet.Sign(unsigned)
if err != nil {
t.Fatalf("Failed to sign transactions: %v", err)
}
signer, err := types.Sender(types.NewEIP155Signer(big.NewInt(1)), signed)
if err != nil {
t.Fatalf("Failed to recover signer: %v", err)
}
fmt.Printf("Ledger signature by: %x\n", signer)
}
}*/
accounts/usbwallet/ledger_wallet.go
View file @
205ea958
...
...
@@ -29,11 +29,10 @@ import (
"fmt"
"io"
"math/big"
"strconv"
"strings"
"sync"
"time"
ethereum
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
...
...
@@ -41,10 +40,15 @@ import (
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
"github.com/karalabe/gousb/usb"
"golang.org/x/net/context"
)
// ledgerDerivationPath is the base derivation parameters used by the wallet.
var
ledgerDerivationPath
=
[]
uint32
{
0x80000000
+
44
,
0x80000000
+
60
,
0x80000000
+
0
,
0
}
// Maximum time between wallet health checks to detect USB unplugs.
const
ledgerHeartbeatCycle
=
time
.
Second
// Minimum time to wait between self derivation attempts, even it the user is
// requesting accounts like crazy.
const
ledgerSelfDeriveThrottling
=
time
.
Second
// ledgerOpcode is an enumeration encoding the supported Ledger opcodes.
type
ledgerOpcode
byte
...
...
@@ -84,7 +88,13 @@ type ledgerWallet struct {
version
[
3
]
byte
// Current version of the Ledger Ethereum app (zero if app is offline)
accounts
[]
accounts
.
Account
// List of derive accounts pinned on the Ledger
paths
map
[
common
.
Address
][]
uint32
// Known derivation paths for signing operations
paths
map
[
common
.
Address
]
accounts
.
DerivationPath
// Known derivation paths for signing operations
selfDeriveNextPath
accounts
.
DerivationPath
// Next derivation path for account auto-discovery
selfDeriveNextAddr
common
.
Address
// Next derived account address for auto-discovery
selfDerivePrevZero
common
.
Address
// Last zero-address where auto-discovery stopped
selfDeriveChain
ethereum
.
ChainStateReader
// Blockchain state reader to discover used account with
selfDeriveTime
time
.
Time
// Timestamp of the last self-derivation to avoid thrashing
quit
chan
chan
error
lock
sync
.
RWMutex
...
...
@@ -107,12 +117,17 @@ func (w *ledgerWallet) Status() string {
if
w
.
device
==
nil
{
return
"Closed"
}
if
w
.
version
==
[
3
]
byte
{
0
,
0
,
0
}
{
if
w
.
offline
()
{
return
"Ethereum app offline"
}
return
fmt
.
Sprintf
(
"Ethereum app v%d.%d.%d online"
,
w
.
version
[
0
],
w
.
version
[
1
],
w
.
version
[
2
])
}
// offline returns whether the wallet and the Ethereum app is offline or not.
func
(
w
*
ledgerWallet
)
offline
()
bool
{
return
w
.
version
==
[
3
]
byte
{
0
,
0
,
0
}
}
// Open implements accounts.Wallet, attempting to open a USB connection to the
// Ledger hardware wallet. The Ledger does not require a user passphrase so that
// is silently discarded.
...
...
@@ -176,13 +191,13 @@ func (w *ledgerWallet) Open(passphrase string) error {
// Wallet seems to be successfully opened, guess if the Ethereum app is running
w
.
device
,
w
.
input
,
w
.
output
=
device
,
input
,
output
w
.
paths
=
make
(
map
[
common
.
Address
]
[]
uint32
)
w
.
paths
=
make
(
map
[
common
.
Address
]
accounts
.
DerivationPath
)
w
.
quit
=
make
(
chan
chan
error
)
defer
func
()
{
go
w
.
heartbeat
()
}()
if
_
,
err
:=
w
.
deriveAddress
(
ledger
DerivationPath
);
err
!=
nil
{
if
_
,
err
:=
w
.
deriveAddress
(
accounts
.
DefaultBase
DerivationPath
);
err
!=
nil
{
// Ethereum app is not running, nothing more to do, return
return
nil
}
...
...
@@ -209,7 +224,7 @@ func (w *ledgerWallet) heartbeat() {
case
errc
=
<-
w
.
quit
:
// Termination requested
continue
case
<-
time
.
After
(
time
.
Second
)
:
case
<-
time
.
After
(
ledgerHeartbeatCycle
)
:
// Heartbeat time
}
// Execute a tiny data exchange to see responsiveness
...
...
@@ -242,16 +257,86 @@ func (w *ledgerWallet) Close() error {
return
err
}
w
.
device
,
w
.
input
,
w
.
output
,
w
.
paths
,
w
.
quit
=
nil
,
nil
,
nil
,
nil
,
nil
w
.
version
=
[
3
]
byte
{}
return
herr
// If all went well, return any health-check errors
}
// Accounts implements accounts.Wallet, returning the list of accounts pinned to
// the Ledger hardware wallet.
// the Ledger hardware wallet. If self derivation was enabled, the account list
// is periodically expanded based on current chain state.
func
(
w
*
ledgerWallet
)
Accounts
()
[]
accounts
.
Account
{
w
.
lock
.
RLock
()
defer
w
.
lock
.
RUnlock
()
w
.
lock
.
Lock
()
defer
w
.
lock
.
Unlock
()
// If the wallet is offline, there are no accounts to return
if
w
.
offline
()
{
return
nil
}
// If no self derivation is done (or throttled), return the current accounts
if
w
.
selfDeriveChain
==
nil
||
time
.
Since
(
w
.
selfDeriveTime
)
<
ledgerSelfDeriveThrottling
{
cpy
:=
make
([]
accounts
.
Account
,
len
(
w
.
accounts
))
copy
(
cpy
,
w
.
accounts
)
return
cpy
}
// Self derivation requested, try to expand our account list
ctx
:=
context
.
Background
()
for
empty
:=
false
;
!
empty
;
{
// Retrieve the next derived Ethereum account
var
err
error
if
w
.
selfDeriveNextAddr
==
(
common
.
Address
{})
{
w
.
selfDeriveNextAddr
,
err
=
w
.
deriveAddress
(
w
.
selfDeriveNextPath
)
if
err
!=
nil
{
// Derivation failed, disable auto discovery
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"self-derivation failed: %v"
,
err
)
w
.
selfDeriveChain
=
nil
break
}
}
// Check the account's status against the current chain state
balance
,
err
:=
w
.
selfDeriveChain
.
BalanceAt
(
ctx
,
w
.
selfDeriveNextAddr
,
nil
)
if
err
!=
nil
{
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"self-derivation balance retrieval failed: %v"
,
err
)
w
.
selfDeriveChain
=
nil
break
}
nonce
,
err
:=
w
.
selfDeriveChain
.
NonceAt
(
ctx
,
w
.
selfDeriveNextAddr
,
nil
)
if
err
!=
nil
{
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"self-derivation nonce retrieval failed: %v"
,
err
)
w
.
selfDeriveChain
=
nil
break
}
// If the next account is empty, stop self-derivation, but add it nonetheless
if
balance
.
BitLen
()
==
0
&&
nonce
==
0
{
w
.
selfDerivePrevZero
=
w
.
selfDeriveNextAddr
empty
=
true
}
// We've just self-derived a new non-zero account, start tracking it
path
:=
make
(
accounts
.
DerivationPath
,
len
(
w
.
selfDeriveNextPath
))
copy
(
path
[
:
],
w
.
selfDeriveNextPath
[
:
])
account
:=
accounts
.
Account
{
Address
:
w
.
selfDeriveNextAddr
,
URL
:
accounts
.
URL
{
Scheme
:
w
.
url
.
Scheme
,
Path
:
fmt
.
Sprintf
(
"%s/%s"
,
w
.
url
.
Path
,
path
)},
}
_
,
known
:=
w
.
paths
[
w
.
selfDeriveNextAddr
]
if
!
known
||
(
!
empty
&&
w
.
selfDeriveNextAddr
==
w
.
selfDerivePrevZero
)
{
// Either fully new account, or previous zero. Report discovery either way
glog
.
V
(
logger
.
Info
)
.
Infof
(
"%s discovered %s (balance %d, nonce %d) at %s"
,
w
.
url
.
String
(),
w
.
selfDeriveNextAddr
.
Hex
(),
balance
,
nonce
,
path
)
}
if
!
known
{
w
.
accounts
=
append
(
w
.
accounts
,
account
)
w
.
paths
[
w
.
selfDeriveNextAddr
]
=
path
}
// Fetch the next potential account
if
!
empty
{
w
.
selfDeriveNextAddr
=
common
.
Address
{}
w
.
selfDeriveNextPath
[
len
(
w
.
selfDeriveNextPath
)
-
1
]
++
}
}
w
.
selfDeriveTime
=
time
.
Now
()
// Return whatever account list we ended up with
cpy
:=
make
([]
accounts
.
Account
,
len
(
w
.
accounts
))
copy
(
cpy
,
w
.
accounts
)
return
cpy
...
...
@@ -271,34 +356,16 @@ func (w *ledgerWallet) Contains(account accounts.Account) bool {
// Derive implements accounts.Wallet, deriving a new account at the specific
// derivation path. If pin is set to true, the account will be added to the list
// of tracked accounts.
func
(
w
*
ledgerWallet
)
Derive
(
path
string
,
pin
bool
)
(
accounts
.
Account
,
error
)
{
func
(
w
*
ledgerWallet
)
Derive
(
path
accounts
.
DerivationPath
,
pin
bool
)
(
accounts
.
Account
,
error
)
{
w
.
lock
.
Lock
()
defer
w
.
lock
.
Unlock
()
// If the wallet is closed, or the Ethereum app doesn't run, abort
if
w
.
device
==
nil
||
w
.
version
==
[
3
]
byte
{
0
,
0
,
0
}
{
if
w
.
device
==
nil
||
w
.
offline
()
{
return
accounts
.
Account
{},
accounts
.
ErrWalletClosed
}
// All seems fine, convert the user derivation path to Ledger representation
path
=
strings
.
TrimPrefix
(
path
,
"/"
)
parts
:=
strings
.
Split
(
path
,
"/"
)
lpath
:=
make
([]
uint32
,
len
(
parts
))
for
i
,
part
:=
range
parts
{
// Handle hardened paths
if
strings
.
HasSuffix
(
part
,
"'"
)
{
lpath
[
i
]
=
0x80000000
part
=
strings
.
TrimSuffix
(
part
,
"'"
)
}
// Handle the non hardened component
val
,
err
:=
strconv
.
Atoi
(
part
)
if
err
!=
nil
{
return
accounts
.
Account
{},
fmt
.
Errorf
(
"path element %d: %v"
,
i
,
err
)
}
lpath
[
i
]
+=
uint32
(
val
)
}
// Try to derive the actual account and update it's URL if succeeful
address
,
err
:=
w
.
deriveAddress
(
l
path
)
address
,
err
:=
w
.
deriveAddress
(
path
)
if
err
!=
nil
{
return
accounts
.
Account
{},
err
}
...
...
@@ -310,12 +377,27 @@ func (w *ledgerWallet) Derive(path string, pin bool) (accounts.Account, error) {
if
pin
{
if
_
,
ok
:=
w
.
paths
[
address
];
!
ok
{
w
.
accounts
=
append
(
w
.
accounts
,
account
)
w
.
paths
[
address
]
=
l
path
w
.
paths
[
address
]
=
path
}
}
return
account
,
nil
}
// SelfDerive implements accounts.Wallet, trying to discover accounts that the
// user used previously (based on the chain state), but ones that he/she did not
// explicitly pin to the wallet manually. To avoid chain head monitoring, self
// derivation only runs during account listing (and even then throttled).
func
(
w
*
ledgerWallet
)
SelfDerive
(
base
accounts
.
DerivationPath
,
chain
ethereum
.
ChainStateReader
)
{
w
.
lock
.
Lock
()
defer
w
.
lock
.
Unlock
()
w
.
selfDeriveNextPath
=
make
(
accounts
.
DerivationPath
,
len
(
base
))
copy
(
w
.
selfDeriveNextPath
[
:
],
base
[
:
])
w
.
selfDeriveNextAddr
=
common
.
Address
{}
w
.
selfDeriveChain
=
chain
}
// SignHash implements accounts.Wallet, however signing arbitrary data is not
// supported for Ledger wallets, so this method will always return an error.
func
(
w
*
ledgerWallet
)
SignHash
(
acc
accounts
.
Account
,
hash
[]
byte
)
([]
byte
,
error
)
{
...
...
cmd/geth/main.go
View file @
205ea958
...
...
@@ -25,12 +25,14 @@ import (
"strings"
"time"
"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/common"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/contracts/release"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
...
...
@@ -249,12 +251,38 @@ func startNode(ctx *cli.Context, stack *node.Node) {
ks
:=
stack
.
AccountManager
()
.
Backends
(
keystore
.
KeyStoreType
)[
0
]
.
(
*
keystore
.
KeyStore
)
passwords
:=
utils
.
MakePasswordList
(
ctx
)
account
s
:=
strings
.
Split
(
ctx
.
GlobalString
(
utils
.
UnlockedAccountFlag
.
Name
),
","
)
for
i
,
account
:=
range
account
s
{
unlock
s
:=
strings
.
Split
(
ctx
.
GlobalString
(
utils
.
UnlockedAccountFlag
.
Name
),
","
)
for
i
,
account
:=
range
unlock
s
{
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 an 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
)
// Listen for wallet event till termination
for
event
:=
range
events
{
if
event
.
Arrive
{
if
err
:=
event
.
Wallet
.
Open
(
""
);
err
!=
nil
{
glog
.
V
(
logger
.
Info
)
.
Infof
(
"New wallet appeared: %s, failed to open: %s"
,
event
.
Wallet
.
URL
(),
err
)
}
else
{
glog
.
V
(
logger
.
Info
)
.
Infof
(
"New wallet appeared: %s, %s"
,
event
.
Wallet
.
URL
(),
event
.
Wallet
.
Status
())
}
event
.
Wallet
.
SelfDerive
(
accounts
.
DefaultBaseDerivationPath
,
stateReader
)
}
else
{
glog
.
V
(
logger
.
Info
)
.
Infof
(
"Old wallet dropped: %s"
,
event
.
Wallet
.
URL
())
}
}
}()
// Start auxiliary services if enabled
if
ctx
.
GlobalBool
(
utils
.
MiningEnabledFlag
.
Name
)
{
var
ethereum
*
eth
.
Ethereum
...
...
internal/ethapi/api.go
View file @
205ea958
...
...
@@ -253,10 +253,14 @@ func (s *PrivateAccountAPI) DeriveAccount(url string, path string, pin *bool) (a
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
(
p
ath
,
*
pin
)
return
wallet
.
Derive
(
derivP
ath
,
*
pin
)
}
// NewAccount will create a new account and returns the address for the new account.
...
...
node/config.go
View file @
205ea958
...
...
@@ -446,22 +446,5 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
}
else
{
backends
=
append
(
backends
,
ledgerhub
)
}
am
:=
accounts
.
NewManager
(
backends
...
)
// Start some logging for the user
changes
:=
make
(
chan
accounts
.
WalletEvent
,
16
)
am
.
Subscribe
(
changes
)
go
func
()
{
for
event
:=
range
changes
{
if
event
.
Arrive
{
glog
.
V
(
logger
.
Info
)
.
Infof
(
"New wallet appeared: %s"
,
event
.
Wallet
.
URL
())
if
err
:=
event
.
Wallet
.
Open
(
""
);
err
!=
nil
{
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"Failed to open wallet %s: %v"
,
event
.
Wallet
.
URL
(),
err
)
}
}
else
{
glog
.
V
(
logger
.
Info
)
.
Infof
(
"Old wallet disappeared: %s"
,
event
.
Wallet
.
URL
())
}
}
}()
return
am
,
ephemeral
,
nil
return
accounts
.
NewManager
(
backends
...
),
ephemeral
,
nil
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment