wallet.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. // Copyright 2017 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. // Package usbwallet implements support for USB hardware wallets.
  17. package usbwallet
  18. import (
  19. "context"
  20. "errors"
  21. "fmt"
  22. "io"
  23. "math/big"
  24. "sync"
  25. "time"
  26. "github.com/ethereum/go-ethereum"
  27. "github.com/ethereum/go-ethereum/accounts"
  28. "github.com/ethereum/go-ethereum/common"
  29. "github.com/ethereum/go-ethereum/core/types"
  30. "github.com/ethereum/go-ethereum/crypto"
  31. "github.com/ethereum/go-ethereum/log"
  32. "github.com/karalabe/usb"
  33. )
  34. // Maximum time between wallet health checks to detect USB unplugs.
  35. const heartbeatCycle = time.Second
  36. // Minimum time to wait between self derivation attempts, even it the user is
  37. // requesting accounts like crazy.
  38. const selfDeriveThrottling = time.Second
  39. // driver defines the vendor specific functionality hardware wallets instances
  40. // must implement to allow using them with the wallet lifecycle management.
  41. type driver interface {
  42. // Status returns a textual status to aid the user in the current state of the
  43. // wallet. It also returns an error indicating any failure the wallet might have
  44. // encountered.
  45. Status() (string, error)
  46. // Open initializes access to a wallet instance. The passphrase parameter may
  47. // or may not be used by the implementation of a particular wallet instance.
  48. Open(device io.ReadWriter, passphrase string) error
  49. // Close releases any resources held by an open wallet instance.
  50. Close() error
  51. // Heartbeat performs a sanity check against the hardware wallet to see if it
  52. // is still online and healthy.
  53. Heartbeat() error
  54. // Derive sends a derivation request to the USB device and returns the Ethereum
  55. // address located on that path.
  56. Derive(path accounts.DerivationPath) (common.Address, error)
  57. // SignTx sends the transaction to the USB device and waits for the user to confirm
  58. // or deny the transaction.
  59. SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error)
  60. SignTypedMessage(path accounts.DerivationPath, messageHash []byte, domainHash []byte) ([]byte, error)
  61. }
  62. // wallet represents the common functionality shared by all USB hardware
  63. // wallets to prevent reimplementing the same complex maintenance mechanisms
  64. // for different vendors.
  65. type wallet struct {
  66. hub *Hub // USB hub scanning
  67. driver driver // Hardware implementation of the low level device operations
  68. url *accounts.URL // Textual URL uniquely identifying this wallet
  69. info usb.DeviceInfo // Known USB device infos about the wallet
  70. device usb.Device // USB device advertising itself as a hardware wallet
  71. accounts []accounts.Account // List of derive accounts pinned on the hardware wallet
  72. paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations
  73. deriveNextPaths []accounts.DerivationPath // Next derivation paths for account auto-discovery (multiple bases supported)
  74. deriveNextAddrs []common.Address // Next derived account addresses for auto-discovery (multiple bases supported)
  75. deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with
  76. deriveReq chan chan struct{} // Channel to request a self-derivation on
  77. deriveQuit chan chan error // Channel to terminate the self-deriver with
  78. healthQuit chan chan error
  79. // Locking a hardware wallet is a bit special. Since hardware devices are lower
  80. // performing, any communication with them might take a non negligible amount of
  81. // time. Worse still, waiting for user confirmation can take arbitrarily long,
  82. // but exclusive communication must be upheld during. Locking the entire wallet
  83. // in the mean time however would stall any parts of the system that don't want
  84. // to communicate, just read some state (e.g. list the accounts).
  85. //
  86. // As such, a hardware wallet needs two locks to function correctly. A state
  87. // lock can be used to protect the wallet's software-side internal state, which
  88. // must not be held exclusively during hardware communication. A communication
  89. // lock can be used to achieve exclusive access to the device itself, this one
  90. // however should allow "skipping" waiting for operations that might want to
  91. // use the device, but can live without too (e.g. account self-derivation).
  92. //
  93. // Since we have two locks, it's important to know how to properly use them:
  94. // - Communication requires the `device` to not change, so obtaining the
  95. // commsLock should be done after having a stateLock.
  96. // - Communication must not disable read access to the wallet state, so it
  97. // must only ever hold a *read* lock to stateLock.
  98. commsLock chan struct{} // Mutex (buf=1) for the USB comms without keeping the state locked
  99. stateLock sync.RWMutex // Protects read and write access to the wallet struct fields
  100. log log.Logger // Contextual logger to tag the base with its id
  101. }
  102. // URL implements accounts.Wallet, returning the URL of the USB hardware device.
  103. func (w *wallet) URL() accounts.URL {
  104. return *w.url // Immutable, no need for a lock
  105. }
  106. // Status implements accounts.Wallet, returning a custom status message from the
  107. // underlying vendor-specific hardware wallet implementation.
  108. func (w *wallet) Status() (string, error) {
  109. w.stateLock.RLock() // No device communication, state lock is enough
  110. defer w.stateLock.RUnlock()
  111. status, failure := w.driver.Status()
  112. if w.device == nil {
  113. return "Closed", failure
  114. }
  115. return status, failure
  116. }
  117. // Open implements accounts.Wallet, attempting to open a USB connection to the
  118. // hardware wallet.
  119. func (w *wallet) Open(passphrase string) error {
  120. w.stateLock.Lock() // State lock is enough since there's no connection yet at this point
  121. defer w.stateLock.Unlock()
  122. // If the device was already opened once, refuse to try again
  123. if w.paths != nil {
  124. return accounts.ErrWalletAlreadyOpen
  125. }
  126. // Make sure the actual device connection is done only once
  127. if w.device == nil {
  128. device, err := w.info.Open()
  129. if err != nil {
  130. return err
  131. }
  132. w.device = device
  133. w.commsLock = make(chan struct{}, 1)
  134. w.commsLock <- struct{}{} // Enable lock
  135. }
  136. // Delegate device initialization to the underlying driver
  137. if err := w.driver.Open(w.device, passphrase); err != nil {
  138. return err
  139. }
  140. // Connection successful, start life-cycle management
  141. w.paths = make(map[common.Address]accounts.DerivationPath)
  142. w.deriveReq = make(chan chan struct{})
  143. w.deriveQuit = make(chan chan error)
  144. w.healthQuit = make(chan chan error)
  145. go w.heartbeat()
  146. go w.selfDerive()
  147. // Notify anyone listening for wallet events that a new device is accessible
  148. go w.hub.updateFeed.Send(accounts.WalletEvent{Wallet: w, Kind: accounts.WalletOpened})
  149. return nil
  150. }
  151. // heartbeat is a health check loop for the USB wallets to periodically verify
  152. // whether they are still present or if they malfunctioned.
  153. func (w *wallet) heartbeat() {
  154. w.log.Debug("USB wallet health-check started")
  155. defer w.log.Debug("USB wallet health-check stopped")
  156. // Execute heartbeat checks until termination or error
  157. var (
  158. errc chan error
  159. err error
  160. )
  161. for errc == nil && err == nil {
  162. // Wait until termination is requested or the heartbeat cycle arrives
  163. select {
  164. case errc = <-w.healthQuit:
  165. // Termination requested
  166. continue
  167. case <-time.After(heartbeatCycle):
  168. // Heartbeat time
  169. }
  170. // Execute a tiny data exchange to see responsiveness
  171. w.stateLock.RLock()
  172. if w.device == nil {
  173. // Terminated while waiting for the lock
  174. w.stateLock.RUnlock()
  175. continue
  176. }
  177. <-w.commsLock // Don't lock state while resolving version
  178. err = w.driver.Heartbeat()
  179. w.commsLock <- struct{}{}
  180. w.stateLock.RUnlock()
  181. if err != nil {
  182. w.stateLock.Lock() // Lock state to tear the wallet down
  183. w.close()
  184. w.stateLock.Unlock()
  185. }
  186. // Ignore non hardware related errors
  187. err = nil
  188. }
  189. // In case of error, wait for termination
  190. if err != nil {
  191. w.log.Debug("USB wallet health-check failed", "err", err)
  192. errc = <-w.healthQuit
  193. }
  194. errc <- err
  195. }
  196. // Close implements accounts.Wallet, closing the USB connection to the device.
  197. func (w *wallet) Close() error {
  198. // Ensure the wallet was opened
  199. w.stateLock.RLock()
  200. hQuit, dQuit := w.healthQuit, w.deriveQuit
  201. w.stateLock.RUnlock()
  202. // Terminate the health checks
  203. var herr error
  204. if hQuit != nil {
  205. errc := make(chan error)
  206. hQuit <- errc
  207. herr = <-errc // Save for later, we *must* close the USB
  208. }
  209. // Terminate the self-derivations
  210. var derr error
  211. if dQuit != nil {
  212. errc := make(chan error)
  213. dQuit <- errc
  214. derr = <-errc // Save for later, we *must* close the USB
  215. }
  216. // Terminate the device connection
  217. w.stateLock.Lock()
  218. defer w.stateLock.Unlock()
  219. w.healthQuit = nil
  220. w.deriveQuit = nil
  221. w.deriveReq = nil
  222. if err := w.close(); err != nil {
  223. return err
  224. }
  225. if herr != nil {
  226. return herr
  227. }
  228. return derr
  229. }
  230. // close is the internal wallet closer that terminates the USB connection and
  231. // resets all the fields to their defaults.
  232. //
  233. // Note, close assumes the state lock is held!
  234. func (w *wallet) close() error {
  235. // Allow duplicate closes, especially for health-check failures
  236. if w.device == nil {
  237. return nil
  238. }
  239. // Close the device, clear everything, then return
  240. w.device.Close()
  241. w.device = nil
  242. w.accounts, w.paths = nil, nil
  243. return w.driver.Close()
  244. }
  245. // Accounts implements accounts.Wallet, returning the list of accounts pinned to
  246. // the USB hardware wallet. If self-derivation was enabled, the account list is
  247. // periodically expanded based on current chain state.
  248. func (w *wallet) Accounts() []accounts.Account {
  249. // Attempt self-derivation if it's running
  250. reqc := make(chan struct{}, 1)
  251. select {
  252. case w.deriveReq <- reqc:
  253. // Self-derivation request accepted, wait for it
  254. <-reqc
  255. default:
  256. // Self-derivation offline, throttled or busy, skip
  257. }
  258. // Return whatever account list we ended up with
  259. w.stateLock.RLock()
  260. defer w.stateLock.RUnlock()
  261. cpy := make([]accounts.Account, len(w.accounts))
  262. copy(cpy, w.accounts)
  263. return cpy
  264. }
  265. // selfDerive is an account derivation loop that upon request attempts to find
  266. // new non-zero accounts.
  267. func (w *wallet) selfDerive() {
  268. w.log.Debug("USB wallet self-derivation started")
  269. defer w.log.Debug("USB wallet self-derivation stopped")
  270. // Execute self-derivations until termination or error
  271. var (
  272. reqc chan struct{}
  273. errc chan error
  274. err error
  275. )
  276. for errc == nil && err == nil {
  277. // Wait until either derivation or termination is requested
  278. select {
  279. case errc = <-w.deriveQuit:
  280. // Termination requested
  281. continue
  282. case reqc = <-w.deriveReq:
  283. // Account discovery requested
  284. }
  285. // Derivation needs a chain and device access, skip if either unavailable
  286. w.stateLock.RLock()
  287. if w.device == nil || w.deriveChain == nil {
  288. w.stateLock.RUnlock()
  289. reqc <- struct{}{}
  290. continue
  291. }
  292. select {
  293. case <-w.commsLock:
  294. default:
  295. w.stateLock.RUnlock()
  296. reqc <- struct{}{}
  297. continue
  298. }
  299. // Device lock obtained, derive the next batch of accounts
  300. var (
  301. accs []accounts.Account
  302. paths []accounts.DerivationPath
  303. nextPaths = append([]accounts.DerivationPath{}, w.deriveNextPaths...)
  304. nextAddrs = append([]common.Address{}, w.deriveNextAddrs...)
  305. context = context.Background()
  306. )
  307. for i := 0; i < len(nextAddrs); i++ {
  308. for empty := false; !empty; {
  309. // Retrieve the next derived Ethereum account
  310. if nextAddrs[i] == (common.Address{}) {
  311. if nextAddrs[i], err = w.driver.Derive(nextPaths[i]); err != nil {
  312. w.log.Warn("USB wallet account derivation failed", "err", err)
  313. break
  314. }
  315. }
  316. // Check the account's status against the current chain state
  317. var (
  318. balance *big.Int
  319. nonce uint64
  320. )
  321. balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
  322. if err != nil {
  323. w.log.Warn("USB wallet balance retrieval failed", "err", err)
  324. break
  325. }
  326. nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
  327. if err != nil {
  328. w.log.Warn("USB wallet nonce retrieval failed", "err", err)
  329. break
  330. }
  331. // We've just self-derived a new account, start tracking it locally
  332. // unless the account was empty.
  333. path := make(accounts.DerivationPath, len(nextPaths[i]))
  334. copy(path[:], nextPaths[i][:])
  335. if balance.Sign() == 0 && nonce == 0 {
  336. empty = true
  337. // If it indeed was empty, make a log output for it anyway. In the case
  338. // of legacy-ledger, the first account on the legacy-path will
  339. // be shown to the user, even if we don't actively track it
  340. if i < len(nextAddrs)-1 {
  341. w.log.Info("Skipping trakcking first account on legacy path, use personal.deriveAccount(<url>,<path>, false) to track",
  342. "path", path, "address", nextAddrs[i])
  343. break
  344. }
  345. }
  346. paths = append(paths, path)
  347. account := accounts.Account{
  348. Address: nextAddrs[i],
  349. URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
  350. }
  351. accs = append(accs, account)
  352. // Display a log message to the user for new (or previously empty accounts)
  353. if _, known := w.paths[nextAddrs[i]]; !known || (!empty && nextAddrs[i] == w.deriveNextAddrs[i]) {
  354. w.log.Info("USB wallet discovered new account", "address", nextAddrs[i], "path", path, "balance", balance, "nonce", nonce)
  355. }
  356. // Fetch the next potential account
  357. if !empty {
  358. nextAddrs[i] = common.Address{}
  359. nextPaths[i][len(nextPaths[i])-1]++
  360. }
  361. }
  362. }
  363. // Self derivation complete, release device lock
  364. w.commsLock <- struct{}{}
  365. w.stateLock.RUnlock()
  366. // Insert any accounts successfully derived
  367. w.stateLock.Lock()
  368. for i := 0; i < len(accs); i++ {
  369. if _, ok := w.paths[accs[i].Address]; !ok {
  370. w.accounts = append(w.accounts, accs[i])
  371. w.paths[accs[i].Address] = paths[i]
  372. }
  373. }
  374. // Shift the self-derivation forward
  375. // TODO(karalabe): don't overwrite changes from wallet.SelfDerive
  376. w.deriveNextAddrs = nextAddrs
  377. w.deriveNextPaths = nextPaths
  378. w.stateLock.Unlock()
  379. // Notify the user of termination and loop after a bit of time (to avoid trashing)
  380. reqc <- struct{}{}
  381. if err == nil {
  382. select {
  383. case errc = <-w.deriveQuit:
  384. // Termination requested, abort
  385. case <-time.After(selfDeriveThrottling):
  386. // Waited enough, willing to self-derive again
  387. }
  388. }
  389. }
  390. // In case of error, wait for termination
  391. if err != nil {
  392. w.log.Debug("USB wallet self-derivation failed", "err", err)
  393. errc = <-w.deriveQuit
  394. }
  395. errc <- err
  396. }
  397. // Contains implements accounts.Wallet, returning whether a particular account is
  398. // or is not pinned into this wallet instance. Although we could attempt to resolve
  399. // unpinned accounts, that would be an non-negligible hardware operation.
  400. func (w *wallet) Contains(account accounts.Account) bool {
  401. w.stateLock.RLock()
  402. defer w.stateLock.RUnlock()
  403. _, exists := w.paths[account.Address]
  404. return exists
  405. }
  406. // Derive implements accounts.Wallet, deriving a new account at the specific
  407. // derivation path. If pin is set to true, the account will be added to the list
  408. // of tracked accounts.
  409. func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
  410. // Try to derive the actual account and update its URL if successful
  411. w.stateLock.RLock() // Avoid device disappearing during derivation
  412. if w.device == nil {
  413. w.stateLock.RUnlock()
  414. return accounts.Account{}, accounts.ErrWalletClosed
  415. }
  416. <-w.commsLock // Avoid concurrent hardware access
  417. address, err := w.driver.Derive(path)
  418. w.commsLock <- struct{}{}
  419. w.stateLock.RUnlock()
  420. // If an error occurred or no pinning was requested, return
  421. if err != nil {
  422. return accounts.Account{}, err
  423. }
  424. account := accounts.Account{
  425. Address: address,
  426. URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
  427. }
  428. if !pin {
  429. return account, nil
  430. }
  431. // Pinning needs to modify the state
  432. w.stateLock.Lock()
  433. defer w.stateLock.Unlock()
  434. if _, ok := w.paths[address]; !ok {
  435. w.accounts = append(w.accounts, account)
  436. w.paths[address] = make(accounts.DerivationPath, len(path))
  437. copy(w.paths[address], path)
  438. }
  439. return account, nil
  440. }
  441. // SelfDerive sets a base account derivation path from which the wallet attempts
  442. // to discover non zero accounts and automatically add them to list of tracked
  443. // accounts.
  444. //
  445. // Note, self derivation will increment the last component of the specified path
  446. // opposed to decending into a child path to allow discovering accounts starting
  447. // from non zero components.
  448. //
  449. // Some hardware wallets switched derivation paths through their evolution, so
  450. // this method supports providing multiple bases to discover old user accounts
  451. // too. Only the last base will be used to derive the next empty account.
  452. //
  453. // You can disable automatic account discovery by calling SelfDerive with a nil
  454. // chain state reader.
  455. func (w *wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) {
  456. w.stateLock.Lock()
  457. defer w.stateLock.Unlock()
  458. w.deriveNextPaths = make([]accounts.DerivationPath, len(bases))
  459. for i, base := range bases {
  460. w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
  461. copy(w.deriveNextPaths[i][:], base[:])
  462. }
  463. w.deriveNextAddrs = make([]common.Address, len(bases))
  464. w.deriveChain = chain
  465. }
  466. // signHash implements accounts.Wallet, however signing arbitrary data is not
  467. // supported for hardware wallets, so this method will always return an error.
  468. func (w *wallet) signHash(account accounts.Account, hash []byte) ([]byte, error) {
  469. return nil, accounts.ErrNotSupported
  470. }
  471. // SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
  472. func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
  473. // Unless we are doing 712 signing, simply dispatch to signHash
  474. if !(mimeType == accounts.MimetypeTypedData && len(data) == 66 && data[0] == 0x19 && data[1] == 0x01) {
  475. return w.signHash(account, crypto.Keccak256(data))
  476. }
  477. // dispatch to 712 signing if the mimetype is TypedData and the format matches
  478. w.stateLock.RLock() // Comms have own mutex, this is for the state fields
  479. defer w.stateLock.RUnlock()
  480. // If the wallet is closed, abort
  481. if w.device == nil {
  482. return nil, accounts.ErrWalletClosed
  483. }
  484. // Make sure the requested account is contained within
  485. path, ok := w.paths[account.Address]
  486. if !ok {
  487. return nil, accounts.ErrUnknownAccount
  488. }
  489. // All infos gathered and metadata checks out, request signing
  490. <-w.commsLock
  491. defer func() { w.commsLock <- struct{}{} }()
  492. // Ensure the device isn't screwed with while user confirmation is pending
  493. // TODO(karalabe): remove if hotplug lands on Windows
  494. w.hub.commsLock.Lock()
  495. w.hub.commsPend++
  496. w.hub.commsLock.Unlock()
  497. defer func() {
  498. w.hub.commsLock.Lock()
  499. w.hub.commsPend--
  500. w.hub.commsLock.Unlock()
  501. }()
  502. // Sign the transaction
  503. signature, err := w.driver.SignTypedMessage(path, data[2:34], data[34:66])
  504. if err != nil {
  505. return nil, err
  506. }
  507. return signature, nil
  508. }
  509. // SignDataWithPassphrase implements accounts.Wallet, attempting to sign the given
  510. // data with the given account using passphrase as extra authentication.
  511. // Since USB wallets don't rely on passphrases, these are silently ignored.
  512. func (w *wallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
  513. return w.SignData(account, mimeType, data)
  514. }
  515. func (w *wallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
  516. return w.signHash(account, accounts.TextHash(text))
  517. }
  518. // SignTx implements accounts.Wallet. It sends the transaction over to the Ledger
  519. // wallet to request a confirmation from the user. It returns either the signed
  520. // transaction or a failure if the user denied the transaction.
  521. //
  522. // Note, if the version of the Ethereum application running on the Ledger wallet is
  523. // too old to sign EIP-155 transactions, but such is requested nonetheless, an error
  524. // will be returned opposed to silently signing in Homestead mode.
  525. func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
  526. w.stateLock.RLock() // Comms have own mutex, this is for the state fields
  527. defer w.stateLock.RUnlock()
  528. if tx.IsPrivate() {
  529. return nil, errors.New("Signing Quorum Private transactions with a USB wallet not yet supported")
  530. }
  531. // If the wallet is closed, abort
  532. if w.device == nil {
  533. return nil, accounts.ErrWalletClosed
  534. }
  535. // Make sure the requested account is contained within
  536. path, ok := w.paths[account.Address]
  537. if !ok {
  538. return nil, accounts.ErrUnknownAccount
  539. }
  540. // All infos gathered and metadata checks out, request signing
  541. <-w.commsLock
  542. defer func() { w.commsLock <- struct{}{} }()
  543. // Ensure the device isn't screwed with while user confirmation is pending
  544. // TODO(karalabe): remove if hotplug lands on Windows
  545. w.hub.commsLock.Lock()
  546. w.hub.commsPend++
  547. w.hub.commsLock.Unlock()
  548. defer func() {
  549. w.hub.commsLock.Lock()
  550. w.hub.commsPend--
  551. w.hub.commsLock.Unlock()
  552. }()
  553. // Sign the transaction and verify the sender to avoid hardware fault surprises
  554. sender, signed, err := w.driver.SignTx(path, tx, chainID)
  555. if err != nil {
  556. return nil, err
  557. }
  558. if sender != account.Address {
  559. return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex())
  560. }
  561. return signed, nil
  562. }
  563. // SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary
  564. // data is not supported for Ledger wallets, so this method will always return
  565. // an error.
  566. func (w *wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
  567. return w.SignText(account, accounts.TextHash(text))
  568. }
  569. // SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
  570. // transaction with the given account using passphrase as extra authentication.
  571. // Since USB wallets don't rely on passphrases, these are silently ignored.
  572. func (w *wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
  573. return w.SignTx(account, tx, chainID)
  574. }