Repository: spesmilo/electrum

Issue:
BTChip setup process: 2 occurences of "BTChip dongle not found" for no apparent reason
During setup procedure, after clicking next on the following screen, the shown popup pops up (doesn't depend on any preconditions, this has happened every time I tried the setup procedure so far)

![image](https://cloud.githubusercontent.com/assets/589745/4362019/a1d4c4c2-4289-11e4-94b4-16723eabb1dc.png)

console says: 

```
=> e0c4000000
```

(side-remark: its confusing to have yes/no buttons here since no question was asked. The user is told to click 'OK', which is not even an option.)

clicking yes (without replugging the device) just repeats '=> e0c4000000' on the console.

clicking yes (with replugging the device) show this popup:

![image](https://cloud.githubusercontent.com/assets/589745/4362052/eeca317c-4289-11e4-856e-fe1c52c0b43c.png)

(essentially the same one, just the dialog in the background is closed)

also console says this:

```
=> e0c4000000
```

only after replugging once again does the process continue.

It's not apparent to me why a replug at this point is necessary, let alone 2 in rapid succession.



Discussion:
I think I'll just solve that by asking the user to remove the dongle from the time being. This is related to the Python API having some issues when re-enumerating the device for some reason.

yes, that'd be good

Detection issues should also be fixed in 1.4.14, so can be closed


Resolving patch:
diff --git a/plugins/btchipwallet.py b/plugins/btchipwallet.py
--- a/plugins/btchipwallet.py
+++ b/plugins/btchipwallet.py
@@ -15,7 +15,7 @@
 from electrum.i18n import _
 from electrum.plugins import BasePlugin, hook
 from electrum.transaction import deserialize
-from electrum.wallet import NewWallet
+from electrum.wallet import BIP32_HD_Wallet
 
 from electrum.util import format_satoshis
 import hashlib
@@ -52,12 +52,14 @@ def __init__(self, gui, name):
     def _init(self):
         return BTCHIP
 
-    def is_available(self):
-        if self.wallet is None:
-            return self._is_available
-        if self.wallet.storage.get('wallet_type') == 'btchip':
-            return True
-        return False
+    def is_available(self):        
+        if not self._is_available:
+            return False
+        if not self.wallet:
+            return False
+        if self.wallet.storage.get('wallet_type') != 'btchip':
+            return False
+        return True
 
     def set_enabled(self, enabled):
         self.wallet.storage.put('use_' + self.name, enabled)
@@ -65,19 +67,30 @@ def set_enabled(self, enabled):
     def is_enabled(self):
         if not self.is_available():
             return False
-
-        if not self.wallet or self.wallet.storage.get('wallet_type') == 'btchip':
-            return True
-
-        return self.wallet.storage.get('use_' + self.name) is True
+        if self.wallet.has_seed():
+            return False
+        return True
 
     def enable(self):
         return BasePlugin.enable(self)
 
+    def btchip_is_connected(self):
+        try:
+            self.wallet.get_client().getFirmwareVersion()
+        except:
+            return False
+        return True
+
     @hook
     def load_wallet(self, wallet):
-        self.wallet = wallet
-
+        if self.btchip_is_connected():
+            if not self.wallet.check_proper_device():
+                QMessageBox.information(self.window, _('Error'), _("This wallet does not match your BTChip device"), _('OK'))
+                self.wallet.force_watching_only = True
+        else:
+            QMessageBox.information(self.window, _('Error'), _("BTChip device not detected.\nContinuing in watching-only mode."), _('OK'))
+            self.wallet.force_watching_only = True
+    
     @hook
     def installwizard_restore(self, wizard, storage):
         if storage.get('wallet_type') != 'btchip':
@@ -98,16 +111,18 @@ def send_tx(self, tx):
         except Exception as e:
             tx.error = str(e)
 
-class BTChipWallet(NewWallet):
+class BTChipWallet(BIP32_HD_Wallet):
     wallet_type = 'btchip'
+    root_derivation = "m/44'/0'"
 
     def __init__(self, storage):
-        NewWallet.__init__(self, storage)
+        BIP32_HD_Wallet.__init__(self, storage)
         self.transport = None
         self.client = None
         self.mpk = None
         self.device_checked = False
         self.signing = False
+        self.force_watching_only = False
 
     def give_error(self, message, clear_client = False):
         if not self.signing:
@@ -129,11 +144,8 @@ def can_create_accounts(self):
     def can_change_password(self):
         return False
 
-    def has_seed(self):
-        return False
-
     def is_watching_only(self):
-        return False
+        return self.force_watching_only
 
     def get_client(self, noPin=False):
         if not BTCHIP:
@@ -258,9 +270,6 @@ def get_public_key(self, bip32_path):
     def get_master_public_key(self):
         try:
             if not self.mpk:
-                self.get_client() # prompt for the PIN if necessary
-                if not self.check_proper_device():
-                    self.give_error('Wrong device or password')        
                 self.mpk = self.get_public_key("44'/0'")
             return self.mpk
         except Exception, e:
@@ -278,6 +287,7 @@ def decrypt_message(self, pubkey, message, password):
 
     def sign_message(self, address, message, password):
         use2FA = False
+        self.signing = True
         self.get_client() # prompt for the PIN before displaying the dialog if necessary
         if not self.check_proper_device():
             self.give_error('Wrong device or password')        
@@ -298,11 +308,15 @@ def sign_message(self, address, message, password):
                 self.get_client(True)
             signature = self.get_client().signMessageSign(pin)
         except Exception, e:
-            self.give_error(e, True)
+            if e.sw == 0x6a80:
+                self.give_error("Unfortunately, this message cannot be signed by BTChip. Only alphanumerical messages shorter than 140 characters are supported. Please remove any extra characters (tab, carriage return) and retry.")
+            else:                
+                self.give_error(e, True)
         finally:
             if waitDialog.waiting:
                 waitDialog.emit(SIGNAL('dongle_done'))
         self.client.bad = use2FA
+        self.signing = False
 
         # Parse the ASN.1 signature
 


Test patch: