aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/usbwallet
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/usbwallet')
-rw-r--r--accounts/usbwallet/ledger_hub.go45
-rw-r--r--accounts/usbwallet/ledger_wallet.go97
-rw-r--r--accounts/usbwallet/usbwallet.go8
-rw-r--r--accounts/usbwallet/usbwallet_ios.go38
4 files changed, 38 insertions, 150 deletions
diff --git a/accounts/usbwallet/ledger_hub.go b/accounts/usbwallet/ledger_hub.go
index ad5940cd4..70396d314 100644
--- a/accounts/usbwallet/ledger_hub.go
+++ b/accounts/usbwallet/ledger_hub.go
@@ -18,18 +18,16 @@
// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
-// +build !ios
-
package usbwallet
import (
- "fmt"
+ "errors"
"sync"
"time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/event"
- "github.com/karalabe/gousb/usb"
+ "github.com/karalabe/hid"
)
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
@@ -49,8 +47,6 @@ const ledgerRefreshThrottling = 500 * time.Millisecond
// LedgerHub is a accounts.Backend that can find and handle Ledger hardware wallets.
type LedgerHub struct {
- ctx *usb.Context // Context interfacing with a libusb instance
-
refreshed time.Time // Time instance when the list of wallets was last refreshed
wallets []accounts.Wallet // List of Ledger devices currently tracking
updateFeed event.Feed // Event feed to notify wallet additions/removals
@@ -63,18 +59,13 @@ type LedgerHub struct {
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
func NewLedgerHub() (*LedgerHub, error) {
- // Initialize the USB library to access Ledgers through
- ctx, err := usb.NewContext()
- if err != nil {
- return nil, err
+ if !hid.Supported() {
+ return nil, errors.New("unsupported platform")
}
- // Create the USB hub, start and return it
hub := &LedgerHub{
- ctx: ctx,
quit: make(chan chan error),
}
hub.refreshWallets()
-
return hub, nil
}
@@ -104,31 +95,23 @@ func (hub *LedgerHub) refreshWallets() {
return
}
// Retrieve the current list of Ledger devices
- var devIDs []deviceID
- var busIDs []uint16
-
- hub.ctx.ListDevices(func(desc *usb.Descriptor) bool {
- // Gather Ledger devices, don't connect any just yet
+ var ledgers []hid.DeviceInfo
+ for _, info := range hid.Enumerate(0, 0) { // Can't enumerate directly, one valid ID is the 0 wildcard
for _, id := range ledgerDeviceIDs {
- if desc.Vendor == id.Vendor && desc.Product == id.Product {
- devIDs = append(devIDs, deviceID{Vendor: desc.Vendor, Product: desc.Product})
- busIDs = append(busIDs, uint16(desc.Bus)<<8+uint16(desc.Address))
- return false
+ if info.VendorID == id.Vendor && info.ProductID == id.Product {
+ ledgers = append(ledgers, info)
+ break
}
}
- // Not ledger, ignore and don't connect either
- return false
- })
+ }
// Transform the current list of wallets into the new one
hub.lock.Lock()
- wallets := make([]accounts.Wallet, 0, len(devIDs))
+ wallets := make([]accounts.Wallet, 0, len(ledgers))
events := []accounts.WalletEvent{}
- for i := 0; i < len(devIDs); i++ {
- devID, busID := devIDs[i], busIDs[i]
-
- url := accounts.URL{Scheme: LedgerScheme, Path: fmt.Sprintf("%03d:%03d", busID>>8, busID&0xff)}
+ for _, ledger := range ledgers {
+ url := accounts.URL{Scheme: LedgerScheme, Path: ledger.Path}
// Drop wallets in front of the next device or those that failed for some reason
for len(hub.wallets) > 0 && (hub.wallets[0].URL().Cmp(url) < 0 || hub.wallets[0].(*ledgerWallet).failed()) {
@@ -137,7 +120,7 @@ func (hub *LedgerHub) refreshWallets() {
}
// If there are no more wallets or the device is before the next, wrap new wallet
if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 {
- wallet := &ledgerWallet{context: hub.ctx, hardwareID: devID, locationID: busID, url: &url}
+ wallet := &ledgerWallet{url: &url, info: ledger}
events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true})
wallets = append(wallets, wallet)
diff --git a/accounts/usbwallet/ledger_wallet.go b/accounts/usbwallet/ledger_wallet.go
index a667f580a..235086d1e 100644
--- a/accounts/usbwallet/ledger_wallet.go
+++ b/accounts/usbwallet/ledger_wallet.go
@@ -18,8 +18,6 @@
// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
-// +build !ios
-
package usbwallet
import (
@@ -39,7 +37,7 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
- "github.com/karalabe/gousb/usb"
+ "github.com/karalabe/hid"
"golang.org/x/net/context"
)
@@ -74,22 +72,22 @@ const (
ledgerP2ReturnAddressChainCode ledgerParam2 = 0x01 // Require a user confirmation before returning the address
)
-// errReplyInvalidHeader is the error message returned by a Ledfer data exchange
+// errReplyInvalidHeader is the error message returned by a Ledger data exchange
// if the device replies with a mismatching header. This usually means the device
// is in browser mode.
var errReplyInvalidHeader = errors.New("invalid reply header")
+// errInvalidVersionReply is the error message returned by a Ledger version retrieval
+// when a response does arrive, but it does not contain the expected data.
+var errInvalidVersionReply = errors.New("invalid version reply")
+
// ledgerWallet represents a live USB Ledger hardware wallet.
type ledgerWallet struct {
- context *usb.Context // USB context to interface libusb through
- hardwareID deviceID // USB identifiers to identify this device type
- locationID uint16 // USB bus and address to identify this device instance
- url *accounts.URL // Textual URL uniquely identifying this wallet
+ url *accounts.URL // Textual URL uniquely identifying this wallet
- device *usb.Device // USB device advertising itself as a Ledger wallet
- input usb.Endpoint // Input endpoint to send data to this device
- output usb.Endpoint // Output endpoint to receive data from this device
- failure error // Any failure that would make the device unusable
+ info hid.DeviceInfo // Known USB device infos about the wallet
+ device *hid.Device // USB device advertising itself as a Ledger wallet
+ failure error // Any failure that would make the device unusable
version [3]byte // Current version of the Ledger Ethereum app (zero if app is offline)
browser bool // Flag whether the Ledger is in browser mode (reply channel mismatch)
@@ -183,59 +181,12 @@ func (w *ledgerWallet) Open(passphrase string) error {
return accounts.ErrWalletAlreadyOpen
}
// Otherwise iterate over all USB devices and find this again (no way to directly do this)
- // Iterate over all attached devices and fetch those seemingly Ledger
- devices, err := w.context.ListDevices(func(desc *usb.Descriptor) bool {
- // Only open this single specific device
- return desc.Vendor == w.hardwareID.Vendor && desc.Product == w.hardwareID.Product &&
- uint16(desc.Bus)<<8+uint16(desc.Address) == w.locationID
- })
+ device, err := w.info.Open()
if err != nil {
return err
}
- if len(devices) == 0 {
- return accounts.ErrUnknownWallet
- }
- // Device opened, attach to the input and output endpoints
- device := devices[0]
-
- var invalid string
- switch {
- case len(device.Descriptor.Configs) == 0:
- invalid = "no endpoint config available"
- case len(device.Descriptor.Configs[0].Interfaces) == 0:
- invalid = "no endpoint interface available"
- case len(device.Descriptor.Configs[0].Interfaces[0].Setups) == 0:
- invalid = "no endpoint setup available"
- case len(device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints) < 2:
- invalid = "not enough IO endpoints available"
- }
- if invalid != "" {
- device.Close()
- return fmt.Errorf("ledger wallet [%s] invalid: %s", w.url, invalid)
- }
- // Open the input and output endpoints to the device
- input, err := device.OpenEndpoint(
- device.Descriptor.Configs[0].Config,
- device.Descriptor.Configs[0].Interfaces[0].Number,
- device.Descriptor.Configs[0].Interfaces[0].Setups[0].Number,
- device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints[1].Address,
- )
- if err != nil {
- device.Close()
- return fmt.Errorf("ledger wallet [%s] input open failed: %v", w.url, err)
- }
- output, err := device.OpenEndpoint(
- device.Descriptor.Configs[0].Config,
- device.Descriptor.Configs[0].Interfaces[0].Number,
- device.Descriptor.Configs[0].Interfaces[0].Setups[0].Number,
- device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints[0].Address,
- )
- if err != nil {
- device.Close()
- return fmt.Errorf("ledger wallet [%s] output open failed: %v", w.url, err)
- }
// Wallet seems to be successfully opened, guess if the Ethereum app is running
- w.device, w.input, w.output = device, input, output
+ w.device = device
w.commsLock = make(chan struct{}, 1)
w.commsLock <- struct{}{} // Enable lock
@@ -298,13 +249,13 @@ func (w *ledgerWallet) heartbeat() {
w.commsLock <- struct{}{}
w.stateLock.RUnlock()
- if err == usb.ERROR_IO || err == usb.ERROR_NO_DEVICE {
+ if err != nil && err != errInvalidVersionReply {
w.stateLock.Lock() // Lock state to tear the wallet down
w.failure = err
w.close()
w.stateLock.Unlock()
}
- // Ignore uninteresting errors
+ // Ignore non hardware related errors
err = nil
}
// In case of error, wait for termination
@@ -363,13 +314,13 @@ func (w *ledgerWallet) close() error {
return nil
}
// Close the device, clear everything, then return
- err := w.device.Close()
+ w.device.Close()
+ w.device = nil
- w.device, w.input, w.output = nil, nil, nil
w.browser, w.version = false, [3]byte{}
w.accounts, w.paths = nil, nil
- return err
+ return nil
}
// Accounts implements accounts.Wallet, returning the list of accounts pinned to
@@ -664,7 +615,7 @@ func (w *ledgerWallet) ledgerVersion() ([3]byte, error) {
return [3]byte{}, err
}
if len(reply) != 4 {
- return [3]byte{}, errors.New("reply not of correct size")
+ return [3]byte{}, errInvalidVersionReply
}
// Cache the version for future reference
var version [3]byte
@@ -768,10 +719,6 @@ func (w *ledgerWallet) ledgerDerive(derivationPath []uint32) (common.Address, er
// signature R | 32 bytes
// signature S | 32 bytes
func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
- // We need to modify the timeouts to account for user feedback
- defer func(old time.Duration) { w.device.ReadTimeout = old }(w.device.ReadTimeout)
- w.device.ReadTimeout = time.Hour * 24 * 30 // Timeout requires a Ledger power cycle, only if you must
-
// Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath))
path[0] = byte(len(derivationPath))
@@ -903,9 +850,9 @@ func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
}
// Send over to the device
if glog.V(logger.Detail) {
- glog.Infof("-> %03d.%03d: %x", w.device.Bus, w.device.Address, chunk)
+ glog.Infof("-> %s: %x", w.device.Path, chunk)
}
- if _, err := w.input.Write(chunk); err != nil {
+ if _, err := w.device.Write(chunk); err != nil {
return nil, err
}
}
@@ -914,11 +861,11 @@ func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
chunk = chunk[:64] // Yeah, we surely have enough space
for {
// Read the next chunk from the Ledger wallet
- if _, err := io.ReadFull(w.output, chunk); err != nil {
+ if _, err := io.ReadFull(w.device, chunk); err != nil {
return nil, err
}
if glog.V(logger.Detail) {
- glog.Infof("<- %03d.%03d: %x", w.device.Bus, w.device.Address, chunk)
+ glog.Infof("<- %s: %x", w.device.Path, chunk)
}
// Make sure the transport header matches
if chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != 0x05 {
diff --git a/accounts/usbwallet/usbwallet.go b/accounts/usbwallet/usbwallet.go
index 3989f3d02..938ab1e6a 100644
--- a/accounts/usbwallet/usbwallet.go
+++ b/accounts/usbwallet/usbwallet.go
@@ -14,16 +14,12 @@
// 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 implements support for USB hardware wallets.
package usbwallet
-import "github.com/karalabe/gousb/usb"
-
// deviceID is a combined vendor/product identifier to uniquely identify a USB
// hardware device.
type deviceID struct {
- Vendor usb.ID // The Vendor identifer
- Product usb.ID // The Product identifier
+ Vendor uint16 // The Vendor identifer
+ Product uint16 // The Product identifier
}
diff --git a/accounts/usbwallet/usbwallet_ios.go b/accounts/usbwallet/usbwallet_ios.go
deleted file mode 100644
index 17d342903..000000000
--- a/accounts/usbwallet/usbwallet_ios.go
+++ /dev/null
@@ -1,38 +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/>.
-
-// This file contains the implementation for interacting with the Ledger hardware
-// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
-// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
-
-// +build ios
-
-package usbwallet
-
-import (
- "errors"
-
- "github.com/ethereum/go-ethereum/accounts"
-)
-
-// Here be dragons! There is no USB support on iOS.
-
-// ErrIOSNotSupported is returned for all USB hardware backends on iOS.
-var ErrIOSNotSupported = errors.New("no USB support on iOS")
-
-func NewLedgerHub() (accounts.Backend, error) {
- return nil, ErrIOSNotSupported
-}