thardware wallets: better handle label collision when selecting device - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 56c3de0e1eb28f6017286f51c37c5c45b86690bd DIR parent 6e3875ceabf75ee96e722358bcff33f883dddbf5 HTML Author: SomberNight <somber.night@protonmail.com> Date: Sun, 17 Nov 2019 01:15:44 +0100 hardware wallets: better handle label collision when selecting device related: #5759 Diffstat: M electrum/plugin.py | 26 +++++++++++++++++++------- M electrum/plugins/hw_wallet/plugin.… | 7 ++++++- 2 files changed, 25 insertions(+), 8 deletions(-) --- DIR diff --git a/electrum/plugin.py b/electrum/plugin.py t@@ -314,6 +314,9 @@ class HardwarePluginToScan(NamedTuple): exception: Optional[Exception] +PLACEHOLDER_HW_CLIENT_LABELS = {"", " "} + + class DeviceMgr(ThreadJob): '''Manages hardware clients. A client communicates over a hardware channel with the device. t@@ -507,7 +510,7 @@ class DeviceMgr(ThreadJob): 'its seed (and passphrase, if any). Otherwise all bitcoins you ' 'receive will be unspendable.').format(plugin.device)) - def unpaired_device_infos(self, handler, plugin: 'HW_PluginBase', devices=None, + def unpaired_device_infos(self, handler, plugin: 'HW_PluginBase', devices: List['Device'] = None, include_failing_clients=False): '''Returns a list of DeviceInfo objects: one for each connected, unpaired device accepted by the plugin.''' t@@ -537,7 +540,7 @@ class DeviceMgr(ThreadJob): return infos def select_device(self, plugin: 'HW_PluginBase', handler, - keystore: 'Hardware_KeyStore', devices=None) -> 'DeviceInfo': + keystore: 'Hardware_KeyStore', devices: List['Device'] = None) -> 'DeviceInfo': '''Ask the user to select a device to use if there is more than one, and return the DeviceInfo for the device.''' while True: t@@ -557,12 +560,21 @@ class DeviceMgr(ThreadJob): devices = None if len(infos) == 1: return infos[0] - # select device by label - for info in infos: - if info.label == keystore.label: - return info + # select device by label automatically; + # but only if not a placeholder label and only if there is no collision + device_labels = [info.label for info in infos] + if (keystore.label not in PLACEHOLDER_HW_CLIENT_LABELS + and device_labels.count(keystore.label) == 1): + for info in infos: + if info.label == keystore.label: + return info + # ask user to select device msg = _("Please select which {} device to use:").format(plugin.device) - descriptions = [str(info.label) + ' (%s)'%(_("initialized") if info.initialized else _("wiped")) for info in infos] + descriptions = ["{label} ({init}, {transport})" + .format(label=info.label, + init=(_("initialized") if info.initialized else _("wiped")), + transport=info.device.transport_ui_string) + for info in infos] c = handler.query_choice(msg, descriptions) if c is None: raise UserCancelled() DIR diff --git a/electrum/plugins/hw_wallet/plugin.py b/electrum/plugins/hw_wallet/plugin.py t@@ -160,7 +160,12 @@ class HardwareClientBase: raise NotImplementedError() def label(self) -> str: - """The name given by the user to the device.""" + """The name given by the user to the device. + + Note: labels are shown to the user to help distinguish their devices, + and they are also used as a fallback to distinguish devices programmatically. + So ideally, different devices would have different labels. + """ raise NotImplementedError() def has_usable_connection_with_device(self) -> bool: