URI: 
       tTrezor/KeepKey: force watching only improvements - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit e61fffab55f8cb59638de7d77ab1791611bd466e
   DIR parent abaf1bc6dca5971653d0d7123904c0c4b128b181
  HTML 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:
         M gui/qt/main_window.py               |       1 +
         M lib/plugins.py                      |      52 +++++++++++++++++++++----------
         M plugins/hw_wallet/hw_wallet.py      |      13 ++++++++-----
         M plugins/trezor/plugin.py            |      15 +++++----------
       
       4 files changed, 50 insertions(+), 31 deletions(-)
       ---
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -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())
   DIR diff --git a/lib/plugins.py b/lib/plugins.py
       t@@ -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")
        
       t@@ -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.'''
   DIR diff --git a/plugins/hw_wallet/hw_wallet.py b/plugins/hw_wallet/hw_wallet.py
       t@@ -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
   DIR diff --git a/plugins/trezor/plugin.py b/plugins/trezor/plugin.py
       t@@ -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):
       t@@ -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