From fb1984685562479bfe2b041ae98f901525d1d9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 9 Feb 2017 03:28:22 +0200 Subject: [PATCH] accounts/usbwallet: Ledger teardown on health-check failure --- accounts/usbwallet/ledger_hub.go | 8 +++---- accounts/usbwallet/ledger_wallet.go | 33 +++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/accounts/usbwallet/ledger_hub.go b/accounts/usbwallet/ledger_hub.go index bd397249f..ad5940cd4 100644 --- a/accounts/usbwallet/ledger_hub.go +++ b/accounts/usbwallet/ledger_hub.go @@ -130,12 +130,12 @@ func (hub *LedgerHub) refreshWallets() { url := accounts.URL{Scheme: LedgerScheme, Path: fmt.Sprintf("%03d:%03d", busID>>8, busID&0xff)} - // Drop wallets while they were in front of the next account - for len(hub.wallets) > 0 && hub.wallets[0].URL().Cmp(url) < 0 { + // Drop wallets in front of the next device or those that failed for some reason + for len(hub.wallets) > 0 && (hub.wallets[0].URL().Cmp(url) < 0 || hub.wallets[0].(*ledgerWallet).failed()) { events = append(events, accounts.WalletEvent{Wallet: hub.wallets[0], Arrive: false}) hub.wallets = hub.wallets[1:] } - // If there are no more wallets or the account is before the next, wrap new wallet + // If there are no more wallets or the device is before the next, wrap new wallet if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 { wallet := &ledgerWallet{context: hub.ctx, hardwareID: devID, locationID: busID, url: &url} @@ -143,7 +143,7 @@ func (hub *LedgerHub) refreshWallets() { wallets = append(wallets, wallet) continue } - // If the account is the same as the first wallet, keep it + // If the device is the same as the first wallet, keep it if hub.wallets[0].URL().Cmp(url) == 0 { wallets = append(wallets, hub.wallets[0]) hub.wallets = hub.wallets[1:] diff --git a/accounts/usbwallet/ledger_wallet.go b/accounts/usbwallet/ledger_wallet.go index 0481a8990..4f848aebd 100644 --- a/accounts/usbwallet/ledger_wallet.go +++ b/accounts/usbwallet/ledger_wallet.go @@ -128,6 +128,15 @@ func (w *ledgerWallet) offline() bool { return w.version == [3]byte{0, 0, 0} } +// 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. +func (w *ledgerWallet) failed() bool { + w.lock.RLock() + defer w.lock.RUnlock() + + return w.failure != nil +} + // Open implements accounts.Wallet, attempting to open a USB connection to the // Ledger hardware wallet. The Ledger does not require a user passphrase so that // is silently discarded. @@ -231,6 +240,8 @@ func (w *ledgerWallet) heartbeat() { w.lock.Lock() if err := w.resolveVersion(); err == usb.ERROR_IO || err == usb.ERROR_NO_DEVICE { w.failure = err + w.close() + fail = err } w.lock.Unlock() @@ -253,15 +264,29 @@ func (w *ledgerWallet) Close() error { w.lock.Lock() defer w.lock.Unlock() - if err := w.device.Close(); err != nil { + w.quit = nil + if err := w.close(); err != nil { return err } - w.device, w.input, w.output, w.paths, w.quit = nil, nil, nil, nil, nil - w.version = [3]byte{} - return herr // If all went well, return any health-check errors } +// close is the internal wallet closer that terminates the USB connection and +// resets all the fields to their defaults. It assumes the lock is held. +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 + err := w.device.Close() + + w.device, w.input, w.output = nil, nil, nil + w.version, w.paths = [3]byte{}, nil + + return err +} + // Accounts implements accounts.Wallet, returning the list of accounts pinned to // the Ledger hardware wallet. If self derivation was enabled, the account list // is periodically expanded based on current chain state.