wallet.go 22 KB

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