1
0

ledger: fix enumerating ledger devices with new bitcoin app (1.5.1)

see https://github.com/bitcoin-core/HWI/issues/402
This commit is contained in:
SomberNight
2020-11-18 15:14:55 +01:00
parent aaff48720f
commit b78cbcffd1
3 changed files with 71 additions and 15 deletions

View File

@@ -24,7 +24,7 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from typing import TYPE_CHECKING, Dict, List, Union, Tuple, Sequence, Optional, Type
from typing import TYPE_CHECKING, Dict, List, Union, Tuple, Sequence, Optional, Type, Iterable, Any
from functools import partial
from electrum.plugin import (BasePlugin, hook, Device, DeviceMgr, DeviceInfo,
@@ -51,6 +51,8 @@ class HW_PluginBase(BasePlugin):
minimum_library = (0, )
maximum_library = (float('inf'), )
DEVICE_IDS: Iterable[Any]
def __init__(self, parent, config, name):
BasePlugin.__init__(self, parent, config, name)
self.device = self.keystore_class.device
@@ -63,7 +65,7 @@ class HW_PluginBase(BasePlugin):
def device_manager(self) -> 'DeviceMgr':
return self.parent.device_manager
def create_device_from_hid_enumeration(self, d: dict, *, product_key) -> 'Device':
def create_device_from_hid_enumeration(self, d: dict, *, product_key) -> Optional['Device']:
# Older versions of hid don't provide interface_number
interface_number = d.get('interface_number', -1)
usage_page = d['usage_page']
@@ -192,6 +194,12 @@ class HW_PluginBase(BasePlugin):
# note: in Qt GUI, 'window' is either an ElectrumWindow or an InstallWizard
raise NotImplementedError()
def can_recognize_device(self, device: Device) -> bool:
"""Whether the plugin thinks it can handle the given device.
Used for filtering all connected hardware devices to only those by this vendor.
"""
return device.product_key in self.DEVICE_IDS
class HardwareClientBase:

View File

@@ -16,7 +16,7 @@ from electrum.wallet import Standard_Wallet
from electrum.util import bfh, bh2u, versiontuple, UserFacingException
from electrum.base_wizard import ScriptTypeNotSupported
from electrum.logging import get_logger
from electrum.plugin import runs_in_hwd_thread
from electrum.plugin import runs_in_hwd_thread, Device
from ..hw_wallet import HW_PluginBase, HardwareClientBase
from ..hw_wallet.plugin import is_any_tx_output_on_change_branch, validate_op_return_output, LibraryFoundButUnusable
@@ -95,15 +95,7 @@ class Ledger_Client(HardwareClientBase):
return self._product_key[0] == 0x2581
def device_model_name(self):
if self.is_hw1():
return "Ledger HW.1"
if self._product_key == (0x2c97, 0x0000):
return "Ledger Blue"
if self._product_key == (0x2c97, 0x0001):
return "Ledger Nano S"
if self._product_key == (0x2c97, 0x0004):
return "Ledger Nano X"
return None
return LedgerPlugin.device_name_from_product_key(self._product_key)
@runs_in_hwd_thread
def has_usable_connection_with_device(self):
@@ -594,6 +586,11 @@ class LedgerPlugin(HW_PluginBase):
(0x2c97, 0x0009), # RFU
(0x2c97, 0x000a) # RFU
]
VENDOR_IDS = (0x2c97, )
LEDGER_MODEL_IDS = {
0x10: "Ledger Nano S",
0x40: "Ledger Nano X",
}
SUPPORTED_XTYPES = ('standard', 'p2wpkh-p2sh', 'p2wpkh', 'p2wsh-p2sh', 'p2wsh')
def __init__(self, parent, config, name):
@@ -602,7 +599,10 @@ class LedgerPlugin(HW_PluginBase):
self.libraries_available = self.check_libraries_available()
if not self.libraries_available:
return
# to support legacy devices and legacy firmwares
self.device_manager().register_devices(self.DEVICE_IDS, plugin=self)
# to support modern firmware
self.device_manager().register_vendor_ids(self.VENDOR_IDS, plugin=self)
def get_library_version(self):
try:
@@ -617,6 +617,43 @@ class LedgerPlugin(HW_PluginBase):
else:
raise LibraryFoundButUnusable(library_version=version)
@classmethod
def _recognize_device(cls, product_key) -> Tuple[bool, Optional[str]]:
"""Returns (can_recognize, model_name) tuple."""
# legacy product_keys
if product_key in cls.DEVICE_IDS:
if product_key[0] == 0x2581:
return True, "Ledger HW.1"
if product_key == (0x2c97, 0x0000):
return True, "Ledger Blue"
if product_key == (0x2c97, 0x0001):
return True, "Ledger Nano S"
if product_key == (0x2c97, 0x0004):
return True, "Ledger Nano X"
return True, None
# modern product_keys
if product_key[0] == 0x2c97:
product_id = product_key[1]
model_id = product_id >> 8
if model_id in cls.LEDGER_MODEL_IDS:
model_name = cls.LEDGER_MODEL_IDS[model_id]
return True, model_name
# give up
return False, None
def can_recognize_device(self, device: Device) -> bool:
return self._recognize_device(device.product_key)[0]
@classmethod
def device_name_from_product_key(cls, product_key) -> Optional[str]:
return cls._recognize_device(product_key)[1]
def create_device_from_hid_enumeration(self, d, *, product_key):
device = super().create_device_from_hid_enumeration(d, product_key=product_key)
if not self.can_recognize_device(device):
return None
return device
@runs_in_hwd_thread
def get_btchip_device(self, device):
ledger = False