Browse Source

ledger: fix scan_devices for certain devices with multiple interfaces

regression from https://github.com/spesmilo/electrum/pull/8041 (4.3.3)

maybe fixes https://github.com/spesmilo/electrum/issues/8293

-----

Testing with a "Ledger Nano S", btc app version 1.6.3.
btchip-python==0.1.32
ledgercomm==1.1.1
ledger-bitcoin==0.1.1

Trying to scan devices hangs/blocks forever on Linux (ubuntu 22.04).

```patch
diff --git a/electrum/plugins/ledger/ledger.py b/electrum/plugins/ledger/ledger.py
index 17c1caca48..ba5ae2e3ee 100644
--- a/electrum/plugins/ledger/ledger.py
+++ b/electrum/plugins/ledger/ledger.py
@@ -306,17 +306,25 @@ class Ledger_Client(HardwareClientBase, ABC):
     @staticmethod
     def construct_new(*args, device: Device, **kwargs) -> 'Ledger_Client':
         """The 'real' constructor, that automatically decides which subclass to use."""
+        _logger.info(f"xxx construct_new(). cp1. {device.path=}. {device=}")
         if LedgerPlugin.is_hw1(device.product_key):
             return Ledger_Client_Legacy_HW1(*args, **kwargs, device=device)
         # for nano S or newer hw, decide which client impl to use based on software/firmware version:
+        _logger.info(f"xxx construct_new(). cp2.")
         hid_device = HID()
         hid_device.path = device.path
+        _logger.info(f"xxx construct_new(). cp3.")
         hid_device.open()
+        _logger.info(f"xxx construct_new(). cp4.")
         transport = ledger_bitcoin.TransportClient('hid', hid=hid_device)
-        cl = ledger_bitcoin.createClient(transport, chain=get_chain())
+        _logger.info(f"xxx construct_new(). cp5.")
+        cl = ledger_bitcoin.createClient(transport, chain=get_chain(), debug=True)
+        _logger.info(f"xxx construct_new(). cp6.")
         if isinstance(cl, ledger_bitcoin.client.NewClient):
+            _logger.info(f"xxx construct_new(). cp7. trying Ledger_Client_New")
             return Ledger_Client_New(hid_device, *args, **kwargs)
         else:
+            _logger.info(f"xxx construct_new(). cp7. trying Ledger_Client_Legacy")
             return Ledger_Client_Legacy(hid_device, *args, **kwargs)

     def __init__(self, *, plugin: HW_PluginBase):
@@ -1416,7 +1424,7 @@ class LedgerPlugin(HW_PluginBase):
         try:
             return Ledger_Client.construct_new(device=device, product_key=device.product_key, plugin=self)
         except Exception as e:
-            self.logger.info(f"cannot connect at {device.path} {e}")
+            self.logger.info(f"cannot connect at {device.path} {e}")  #
         return None

     def setup_device(self, device_info, wizard, purpose):
```

scanning devices freezes... log:
```
  8.94 | I | plugin.DeviceMgr | scanning devices...
  9.18 | D | util.profiler | DeviceMgr.scan_devices 0.2357 sec
  9.18 | I | plugins.ledger.ledger | xxx construct_new(). cp1. device.path=b'0001:0008:00'. device=Device(path=b'0001:0008:00', interface_number=0, id_="b'0001:0008:00',0001,0,0", product_key=(11415, 4117), usage_page=0, transport_ui_string='hid')
  9.18 | I | plugins.ledger.ledger | xxx construct_new(). cp2.
  9.18 | I | plugins.ledger.ledger | xxx construct_new(). cp3.
heyheyhey. cp1. self.path=b'0001:0008:00'
  9.18 | I | plugins.ledger.ledger | xxx construct_new(). cp4.
  9.18 | I | plugins.ledger.ledger | xxx construct_new(). cp5.
=> b001000000
<= 010c426974636f696e205465737405312e362e3301029000
  9.22 | I | plugins.ledger.ledger | xxx construct_new(). cp6.
  9.22 | I | plugins.ledger.ledger | xxx construct_new(). cp7. trying Ledger_Client_Legacy
  9.29 | I | plugin.DeviceMgr | Registering <electrum.plugins.ledger.ledger.Ledger_Client_Legacy object at 0x7f4369e6e380>
 10.33 | I | plugins.ledger.ledger | xxx construct_new(). cp1. device.path=b'0001:0008:01'. device=Device(path=b'0001:0008:01', interface_number=1, id_="b'0001:0008:01',0001,1,0", product_key=(11415, 4117), usage_page=0, transport_ui_string='hid')
 10.33 | I | plugins.ledger.ledger | xxx construct_new(). cp2.
 10.33 | I | plugins.ledger.ledger | xxx construct_new(). cp3.
heyheyhey. cp1. self.path=b'0001:0008:01'
 10.33 | I | plugins.ledger.ledger | xxx construct_new(). cp4.
 10.33 | I | plugins.ledger.ledger | xxx construct_new(). cp5.
=> b001000000
```

in Qt console (before change):
```
>>> lp = plugins.get_plugin("ledger")
>>> plugins.device_manager.scan_devices()
[Device(path=b'0001:000a:00', interface_number=0, id_="b'0001:000a:00',0001,0,0", product_key=(11415, 4117), usage_page=0, transport_ui_string='hid'), Device(path=b'0001:000a:01', interface_number=1, id_="b'0001:000a:01',0001,1,0", product_key=(11415, 4117), usage_page=0, transport_ui_string='hid')]
```
master
SomberNight 3 years ago
parent
commit
33d394c9d7
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 11
      electrum/plugins/ledger/ledger.py

11
electrum/plugins/ledger/ledger.py

@ -1399,7 +1399,16 @@ class LedgerPlugin(HW_PluginBase):
return False, None
def can_recognize_device(self, device: Device) -> bool:
return self._recognize_device(device.product_key)[0]
can_recognize = self._recognize_device(device.product_key)[0]
if can_recognize:
# Do a further check, duplicated from:
# https://github.com/LedgerHQ/ledgercomm/blob/bc5ada865980cb63c2b9b71a916e01f2f8e53716/ledgercomm/interfaces/hid_device.py#L79-L82
# Modern ledger devices can have multiple interfaces picked up by hid, only one of which is usable by us.
# If we try communicating with the wrong one, we might not get a reply and block forever.
if device.product_key[0] == 0x2c97:
if not (device.interface_number == 0 or device.usage_page == 0xffa0):
return False
return can_recognize
@classmethod
def device_name_from_product_key(cls, product_key) -> Optional[str]:

Loading…
Cancel
Save