2017-01-23 09:58:05 +02: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/>.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// 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  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								package  usbwallet  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  (  
						 
					
						
							
								
									
										
										
										
											2017-03-22 18:20:33 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"context" 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"encoding/binary" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"encoding/hex" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"errors" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"fmt" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"io" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"math/big" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"sync" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"time" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									ethereum  "github.com/ethereum/go-ethereum" 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"github.com/ethereum/go-ethereum/accounts" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"github.com/ethereum/go-ethereum/common" 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-22 16:58:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/ethereum/go-ethereum/common/hexutil" 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"github.com/ethereum/go-ethereum/core/types" 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-22 14:10:07 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/ethereum/go-ethereum/log" 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"github.com/ethereum/go-ethereum/rlp" 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/karalabe/hid" 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								)  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// 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  
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// ledgerOpcode is an enumeration encoding the supported Ledger opcodes.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								type  ledgerOpcode  byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// ledgerParam1 is an enumeration encoding the supported Ledger parameters for  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// specific opcodes. The same parameter values may be reused between opcodes.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								type  ledgerParam1  byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// ledgerParam2 is an enumeration encoding the supported Ledger parameters for  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// specific opcodes. The same parameter values may be reused between opcodes.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								type  ledgerParam2  byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								const  (  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									ledgerOpRetrieveAddress   ledgerOpcode  =  0x02  // Returns the public key and Ethereum address for a given BIP 32 path 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									ledgerOpSignTransaction   ledgerOpcode  =  0x04  // Signs an Ethereum transaction after having the user validate the parameters 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									ledgerOpGetConfiguration  ledgerOpcode  =  0x06  // Returns specific wallet application configuration 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									ledgerP1DirectlyFetchAddress     ledgerParam1  =  0x00  // Return address directly from the wallet 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									ledgerP1ConfirmFetchAddress      ledgerParam1  =  0x01  // Require a user confirmation before returning the address 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									ledgerP1InitTransactionData      ledgerParam1  =  0x00  // First transaction data block for signing 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									ledgerP1ContTransactionData      ledgerParam1  =  0x80  // Subsequent transaction data block for signing 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									ledgerP2DiscardAddressChainCode  ledgerParam2  =  0x00  // Do not return the chain code along with the address 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									ledgerP2ReturnAddressChainCode   ledgerParam2  =  0x01  // Require a user confirmation before returning the address 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								)  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// errReplyInvalidHeader is the error message returned by a Ledger data exchange  
						 
					
						
							
								
									
										
										
										
											2017-02-10 14:10:56 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// if the device replies with a mismatching header. This usually means the device  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// is in browser mode.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								var  errReplyInvalidHeader  =  errors . New ( "invalid reply header" )  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// 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" )  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								// ledgerWallet represents a live USB Ledger hardware wallet.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								type  ledgerWallet  struct  {  
						 
					
						
							
								
									
										
										
										
											2017-03-20 13:35:56 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									hub  * LedgerHub     // USB hub the device originates from (TODO(karalabe): remove if hotplug lands on Windows) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									url  * accounts . URL  // Textual URL uniquely identifying this wallet 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									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 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									version   [ 3 ] byte                                     // Current version of the Ledger Ethereum app (zero if app is offline) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-10 14:10:56 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									browser   bool                                        // Flag whether the Ledger is in browser mode (reply channel mismatch) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									accounts  [ ] accounts . Account                          // List of derive accounts pinned on the Ledger 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									paths     map [ common . Address ] accounts . DerivationPath  // Known derivation paths for signing operations 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									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 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// must not be held exlusively during hardware communication. A communication 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// 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 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-22 16:58:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									log  log . Logger  // Contextual logger to tag the ledger with its id 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// URL implements accounts.Wallet, returning the URL of the Ledger device.  
						 
					
						
							
								
									
										
										
										
											2017-02-08 15:53:02 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  URL ( )  accounts . URL  {  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									return  * w . url  // Immutable, no need for a lock 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// Status implements accounts.Wallet, always whether the Ledger is opened, closed  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// or whether the Ethereum app was not started on it.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  Status ( )  string  {  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . stateLock . RLock ( )  // No device communication, state lock is enough 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									defer  w . stateLock . RUnlock ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  w . failure  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  fmt . Sprintf ( "Failed: %v" ,  w . failure ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  w . device  ==  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  "Closed" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-10 14:10:56 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  w . browser  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  "Ethereum app in browser mode" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  w . offline ( )  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 15:53:02 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  "Ethereum app offline" 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 15:53:02 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									return  fmt . Sprintf ( "Ethereum app v%d.%d.%d online" ,  w . version [ 0 ] ,  w . version [ 1 ] ,  w . version [ 2 ] ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// offline returns whether the wallet and the Ethereum app is offline or not.  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// The method assumes that the state lock is held!  
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  offline ( )  bool  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  w . version  ==  [ 3 ] byte { 0 ,  0 ,  0 } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 03:28:22 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// failed returns if the USB device wrapped by the wallet failed for some reason.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// This is used by the device scanner to report failed wallets as departed.  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// The method assumes that the state lock is *not* held!  
						 
					
						
							
								
									
										
										
										
											2017-02-09 03:28:22 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  failed ( )  bool  {  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . stateLock . RLock ( )  // No device communication, state lock is enough 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									defer  w . stateLock . RUnlock ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 03:28:22 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  w . failure  !=  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// Open implements accounts.Wallet, attempting to open a USB connection to the  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// Ledger hardware wallet. The Ledger does not require a user passphrase, so that  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// parameter is silently discarded.  
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  Open ( passphrase  string )  error  {  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . stateLock . Lock ( )  // State lock is enough since there's no connection yet at this point 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									defer  w . stateLock . Unlock ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// If the wallet was already opened, don't try to open again 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  w . device  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  accounts . ErrWalletAlreadyOpen 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// Otherwise iterate over all USB devices and find this again (no way to directly do this) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									device ,  err  :=  w . info . Open ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  err 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// Wallet seems to be successfully opened, guess if the Ethereum app is running 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . device  =  device 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . commsLock  =  make ( chan  struct { } ,  1 ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . commsLock  <-  struct { } { }  // Enable lock 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . paths  =  make ( map [ common . Address ] accounts . DerivationPath ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . deriveReq  =  make ( chan  chan  struct { } ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . deriveQuit  =  make ( chan  chan  error ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . healthQuit  =  make ( chan  chan  error ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									defer  func ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										go  w . heartbeat ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										go  w . selfDerive ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  _ ,  err  =  w . ledgerDerive ( accounts . DefaultBaseDerivationPath ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-10 14:10:56 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										// Ethereum app is not running or in browser mode, nothing more to do, return 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  err  ==  errReplyInvalidHeader  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											w . browser  =  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Try to resolve the Ethereum app's version, will fail prior to v1.0.2 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  w . version ,  err  =  w . ledgerVersion ( ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										w . version  =  [ 3 ] byte { 1 ,  0 ,  0 }  // Assume worst case, can't verify if v1.0.0 or v1.0.1 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  nil 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// heartbeat is a health check loop for the Ledger wallets to periodically verify  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// whether they are still present or if they malfunctioned. It is needed because:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  - libusb on Windows doesn't support hotplug, so we can't detect USB unplugs  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  - communication timeout on the Ledger requires a device power cycle to fix  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  heartbeat ( )  {  
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . log . Debug ( "Ledger health-check started" ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									defer  w . log . Debug ( "Ledger health-check stopped" ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// Execute heartbeat checks until termination or error 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  ( 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										errc  chan  error 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										err   error 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									for  errc  ==  nil  &&  err  ==  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										// Wait until termination is requested or the heartbeat cycle arrives 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										select  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										case  errc  =  <- w . healthQuit : 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											// Termination requested 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											continue 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										case  <- time . After ( ledgerHeartbeatCycle ) : 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											// Heartbeat time 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// Execute a tiny data exchange to see responsiveness 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										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 . ledgerVersion ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										w . commsLock  <-  struct { } { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										w . stateLock . RUnlock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  err  !=  nil  &&  err  !=  errInvalidVersionReply  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											w . stateLock . Lock ( )  // Lock state to tear the wallet down 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											w . failure  =  err 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 03:28:22 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											w . close ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											w . stateLock . Unlock ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										// Ignore non hardware related errors 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										err  =  nil 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// In case of error, wait for termination 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										w . log . Debug ( "Ledger health-check failed" ,  "err" ,  err ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										errc  =  <- w . healthQuit 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									errc  <-  err 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// Close implements accounts.Wallet, closing the USB connection to the Ledger.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  Close ( )  error  {  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// Ensure the wallet was opened 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . stateLock . RLock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									hQuit ,  dQuit  :=  w . healthQuit ,  w . deriveQuit 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . stateLock . RUnlock ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// 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 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// Terminate the device connection 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . stateLock . Lock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									defer  w . stateLock . Unlock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . healthQuit  =  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . deriveQuit  =  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . deriveReq  =  nil 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 03:28:22 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  err  :=  w . close ( ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  herr  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  herr 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  derr 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 03:28:22 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// close is the internal wallet closer that terminates the USB connection and  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// resets all the fields to their defaults.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// Note, close assumes the state lock is held!  
						 
					
						
							
								
									
										
										
										
											2017-02-09 03:28:22 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  close ( )  error  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Allow duplicate closes, especially for health-check failures 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  w . device  ==  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Close the device, clear everything, then return 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . device . Close ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . device  =  nil 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 03:28:22 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-10 14:10:56 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . browser ,  w . version  =  false ,  [ 3 ] byte { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . accounts ,  w . paths  =  nil ,  nil 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 03:28:22 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									return  nil 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 03:28:22 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// Accounts implements accounts.Wallet, returning the list of accounts pinned to  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// the Ledger hardware wallet. If self-derivation was enabled, the account list  
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// is periodically expanded based on current chain state.  
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  Accounts ( )  [ ] accounts . Account  {  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// 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 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// 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  * ledgerWallet )  selfDerive ( )  {  
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . log . Debug ( "Ledger self-derivation started" ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									defer  w . log . Debug ( "Ledger self-derivation stopped" ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// 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 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										// Derivation needs a chain and device access, skip if either unavailable 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										w . stateLock . RLock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  w . device  ==  nil  ||  w . deriveChain  ==  nil  ||  w . offline ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											w . stateLock . RUnlock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											reqc  <-  struct { } { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											continue 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										select  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										case  <- w . commsLock : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										default : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											w . stateLock . RUnlock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											reqc  <-  struct { } { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											continue 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										// 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 . ledgerDerive ( nextPath ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
													w . log . Warn ( "Ledger account derivation failed" ,  "err" ,  err ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
													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  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
												w . log . Warn ( "Ledger balance retrieval failed" ,  "err" ,  err ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
												break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											nonce ,  err  =  w . deriveChain . NonceAt ( context ,  nextAddr ,  nil ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
												w . log . Warn ( "Ledger nonce retrieval failed" ,  "err" ,  err ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
												break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											// If the next account is empty, stop self-derivation, but add it nonetheless 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-28 15:09:11 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											if  balance . Sign ( )  ==  0  &&  nonce  ==  0  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
												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 ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											// Display a log message to the user for new (or previously empty accounts) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  _ ,  known  :=  w . paths [ nextAddr ] ;  ! known  ||  ( ! empty  &&  nextAddr  ==  w . deriveNextAddr )  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
												w . log . Info ( "Ledger discovered new account" ,  "address" ,  nextAddr ,  "path" ,  path ,  "balance" ,  balance ,  "nonce" ,  nonce ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											// Fetch the next potential account 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  ! empty  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												nextAddr  =  common . Address { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												nextPath [ len ( nextPath ) - 1 ] ++ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										// 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 ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										// 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 ( ledgerSelfDeriveThrottling ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												// Waited enough, willing to self-derive again 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// In case of error, wait for termination 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										w . log . Debug ( "Ledger self-derivation failed" ,  "err" ,  err ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										errc  =  <- w . deriveQuit 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									errc  <-  err 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// Contains implements accounts.Wallet, returning whether a particular account is  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// or is not pinned into this Ledger instance. Although we could attempt to resolve  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// unpinned accounts, that would be an non-negligible hardware operation.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  Contains ( account  accounts . Account )  bool  {  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . stateLock . RLock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									defer  w . stateLock . RUnlock ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									_ ,  exists  :=  w . paths [ account . Address ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  exists 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// 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.  
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  Derive ( path  accounts . DerivationPath ,  pin  bool )  ( accounts . Account ,  error )  {  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// Try to derive the actual account and update its URL if successful 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . stateLock . RLock ( )  // Avoid device disappearing during derivation 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  w . device  ==  nil  ||  w . offline ( )  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										w . stateLock . RUnlock ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  accounts . Account { } ,  accounts . ErrWalletClosed 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									<- w . commsLock  // Avoid concurrent hardware access 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									address ,  err  :=  w . ledgerDerive ( path ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . commsLock  <-  struct { } { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . stateLock . RUnlock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// If an error occurred or no pinning was requested, return 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  accounts . Account { } ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									account  :=  accounts . Account { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										Address :  address , 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 15:53:02 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										URL :      accounts . URL { Scheme :  w . url . Scheme ,  Path :  fmt . Sprintf ( "%s/%s" ,  w . url . Path ,  path ) } , 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									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 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  account ,  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// 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 )  {  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . stateLock . Lock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									defer  w . stateLock . Unlock ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . deriveNextPath  =  make ( accounts . DerivationPath ,  len ( base ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									copy ( w . deriveNextPath [ : ] ,  base [ : ] ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . deriveNextAddr  =  common . Address { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . deriveChain  =  chain 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-08 20:25:52 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// 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 )  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									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  * ledgerWallet )  SignTx ( account  accounts . Account ,  tx  * types . Transaction ,  chainID  * big . Int )  ( * types . Transaction ,  error )  {  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . stateLock . RLock ( )  // Comms have own mutex, this is for the state fields 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									defer  w . stateLock . RUnlock ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// If the wallet is closed, or the Ethereum app doesn't run, abort 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  w . device  ==  nil  ||  w . offline ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  accounts . ErrWalletClosed 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// Make sure the requested account is contained within 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									path ,  ok  :=  w . paths [ account . Address ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  ! ok  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  accounts . ErrUnknownAccount 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Ensure the wallet is capable of signing the given transaction 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  chainID  !=  nil  &&  w . version [ 0 ]  <=  1  &&  w . version [ 1 ]  <=  0  &&  w . version [ 2 ]  <=  2  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  nil ,  fmt . Errorf ( "Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least" ,  w . version [ 0 ] ,  w . version [ 1 ] ,  w . version [ 2 ] ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// All infos gathered and metadata checks out, request signing 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									<- w . commsLock 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									defer  func ( )  {  w . commsLock  <-  struct { } { }  } ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-03-20 13:35:56 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// Ensure the device isn't screwed with while user confirmation is pending 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// TODO(karalabe): remove if hotplug lands on Windows 
							 
						 
					
						
							
								
									
										
										
										
											2017-03-23 17:04:39 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									w . hub . commsLock . Lock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . hub . commsPend ++ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									w . hub . commsLock . Unlock ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-03-20 13:35:56 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-03-23 17:04:39 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									defer  func ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										w . hub . commsLock . Lock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										w . hub . commsPend -- 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										w . hub . commsLock . Unlock ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									return  w . ledgerSign ( path ,  account . Address ,  tx ,  chainID ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// data is not supported for Ledger wallets, so this method will always return  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// an error.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  SignHashWithPassphrase ( account  accounts . Account ,  passphrase  string ,  hash  [ ] byte )  ( [ ] byte ,  error )  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  nil ,  accounts . ErrNotSupported 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// transaction with the given account using passphrase as extra authentication.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// Since the Ledger does not support extra passphrases, it is silently ignored.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  SignTxWithPassphrase ( account  accounts . Account ,  passphrase  string ,  tx  * types . Transaction ,  chainID  * big . Int )  ( * types . Transaction ,  error )  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  w . SignTx ( account ,  tx ,  chainID ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// ledgerVersion retrieves the current version of the Ethereum wallet app running  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// on the Ledger wallet.  
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// The version retrieval protocol is defined as follows:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   CLA | INS | P1 | P2 | Lc | Le  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   ----+-----+----+----+----+---  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//    E0 | 06  | 00 | 00 | 00 | 04  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// With no input data, and the output data being:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Description                                        | Length  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   ---------------------------------------------------+--------  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Flags 01: arbitrary data signature enabled by user | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Application major version                          | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Application minor version                          | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Application patch version                          | 1 byte  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  ledgerVersion ( )  ( [ 3 ] byte ,  error )  {  
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									// Send the request and wait for the response 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									reply ,  err  :=  w . ledgerExchange ( ledgerOpGetConfiguration ,  0 ,  0 ,  nil ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  [ 3 ] byte { } ,  err 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  len ( reply )  !=  4  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  [ 3 ] byte { } ,  errInvalidVersionReply 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Cache the version for future reference 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									var  version  [ 3 ] byte 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									copy ( version [ : ] ,  reply [ 1 : ] ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  version ,  nil 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// ledgerDerive retrieves the currently active Ethereum address from a Ledger  
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// wallet at the specified derivation path.  
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// The address derivation protocol is defined as follows:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   CLA | INS | P1 | P2 | Lc  | Le  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   ----+-----+----+----+-----+---  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//    E0 | 02  | 00 return address  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//               01 display address and confirm before returning  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//                  | 00: do not return the chain code  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//                  | 01: return the chain code  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//                       | var | 00  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// Where the input data is:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Description                                      | Length  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   -------------------------------------------------+--------  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Number of BIP 32 derivations to perform (max 10) | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   First derivation index (big endian)              | 4 bytes  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   ...                                              | 4 bytes  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Last derivation index (big endian)               | 4 bytes  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// And the output data is:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Description             | Length  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   ------------------------+-------------------  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Public Key length       | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Uncompressed Public Key | arbitrary  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Ethereum address length | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Ethereum address        | 40 bytes hex ascii  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Chain code if requested | 32 bytes  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  ledgerDerive ( derivationPath  [ ] uint32 )  ( common . Address ,  error )  {  
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									// Flatten the derivation path into the Ledger request 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									path  :=  make ( [ ] byte ,  1 + 4 * len ( derivationPath ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									path [ 0 ]  =  byte ( len ( derivationPath ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  i ,  component  :=  range  derivationPath  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										binary . BigEndian . PutUint32 ( path [ 1 + 4 * i : ] ,  component ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Send the request and wait for the response 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									reply ,  err  :=  w . ledgerExchange ( ledgerOpRetrieveAddress ,  ledgerP1DirectlyFetchAddress ,  ledgerP2DiscardAddressChainCode ,  path ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  common . Address { } ,  err 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Discard the public key, we don't need that for now 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  len ( reply )  <  1  ||  len ( reply )  <  1 + int ( reply [ 0 ] )  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  common . Address { } ,  errors . New ( "reply lacks public key entry" ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									reply  =  reply [ 1 + int ( reply [ 0 ] ) : ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Extract the Ethereum hex address string 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  len ( reply )  <  1  ||  len ( reply )  <  1 + int ( reply [ 0 ] )  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  common . Address { } ,  errors . New ( "reply lacks address entry" ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									hexstr  :=  reply [ 1  :  1 + int ( reply [ 0 ] ) ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Decode the hex sting into an Ethereum address and return 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									var  address  common . Address 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									hex . Decode ( address [ : ] ,  hexstr ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  address ,  nil 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// ledgerSign sends the transaction to the Ledger wallet, and waits for the user  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// to confirm or deny the transaction.  
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// The transaction signing protocol is defined as follows:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   CLA | INS | P1 | P2 | Lc  | Le  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   ----+-----+----+----+-----+---  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//    E0 | 04  | 00: first transaction data block  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//               80: subsequent transaction data block  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//                  | 00 | variable | variable  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// Where the input for the first transaction block (first 255 bytes) is:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Description                                      | Length  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   -------------------------------------------------+----------  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Number of BIP 32 derivations to perform (max 10) | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   First derivation index (big endian)              | 4 bytes  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   ...                                              | 4 bytes  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Last derivation index (big endian)               | 4 bytes  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   RLP transaction chunk                            | arbitrary  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// And the input for subsequent transaction blocks (first 255 bytes) are:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Description           | Length  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   ----------------------+----------  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   RLP transaction chunk | arbitrary  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// And the output data is:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   Description | Length  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   ------------+---------  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   signature V | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   signature R | 32 bytes  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//   signature S | 32 bytes  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  ledgerSign ( derivationPath  [ ] uint32 ,  address  common . Address ,  tx  * types . Transaction ,  chainID  * big . Int )  ( * types . Transaction ,  error )  {  
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									// Flatten the derivation path into the Ledger request 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									path  :=  make ( [ ] byte ,  1 + 4 * len ( derivationPath ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									path [ 0 ]  =  byte ( len ( derivationPath ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  i ,  component  :=  range  derivationPath  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										binary . BigEndian . PutUint32 ( path [ 1 + 4 * i : ] ,  component ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Create the transaction RLP based on whether legacy or EIP155 signing was requeste 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  ( 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										txrlp  [ ] byte 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										err    error 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  chainID  ==  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  txrlp ,  err  =  rlp . EncodeToBytes ( [ ] interface { } { tx . Nonce ( ) ,  tx . GasPrice ( ) ,  tx . Gas ( ) ,  tx . To ( ) ,  tx . Value ( ) ,  tx . Data ( ) } ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  nil ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									}  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  txrlp ,  err  =  rlp . EncodeToBytes ( [ ] interface { } { tx . Nonce ( ) ,  tx . GasPrice ( ) ,  tx . Gas ( ) ,  tx . To ( ) ,  tx . Value ( ) ,  tx . Data ( ) ,  chainID ,  big . NewInt ( 0 ) ,  big . NewInt ( 0 ) } ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  nil ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									payload  :=  append ( path ,  txrlp ... ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Send the request and wait for the response 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  ( 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										op     =  ledgerP1InitTransactionData 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										reply  [ ] byte 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  len ( payload )  >  0  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// Calculate the size of the next data chunk 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										chunk  :=  255 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  chunk  >  len ( payload )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											chunk  =  len ( payload ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// Send the chunk over, ensuring it's processed correctly 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										reply ,  err  =  w . ledgerExchange ( ledgerOpSignTransaction ,  op ,  0 ,  payload [ : chunk ] ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  nil ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// Shift the payload and ensure subsequent chunks are marked as such 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										payload  =  payload [ chunk : ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										op  =  ledgerP1ContTransactionData 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Extract the Ethereum signature and do a sanity validation 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  len ( reply )  !=  65  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  errors . New ( "reply lacks signature" ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									signature  :=  append ( reply [ 1 : ] ,  reply [ 0 ] ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Create the correct signer and signature transform based on the chain ID 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  signer  types . Signer 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  chainID  ==  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										signer  =  new ( types . HomesteadSigner ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									}  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										signer  =  types . NewEIP155Signer ( chainID ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										signature [ 64 ]  =  signature [ 64 ]  -  byte ( chainID . Uint64 ( ) * 2 + 35 ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Inject the final signature into the transaction and sanity check the sender 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									signed ,  err  :=  tx . WithSignature ( signer ,  signature ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									sender ,  err  :=  types . Sender ( signer ,  signed ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-07 12:47:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  sender  !=  address  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  fmt . Errorf ( "signer mismatch: expected %s, got %s" ,  address . Hex ( ) ,  sender . Hex ( ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  signed ,  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// ledgerExchange performs a data exchange with the Ledger wallet, sending it a  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// message and retrieving the response.  
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// The common transport header is defined as follows:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  Description                           | Length  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  --------------------------------------+----------  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  Communication channel ID (big endian) | 2 bytes  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  Command tag                           | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  Packet sequence index (big endian)    | 2 bytes  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  Payload                               | arbitrary  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// The Communication channel ID allows commands multiplexing over the same  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// physical link. It is not used for the time being, and should be set to 0101  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// to avoid compatibility issues with implementations ignoring a leading 00 byte.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// The Command tag describes the message content. Use TAG_APDU (0x05) for standard  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// APDU payloads, or TAG_PING (0x02) for a simple link test.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// The Packet sequence index describes the current sequence for fragmented payloads.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// The first fragment index is 0x00.  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// APDU Command payloads are encoded as follows:  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  Description              | Length  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  -----------------------------------  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  APDU length (big endian) | 2 bytes  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  APDU CLA                 | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  APDU INS                 | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  APDU P1                  | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  APDU P2                  | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  APDU length              | 1 byte  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  Optional APDU data       | arbitrary  
						 
					
						
							
								
									
										
										
										
											2017-02-09 14:39:26 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( w  * ledgerWallet )  ledgerExchange ( opcode  ledgerOpcode ,  p1  ledgerParam1 ,  p2  ledgerParam2 ,  data  [ ] byte )  ( [ ] byte ,  error )  {  
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									// Construct the message payload, possibly split into multiple chunks 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									apdu  :=  make ( [ ] byte ,  2 ,  7 + len ( data ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									binary . BigEndian . PutUint16 ( apdu ,  uint16 ( 5 + len ( data ) ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									apdu  =  append ( apdu ,  [ ] byte { 0xe0 ,  byte ( opcode ) ,  byte ( p1 ) ,  byte ( p2 ) ,  byte ( len ( data ) ) } ... ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									apdu  =  append ( apdu ,  data ... ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									// Stream all the chunks to the device 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									header  :=  [ ] byte { 0x01 ,  0x01 ,  0x05 ,  0x00 ,  0x00 }  // Channel ID and command tag appended 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									chunk  :=  make ( [ ] byte ,  64 ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									space  :=  len ( chunk )  -  len ( header ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									for  i  :=  0 ;  len ( apdu )  >  0 ;  i ++  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// Construct the new message to stream 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										chunk  =  append ( chunk [ : 0 ] ,  header ... ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										binary . BigEndian . PutUint16 ( chunk [ 3 : ] ,  uint16 ( i ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  len ( apdu )  >  space  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											chunk  =  append ( chunk ,  apdu [ : space ] ... ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											apdu  =  apdu [ space : ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										}  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											chunk  =  append ( chunk ,  apdu ... ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											apdu  =  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										// Send over to the device 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										w . log . Trace ( "Data chunk sent to the Ledger" ,  "chunk" ,  hexutil . Bytes ( chunk ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  _ ,  err  :=  w . device . Write ( chunk ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
											return  nil ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// Stream the reply back from the wallet in 64 byte chunks 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  reply  [ ] byte 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									chunk  =  chunk [ : 64 ]  // Yeah, we surely have enough space 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									for  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// Read the next chunk from the Ledger wallet 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-16 15:36:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  _ ,  err  :=  io . ReadFull ( w . device ,  chunk ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
											return  nil ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-28 13:35:17 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										w . log . Trace ( "Data chunk received from the Ledger" ,  "chunk" ,  hexutil . Bytes ( chunk ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-22 14:10:07 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										// Make sure the transport header matches 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  chunk [ 0 ]  !=  0x01  ||  chunk [ 1 ]  !=  0x01  ||  chunk [ 2 ]  !=  0x05  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-10 14:10:56 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											return  nil ,  errReplyInvalidHeader 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// If it's the first chunk, retrieve the total message length 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										var  payload  [ ] byte 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										if  chunk [ 3 ]  ==  0x00  &&  chunk [ 4 ]  ==  0x00  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											reply  =  make ( [ ] byte ,  0 ,  int ( binary . BigEndian . Uint16 ( chunk [ 5 : 7 ] ) ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											payload  =  chunk [ 7 : ] 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										}  else  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											payload  =  chunk [ 5 : ] 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// Append to the reply and stop when filled up 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  left  :=  cap ( reply )  -  len ( reply ) ;  left  >  len ( payload )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											reply  =  append ( reply ,  payload ... ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										}  else  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-02-11 17:02:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											reply  =  append ( reply ,  payload [ : left ] ... ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-01-23 09:58:05 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
											break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  reply [ : len ( reply ) - 2 ] ,  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}