| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | // 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 usbwallet implements support for USB hardware wallets. | 
					
						
							|  |  |  | package usbwallet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							|  |  |  | 	"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" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/log" | 
					
						
							|  |  |  | 	"github.com/karalabe/hid" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Maximum time between wallet health checks to detect USB unplugs. | 
					
						
							|  |  |  | const heartbeatCycle = time.Second | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Minimum time to wait between self derivation attempts, even it the user is | 
					
						
							|  |  |  | // requesting accounts like crazy. | 
					
						
							|  |  |  | const selfDeriveThrottling = time.Second | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // driver defines the vendor specific functionality hardware wallets instances | 
					
						
							|  |  |  | // must implement to allow using them with the wallet lifecycle management. | 
					
						
							|  |  |  | type driver interface { | 
					
						
							|  |  |  | 	// Status returns a textual status to aid the user in the current state of the | 
					
						
							|  |  |  | 	// wallet. It also returns an error indicating any failure the wallet might have | 
					
						
							|  |  |  | 	// encountered. | 
					
						
							|  |  |  | 	Status() (string, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Open initializes access to a wallet instance. The passphrase parameter may | 
					
						
							|  |  |  | 	// or may not be used by the implementation of a particular wallet instance. | 
					
						
							|  |  |  | 	Open(device io.ReadWriter, passphrase string) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Close releases any resources held by an open wallet instance. | 
					
						
							|  |  |  | 	Close() error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Heartbeat performs a sanity check against the hardware wallet to see if it | 
					
						
							|  |  |  | 	// is still online and healthy. | 
					
						
							|  |  |  | 	Heartbeat() error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Derive sends a derivation request to the USB device and returns the Ethereum | 
					
						
							|  |  |  | 	// address located on that path. | 
					
						
							|  |  |  | 	Derive(path accounts.DerivationPath) (common.Address, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// SignTx sends the transaction to the USB device and waits for the user to confirm | 
					
						
							|  |  |  | 	// or deny the transaction. | 
					
						
							|  |  |  | 	SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // wallet represents the common functionality shared by all USB hardware | 
					
						
							|  |  |  | // wallets to prevent reimplementing the same complex maintenance mechanisms | 
					
						
							|  |  |  | // for different vendors. | 
					
						
							|  |  |  | type wallet struct { | 
					
						
							|  |  |  | 	hub    *Hub          // USB hub scanning | 
					
						
							|  |  |  | 	driver driver        // Hardware implementation of the low level device operations | 
					
						
							|  |  |  | 	url    *accounts.URL // Textual URL uniquely identifying this wallet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	info   hid.DeviceInfo // Known USB device infos about the wallet | 
					
						
							|  |  |  | 	device *hid.Device    // USB device advertising itself as a hardware wallet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	accounts []accounts.Account                         // List of derive accounts pinned on the hardware wallet | 
					
						
							|  |  |  | 	paths    map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	deriveNextPath accounts.DerivationPath   // Next derivation path for account auto-discovery | 
					
						
							|  |  |  | 	deriveNextAddr common.Address            // Next derived account address for auto-discovery | 
					
						
							|  |  |  | 	deriveChain    ethereum.ChainStateReader // Blockchain state reader to discover used account with | 
					
						
							|  |  |  | 	deriveReq      chan chan struct{}        // Channel to request a self-derivation on | 
					
						
							|  |  |  | 	deriveQuit     chan chan error           // Channel to terminate the self-deriver with | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	healthQuit chan chan error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Locking a hardware wallet is a bit special. Since hardware devices are lower | 
					
						
							|  |  |  | 	// performing, any communication with them might take a non negligible amount of | 
					
						
							|  |  |  | 	// time. Worse still, waiting for user confirmation can take arbitrarily long, | 
					
						
							|  |  |  | 	// but exclusive communication must be upheld during. Locking the entire wallet | 
					
						
							|  |  |  | 	// in the mean time however would stall any parts of the system that don't want | 
					
						
							|  |  |  | 	// to communicate, just read some state (e.g. list the accounts). | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// As such, a hardware wallet needs two locks to function correctly. A state | 
					
						
							|  |  |  | 	// lock can be used to protect the wallet's software-side internal state, which | 
					
						
							| 
									
										
											  
											
												cmd/clef, signer: initial poc of the standalone signer (#16154)
* signer: introduce external signer command
* cmd/signer, rpc: Implement new signer. Add info about remote user to Context
* signer: refactored request/response, made use of urfave.cli
* cmd/signer: Use common flags
* cmd/signer: methods to validate calldata against abi
* cmd/signer: work on abi parser
* signer: add mutex around UI
* cmd/signer: add json 4byte directory, remove passwords from api
* cmd/signer: minor changes
* cmd/signer: Use ErrRequestDenied, enable lightkdf
* cmd/signer: implement tests
* cmd/signer: made possible for UI to modify tx parameters
* cmd/signer: refactors, removed channels in ui comms, added UI-api via stdin/out
* cmd/signer: Made lowercase json-definitions, added UI-signer test functionality
* cmd/signer: update documentation
* cmd/signer: fix bugs, improve abi detection, abi argument display
* cmd/signer: minor change in json format
* cmd/signer: rework json communication
* cmd/signer: implement mixcase addresses in API, fix json id bug
* cmd/signer: rename fromaccount, update pythonpoc with new json encoding format
* cmd/signer: make use of new abi interface
* signer: documentation
* signer/main: remove redundant  option
* signer: implement audit logging
* signer: create package 'signer', minor changes
* common: add 0x-prefix to mixcaseaddress in json marshalling + validation
* signer, rules, storage: implement rules + ephemeral storage for signer rules
* signer: implement OnApprovedTx, change signing response (API BREAKAGE)
* signer: refactoring + documentation
* signer/rules: implement dispatching to next handler
* signer: docs
* signer/rules: hide json-conversion from users, ensure context is cleaned
* signer: docs
* signer: implement validation rules, change signature of call_info
* signer: fix log flaw with string pointer
* signer: implement custom 4byte databsae that saves submitted signatures
* signer/storage: implement aes-gcm-backed credential storage
* accounts: implement json unmarshalling of url
* signer: fix listresponse, fix gas->uint64
* node: make http/ipc start methods public
* signer: add ipc capability+review concerns
* accounts: correct docstring
* signer: address review concerns
* rpc: go fmt -s
* signer: review concerns+ baptize Clef
* signer,node: move Start-functions to separate file
* signer: formatting
											
										 
											2018-04-16 14:04:32 +02:00
										 |  |  | 	// must not be held exclusively during hardware communication. A communication | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 	// lock can be used to achieve exclusive access to the device itself, this one | 
					
						
							|  |  |  | 	// however should allow "skipping" waiting for operations that might want to | 
					
						
							|  |  |  | 	// use the device, but can live without too (e.g. account self-derivation). | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// Since we have two locks, it's important to know how to properly use them: | 
					
						
							|  |  |  | 	//   - Communication requires the `device` to not change, so obtaining the | 
					
						
							|  |  |  | 	//     commsLock should be done after having a stateLock. | 
					
						
							|  |  |  | 	//   - Communication must not disable read access to the wallet state, so it | 
					
						
							|  |  |  | 	//     must only ever hold a *read* lock to stateLock. | 
					
						
							|  |  |  | 	commsLock chan struct{} // Mutex (buf=1) for the USB comms without keeping the state locked | 
					
						
							|  |  |  | 	stateLock sync.RWMutex  // Protects read and write access to the wallet struct fields | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	log log.Logger // Contextual logger to tag the base with its id | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // URL implements accounts.Wallet, returning the URL of the USB hardware device. | 
					
						
							|  |  |  | func (w *wallet) URL() accounts.URL { | 
					
						
							|  |  |  | 	return *w.url // Immutable, no need for a lock | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Status implements accounts.Wallet, returning a custom status message from the | 
					
						
							|  |  |  | // underlying vendor-specific hardware wallet implementation. | 
					
						
							|  |  |  | func (w *wallet) Status() (string, error) { | 
					
						
							|  |  |  | 	w.stateLock.RLock() // No device communication, state lock is enough | 
					
						
							|  |  |  | 	defer w.stateLock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	status, failure := w.driver.Status() | 
					
						
							|  |  |  | 	if w.device == nil { | 
					
						
							|  |  |  | 		return "Closed", failure | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return status, failure | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Open implements accounts.Wallet, attempting to open a USB connection to the | 
					
						
							|  |  |  | // hardware wallet. | 
					
						
							|  |  |  | func (w *wallet) Open(passphrase string) error { | 
					
						
							|  |  |  | 	w.stateLock.Lock() // State lock is enough since there's no connection yet at this point | 
					
						
							|  |  |  | 	defer w.stateLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If the device was already opened once, refuse to try again | 
					
						
							|  |  |  | 	if w.paths != nil { | 
					
						
							|  |  |  | 		return accounts.ErrWalletAlreadyOpen | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Make sure the actual device connection is done only once | 
					
						
							|  |  |  | 	if w.device == nil { | 
					
						
							|  |  |  | 		device, err := w.info.Open() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w.device = device | 
					
						
							|  |  |  | 		w.commsLock = make(chan struct{}, 1) | 
					
						
							|  |  |  | 		w.commsLock <- struct{}{} // Enable lock | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Delegate device initialization to the underlying driver | 
					
						
							|  |  |  | 	if err := w.driver.Open(w.device, passphrase); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Connection successful, start life-cycle management | 
					
						
							|  |  |  | 	w.paths = make(map[common.Address]accounts.DerivationPath) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.deriveReq = make(chan chan struct{}) | 
					
						
							|  |  |  | 	w.deriveQuit = make(chan chan error) | 
					
						
							|  |  |  | 	w.healthQuit = make(chan chan error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go w.heartbeat() | 
					
						
							|  |  |  | 	go w.selfDerive() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Notify anyone listening for wallet events that a new device is accessible | 
					
						
							|  |  |  | 	go w.hub.updateFeed.Send(accounts.WalletEvent{Wallet: w, Kind: accounts.WalletOpened}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // heartbeat is a health check loop for the USB wallets to periodically verify | 
					
						
							|  |  |  | // whether they are still present or if they malfunctioned. | 
					
						
							|  |  |  | func (w *wallet) heartbeat() { | 
					
						
							|  |  |  | 	w.log.Debug("USB wallet health-check started") | 
					
						
							|  |  |  | 	defer w.log.Debug("USB wallet health-check stopped") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Execute heartbeat checks until termination or error | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		errc chan error | 
					
						
							|  |  |  | 		err  error | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	for errc == nil && err == nil { | 
					
						
							|  |  |  | 		// Wait until termination is requested or the heartbeat cycle arrives | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case errc = <-w.healthQuit: | 
					
						
							|  |  |  | 			// Termination requested | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		case <-time.After(heartbeatCycle): | 
					
						
							|  |  |  | 			// Heartbeat time | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Execute a tiny data exchange to see responsiveness | 
					
						
							|  |  |  | 		w.stateLock.RLock() | 
					
						
							|  |  |  | 		if w.device == nil { | 
					
						
							|  |  |  | 			// Terminated while waiting for the lock | 
					
						
							|  |  |  | 			w.stateLock.RUnlock() | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		<-w.commsLock // Don't lock state while resolving version | 
					
						
							|  |  |  | 		err = w.driver.Heartbeat() | 
					
						
							|  |  |  | 		w.commsLock <- struct{}{} | 
					
						
							|  |  |  | 		w.stateLock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			w.stateLock.Lock() // Lock state to tear the wallet down | 
					
						
							|  |  |  | 			w.close() | 
					
						
							|  |  |  | 			w.stateLock.Unlock() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Ignore non hardware related errors | 
					
						
							|  |  |  | 		err = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// In case of error, wait for termination | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		w.log.Debug("USB wallet health-check failed", "err", err) | 
					
						
							|  |  |  | 		errc = <-w.healthQuit | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	errc <- err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Close implements accounts.Wallet, closing the USB connection to the device. | 
					
						
							|  |  |  | func (w *wallet) Close() error { | 
					
						
							|  |  |  | 	// Ensure the wallet was opened | 
					
						
							|  |  |  | 	w.stateLock.RLock() | 
					
						
							|  |  |  | 	hQuit, dQuit := w.healthQuit, w.deriveQuit | 
					
						
							|  |  |  | 	w.stateLock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Terminate the health checks | 
					
						
							|  |  |  | 	var herr error | 
					
						
							|  |  |  | 	if hQuit != nil { | 
					
						
							|  |  |  | 		errc := make(chan error) | 
					
						
							|  |  |  | 		hQuit <- errc | 
					
						
							|  |  |  | 		herr = <-errc // Save for later, we *must* close the USB | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Terminate the self-derivations | 
					
						
							|  |  |  | 	var derr error | 
					
						
							|  |  |  | 	if dQuit != nil { | 
					
						
							|  |  |  | 		errc := make(chan error) | 
					
						
							|  |  |  | 		dQuit <- errc | 
					
						
							|  |  |  | 		derr = <-errc // Save for later, we *must* close the USB | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Terminate the device connection | 
					
						
							|  |  |  | 	w.stateLock.Lock() | 
					
						
							|  |  |  | 	defer w.stateLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.healthQuit = nil | 
					
						
							|  |  |  | 	w.deriveQuit = nil | 
					
						
							|  |  |  | 	w.deriveReq = nil | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := w.close(); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if herr != nil { | 
					
						
							|  |  |  | 		return herr | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return derr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // close is the internal wallet closer that terminates the USB connection and | 
					
						
							|  |  |  | // resets all the fields to their defaults. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Note, close assumes the state lock is held! | 
					
						
							|  |  |  | func (w *wallet) close() error { | 
					
						
							|  |  |  | 	// Allow duplicate closes, especially for health-check failures | 
					
						
							|  |  |  | 	if w.device == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Close the device, clear everything, then return | 
					
						
							|  |  |  | 	w.device.Close() | 
					
						
							|  |  |  | 	w.device = nil | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.accounts, w.paths = nil, nil | 
					
						
							|  |  |  | 	w.driver.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Accounts implements accounts.Wallet, returning the list of accounts pinned to | 
					
						
							|  |  |  | // the USB hardware wallet. If self-derivation was enabled, the account list is | 
					
						
							|  |  |  | // periodically expanded based on current chain state. | 
					
						
							|  |  |  | func (w *wallet) Accounts() []accounts.Account { | 
					
						
							|  |  |  | 	// Attempt self-derivation if it's running | 
					
						
							|  |  |  | 	reqc := make(chan struct{}, 1) | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case w.deriveReq <- reqc: | 
					
						
							|  |  |  | 		// Self-derivation request accepted, wait for it | 
					
						
							|  |  |  | 		<-reqc | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		// Self-derivation offline, throttled or busy, skip | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Return whatever account list we ended up with | 
					
						
							|  |  |  | 	w.stateLock.RLock() | 
					
						
							|  |  |  | 	defer w.stateLock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cpy := make([]accounts.Account, len(w.accounts)) | 
					
						
							|  |  |  | 	copy(cpy, w.accounts) | 
					
						
							|  |  |  | 	return cpy | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // selfDerive is an account derivation loop that upon request attempts to find | 
					
						
							|  |  |  | // new non-zero accounts. | 
					
						
							|  |  |  | func (w *wallet) selfDerive() { | 
					
						
							|  |  |  | 	w.log.Debug("USB wallet self-derivation started") | 
					
						
							|  |  |  | 	defer w.log.Debug("USB wallet self-derivation stopped") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Execute self-derivations until termination or error | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		reqc chan struct{} | 
					
						
							|  |  |  | 		errc chan error | 
					
						
							|  |  |  | 		err  error | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	for errc == nil && err == nil { | 
					
						
							|  |  |  | 		// Wait until either derivation or termination is requested | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case errc = <-w.deriveQuit: | 
					
						
							|  |  |  | 			// Termination requested | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		case reqc = <-w.deriveReq: | 
					
						
							|  |  |  | 			// Account discovery requested | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Derivation needs a chain and device access, skip if either unavailable | 
					
						
							|  |  |  | 		w.stateLock.RLock() | 
					
						
							|  |  |  | 		if w.device == nil || w.deriveChain == nil { | 
					
						
							|  |  |  | 			w.stateLock.RUnlock() | 
					
						
							|  |  |  | 			reqc <- struct{}{} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-w.commsLock: | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			w.stateLock.RUnlock() | 
					
						
							|  |  |  | 			reqc <- struct{}{} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Device lock obtained, derive the next batch of accounts | 
					
						
							|  |  |  | 		var ( | 
					
						
							|  |  |  | 			accs  []accounts.Account | 
					
						
							|  |  |  | 			paths []accounts.DerivationPath | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			nextAddr = w.deriveNextAddr | 
					
						
							|  |  |  | 			nextPath = w.deriveNextPath | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			context = context.Background() | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 		for empty := false; !empty; { | 
					
						
							|  |  |  | 			// Retrieve the next derived Ethereum account | 
					
						
							|  |  |  | 			if nextAddr == (common.Address{}) { | 
					
						
							|  |  |  | 				if nextAddr, err = w.driver.Derive(nextPath); err != nil { | 
					
						
							|  |  |  | 					w.log.Warn("USB wallet account derivation failed", "err", err) | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Check the account's status against the current chain state | 
					
						
							|  |  |  | 			var ( | 
					
						
							|  |  |  | 				balance *big.Int | 
					
						
							|  |  |  | 				nonce   uint64 | 
					
						
							|  |  |  | 			) | 
					
						
							|  |  |  | 			balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				w.log.Warn("USB wallet balance retrieval failed", "err", err) | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				w.log.Warn("USB wallet nonce retrieval failed", "err", err) | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// If the next account is empty, stop self-derivation, but add it nonetheless | 
					
						
							|  |  |  | 			if balance.Sign() == 0 && nonce == 0 { | 
					
						
							|  |  |  | 				empty = true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// We've just self-derived a new account, start tracking it locally | 
					
						
							|  |  |  | 			path := make(accounts.DerivationPath, len(nextPath)) | 
					
						
							|  |  |  | 			copy(path[:], nextPath[:]) | 
					
						
							|  |  |  | 			paths = append(paths, path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			account := accounts.Account{ | 
					
						
							|  |  |  | 				Address: nextAddr, | 
					
						
							|  |  |  | 				URL:     accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)}, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			accs = append(accs, account) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Display a log message to the user for new (or previously empty accounts) | 
					
						
							|  |  |  | 			if _, known := w.paths[nextAddr]; !known || (!empty && nextAddr == w.deriveNextAddr) { | 
					
						
							|  |  |  | 				w.log.Info("USB wallet discovered new account", "address", nextAddr, "path", path, "balance", balance, "nonce", nonce) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Fetch the next potential account | 
					
						
							|  |  |  | 			if !empty { | 
					
						
							|  |  |  | 				nextAddr = common.Address{} | 
					
						
							|  |  |  | 				nextPath[len(nextPath)-1]++ | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Self derivation complete, release device lock | 
					
						
							|  |  |  | 		w.commsLock <- struct{}{} | 
					
						
							|  |  |  | 		w.stateLock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Insert any accounts successfully derived | 
					
						
							|  |  |  | 		w.stateLock.Lock() | 
					
						
							|  |  |  | 		for i := 0; i < len(accs); i++ { | 
					
						
							|  |  |  | 			if _, ok := w.paths[accs[i].Address]; !ok { | 
					
						
							|  |  |  | 				w.accounts = append(w.accounts, accs[i]) | 
					
						
							|  |  |  | 				w.paths[accs[i].Address] = paths[i] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Shift the self-derivation forward | 
					
						
							|  |  |  | 		// TODO(karalabe): don't overwrite changes from wallet.SelfDerive | 
					
						
							|  |  |  | 		w.deriveNextAddr = nextAddr | 
					
						
							|  |  |  | 		w.deriveNextPath = nextPath | 
					
						
							|  |  |  | 		w.stateLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Notify the user of termination and loop after a bit of time (to avoid trashing) | 
					
						
							|  |  |  | 		reqc <- struct{}{} | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case errc = <-w.deriveQuit: | 
					
						
							|  |  |  | 				// Termination requested, abort | 
					
						
							|  |  |  | 			case <-time.After(selfDeriveThrottling): | 
					
						
							|  |  |  | 				// Waited enough, willing to self-derive again | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// In case of error, wait for termination | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		w.log.Debug("USB wallet self-derivation failed", "err", err) | 
					
						
							|  |  |  | 		errc = <-w.deriveQuit | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	errc <- err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Contains implements accounts.Wallet, returning whether a particular account is | 
					
						
							|  |  |  | // or is not pinned into this wallet instance. Although we could attempt to resolve | 
					
						
							|  |  |  | // unpinned accounts, that would be an non-negligible hardware operation. | 
					
						
							|  |  |  | func (w *wallet) Contains(account accounts.Account) bool { | 
					
						
							|  |  |  | 	w.stateLock.RLock() | 
					
						
							|  |  |  | 	defer w.stateLock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_, exists := w.paths[account.Address] | 
					
						
							|  |  |  | 	return exists | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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 *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { | 
					
						
							|  |  |  | 	// Try to derive the actual account and update its URL if successful | 
					
						
							|  |  |  | 	w.stateLock.RLock() // Avoid device disappearing during derivation | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if w.device == nil { | 
					
						
							|  |  |  | 		w.stateLock.RUnlock() | 
					
						
							|  |  |  | 		return accounts.Account{}, accounts.ErrWalletClosed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	<-w.commsLock // Avoid concurrent hardware access | 
					
						
							|  |  |  | 	address, err := w.driver.Derive(path) | 
					
						
							|  |  |  | 	w.commsLock <- struct{}{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.stateLock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If an error occurred or no pinning was requested, return | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return accounts.Account{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	account := accounts.Account{ | 
					
						
							|  |  |  | 		Address: address, | 
					
						
							|  |  |  | 		URL:     accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !pin { | 
					
						
							|  |  |  | 		return account, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Pinning needs to modify the state | 
					
						
							|  |  |  | 	w.stateLock.Lock() | 
					
						
							|  |  |  | 	defer w.stateLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, ok := w.paths[address]; !ok { | 
					
						
							|  |  |  | 		w.accounts = append(w.accounts, account) | 
					
						
							|  |  |  | 		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 *wallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) { | 
					
						
							|  |  |  | 	w.stateLock.Lock() | 
					
						
							|  |  |  | 	defer w.stateLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.deriveNextPath = make(accounts.DerivationPath, len(base)) | 
					
						
							|  |  |  | 	copy(w.deriveNextPath[:], base[:]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.deriveNextAddr = common.Address{} | 
					
						
							|  |  |  | 	w.deriveChain = chain | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SignHash implements accounts.Wallet, however signing arbitrary data is not | 
					
						
							|  |  |  | // supported for hardware wallets, so this method will always return an error. | 
					
						
							|  |  |  | func (w *wallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) { | 
					
						
							|  |  |  | 	return nil, accounts.ErrNotSupported | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SignTx implements accounts.Wallet. It sends the transaction over to the Ledger | 
					
						
							|  |  |  | // wallet to request a confirmation from the user. It returns either the signed | 
					
						
							|  |  |  | // transaction or a failure if the user denied the transaction. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Note, if the version of the Ethereum application running on the Ledger wallet is | 
					
						
							|  |  |  | // too old to sign EIP-155 transactions, but such is requested nonetheless, an error | 
					
						
							|  |  |  | // will be returned opposed to silently signing in Homestead mode. | 
					
						
							|  |  |  | func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { | 
					
						
							|  |  |  | 	w.stateLock.RLock() // Comms have own mutex, this is for the state fields | 
					
						
							|  |  |  | 	defer w.stateLock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If the wallet is closed, abort | 
					
						
							|  |  |  | 	if w.device == nil { | 
					
						
							|  |  |  | 		return nil, accounts.ErrWalletClosed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Make sure the requested account is contained within | 
					
						
							|  |  |  | 	path, ok := w.paths[account.Address] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, accounts.ErrUnknownAccount | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// All infos gathered and metadata checks out, request signing | 
					
						
							|  |  |  | 	<-w.commsLock | 
					
						
							|  |  |  | 	defer func() { w.commsLock <- struct{}{} }() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Ensure the device isn't screwed with while user confirmation is pending | 
					
						
							|  |  |  | 	// TODO(karalabe): remove if hotplug lands on Windows | 
					
						
							|  |  |  | 	w.hub.commsLock.Lock() | 
					
						
							|  |  |  | 	w.hub.commsPend++ | 
					
						
							|  |  |  | 	w.hub.commsLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		w.hub.commsLock.Lock() | 
					
						
							|  |  |  | 		w.hub.commsPend-- | 
					
						
							|  |  |  | 		w.hub.commsLock.Unlock() | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	// Sign the transaction and verify the sender to avoid hardware fault surprises | 
					
						
							|  |  |  | 	sender, signed, err := w.driver.SignTx(path, tx, chainID) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if sender != account.Address { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return signed, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary | 
					
						
							|  |  |  | // data is not supported for Ledger wallets, so this method will always return | 
					
						
							|  |  |  | // an error. | 
					
						
							|  |  |  | func (w *wallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { | 
					
						
							|  |  |  | 	return w.SignHash(account, hash) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given | 
					
						
							|  |  |  | // transaction with the given account using passphrase as extra authentication. | 
					
						
							|  |  |  | // Since USB wallets don't rely on passphrases, these are silently ignored. | 
					
						
							|  |  |  | func (w *wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { | 
					
						
							|  |  |  | 	return w.SignTx(account, tx, chainID) | 
					
						
							|  |  |  | } |