mirror of https://github.com/ethereum/go-ethereum
parent
c5215fdd48
commit
205ea95802
@ -0,0 +1,130 @@ |
||||
// 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 |
||||
} |
@ -0,0 +1,79 @@ |
||||
// 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' /\n0 ' /\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) |
||||
} |
||||
} |
||||
} |
@ -1,77 +0,0 @@ |
||||
// 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) |
||||
} |
||||
}*/ |
Loading…
Reference in new issue