commit 56c3de0e1eb28f6017286f51c37c5c45b86690bd
parent 6e3875ceabf75ee96e722358bcff33f883dddbf5
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:
2 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/electrum/plugin.py b/electrum/plugin.py
@@ -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.
@@ -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.'''
@@ -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:
@@ -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()
diff --git a/electrum/plugins/hw_wallet/plugin.py b/electrum/plugins/hw_wallet/plugin.py
@@ -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: