commit e61fffab55f8cb59638de7d77ab1791611bd466e
parent abaf1bc6dca5971653d0d7123904c0c4b128b181
Author: Neil Booth <kyuupichan@gmail.com>
Date: Sun, 31 Jan 2016 19:36:21 +0900
Trezor/KeepKey: force watching only improvements
Only warn about watching only once given a chance to pair.
Failure to pair makes watching-only and warns.
In error message to user, distinguish between failure to connect
and failure to pair.
Diffstat:
4 files changed, 50 insertions(+), 31 deletions(-)
diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
@@ -311,6 +311,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
title = 'Electrum %s - %s' % (self.wallet.electrum_version,
self.wallet.basename())
if self.wallet.is_watching_only():
+ self.warn_if_watching_only()
title += ' [%s]' % (_('watching only'))
self.setWindowTitle(title)
self.password_menu.setEnabled(self.wallet.can_change_password())
diff --git a/lib/plugins.py b/lib/plugins.py
@@ -234,6 +234,13 @@ class BasePlugin(PrintError):
def settings_dialog(self):
pass
+
+class DeviceNotFoundError(Exception):
+ pass
+
+class DeviceUnpairableError(Exception):
+ pass
+
Device = namedtuple("Device", "path interface_number id_ product_key")
DeviceInfo = namedtuple("DeviceInfo", "device description initialized")
@@ -368,25 +375,38 @@ class DeviceMgr(PrintError):
return self.create_client(device, wallet.handler, plugin)
if force_pair:
- first_address, derivation = wallet.first_address()
- assert first_address
-
- # The wallet has not been previously paired, so let the user
- # choose an unpaired device and compare its first address.
- info = self.select_device(wallet, plugin, devices)
- if info:
- client = self.client_lookup(info.device.id_)
- if client and client.is_pairable():
- # See comment above for same code
- client.handler = wallet.handler
- # This will trigger a PIN/passphrase entry request
- client_first_address = client.first_address(derivation)
- if client_first_address == first_address:
- self.pair_wallet(wallet, info.device.id_)
- return client
+ return self.force_pair_wallet(plugin, wallet, devices)
return None
+ def force_pair_wallet(self, plugin, wallet, devices):
+ first_address, derivation = wallet.first_address()
+ assert first_address
+
+ # The wallet has not been previously paired, so let the user
+ # choose an unpaired device and compare its first address.
+ info = self.select_device(wallet, plugin, devices)
+ if info:
+ client = self.client_lookup(info.device.id_)
+ if client and client.is_pairable():
+ # See comment above for same code
+ client.handler = wallet.handler
+ # This will trigger a PIN/passphrase entry request
+ client_first_address = client.first_address(derivation)
+ if client_first_address == first_address:
+ self.pair_wallet(wallet, info.device.id_)
+ return client
+
+ if info and client:
+ # The user input has wrong PIN or passphrase
+ raise DeviceUnpairableError(
+ _('Unable to pair with your %s.') % plugin.device)
+
+ raise DeviceNotFoundError(
+ _('Could not connect to your %s. Verify the cable is '
+ 'connected and that no other application is using it.')
+ % plugin.device)
+
def unpaired_device_infos(self, handler, plugin, devices=None):
'''Returns a list of DeviceInfo objects: one for each connected,
unpaired device accepted by the plugin.'''
diff --git a/plugins/hw_wallet/hw_wallet.py b/plugins/hw_wallet/hw_wallet.py
@@ -39,26 +39,29 @@ class BIP44_HW_Wallet(BIP44_Wallet):
# handler. The handler is per-window and preserved across
# device reconnects
self.handler = None
- self.force_watching_only = True
+ self.force_watching_only = False
def set_session_timeout(self, seconds):
self.print_error("setting session timeout to %d seconds" % seconds)
self.session_timeout = seconds
self.storage.put('session_timeout', seconds)
+ def set_force_watching_only(self, value):
+ if value != self.force_watching_only:
+ self.force_watching_only = value
+ self.handler.watching_only_changed()
+
def unpaired(self):
'''A device paired with the wallet was diconnected. This can be
called in any thread context.'''
self.print_error("unpaired")
- self.force_watching_only = True
- self.handler.watching_only_changed()
+ self.set_force_watching_only(True)
def paired(self):
'''A device paired with the wallet was (re-)connected. This can be
called in any thread context.'''
self.print_error("paired")
- self.force_watching_only = False
- self.handler.watching_only_changed()
+ self.set_force_watching_only(False)
def timeout(self):
'''Called when the wallet session times out. Note this is called from
diff --git a/plugins/trezor/plugin.py b/plugins/trezor/plugin.py
@@ -20,9 +20,6 @@ from ..hw_wallet import BIP44_HW_Wallet, HW_PluginBase
# TREZOR initialization methods
TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY = range(0, 4)
-class DeviceDisconnectedError(Exception):
- pass
-
class TrezorCompatibleWallet(BIP44_HW_Wallet):
def get_public_key(self, bip32_path):
@@ -137,17 +134,15 @@ class TrezorCompatiblePlugin(HW_PluginBase):
assert self.main_thread != threading.current_thread()
devmgr = self.device_manager()
- client = devmgr.client_for_wallet(self, wallet, force_pair)
+ try:
+ client = devmgr.client_for_wallet(self, wallet, force_pair)
+ except:
+ wallet.set_force_watching_only(True)
+ raise
if client:
self.print_error("set last_operation")
wallet.last_operation = time.time()
- elif force_pair:
- msg = (_('Could not connect to your %s. Verify the '
- 'cable is connected and that no other app is '
- 'using it.\nContinuing in watching-only mode '
- 'until the device is re-connected.') % self.device)
- raise DeviceDisconnectedError(msg)
return client