hww: smarter auto-selection of which device to pair with
scenario1: - 2of2 multisig wallet with trezor1 and trezor2 keystores - only trezor2 connected - previously we would pair first keystore with connected device and then display error. now we will pair the device with the correct keystore on the first try scenario2: - standard wallet with trezor1 keystore - trezor2 connected (different device) - previously we would pair trezor2 with the keystore and then display error. now we will prompt the user to select which device to pair with (out of one) related: #5789
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
#
|
||||
import os, time, io
|
||||
import traceback
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
import struct
|
||||
|
||||
from electrum import bip32
|
||||
@@ -536,11 +536,12 @@ class ColdcardPlugin(HW_PluginBase):
|
||||
xpub = client.get_xpub(derivation, xtype)
|
||||
return xpub
|
||||
|
||||
def get_client(self, keystore, force_pair=True) -> 'CKCCClient':
|
||||
def get_client(self, keystore, force_pair=True, *,
|
||||
devices=None, allow_user_interaction=True) -> Optional['CKCCClient']:
|
||||
# Acquire a connection to the hardware device (via USB)
|
||||
devmgr = self.device_manager()
|
||||
handler = keystore.handler
|
||||
client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
|
||||
client = super().get_client(keystore, force_pair,
|
||||
devices=devices,
|
||||
allow_user_interaction=allow_user_interaction)
|
||||
|
||||
if client is not None:
|
||||
client.ping_check()
|
||||
|
||||
@@ -741,10 +741,11 @@ class DigitalBitboxPlugin(HW_PluginBase):
|
||||
return xpub
|
||||
|
||||
|
||||
def get_client(self, keystore, force_pair=True):
|
||||
devmgr = self.device_manager()
|
||||
handler = keystore.handler
|
||||
client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
|
||||
def get_client(self, keystore, force_pair=True, *,
|
||||
devices=None, allow_user_interaction=True):
|
||||
client = super().get_client(keystore, force_pair,
|
||||
devices=devices,
|
||||
allow_user_interaction=allow_user_interaction)
|
||||
if client is not None:
|
||||
client.check_device_dialog()
|
||||
return client
|
||||
|
||||
@@ -83,8 +83,15 @@ class HW_PluginBase(BasePlugin):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_client(self, keystore: 'Hardware_KeyStore', force_pair: bool = True) -> Optional['HardwareClientBase']:
|
||||
raise NotImplementedError()
|
||||
def get_client(self, keystore: 'Hardware_KeyStore', force_pair: bool = True, *,
|
||||
devices: Sequence['Device'] = None,
|
||||
allow_user_interaction: bool = True) -> Optional['HardwareClientBase']:
|
||||
devmgr = self.device_manager()
|
||||
handler = keystore.handler
|
||||
client = devmgr.client_for_keystore(self, handler, keystore, force_pair,
|
||||
devices=devices,
|
||||
allow_user_interaction=allow_user_interaction)
|
||||
return client
|
||||
|
||||
def show_address(self, wallet: 'Abstract_Wallet', address, keystore: 'Hardware_KeyStore' = None):
|
||||
pass # implemented in child classes
|
||||
|
||||
@@ -206,9 +206,11 @@ class QtPluginBase(object):
|
||||
|
||||
@hook
|
||||
def load_wallet(self: Union['QtPluginBase', HW_PluginBase], wallet: 'Abstract_Wallet', window: ElectrumWindow):
|
||||
for keystore in wallet.get_keystores():
|
||||
if not isinstance(keystore, self.keystore_class):
|
||||
continue
|
||||
relevant_keystores = [keystore for keystore in wallet.get_keystores()
|
||||
if isinstance(keystore, self.keystore_class)]
|
||||
if not relevant_keystores:
|
||||
return
|
||||
for keystore in relevant_keystores:
|
||||
if not self.libraries_available:
|
||||
message = keystore.plugin.get_library_not_available_message()
|
||||
window.show_error(message)
|
||||
@@ -224,8 +226,31 @@ class QtPluginBase(object):
|
||||
keystore.handler = handler
|
||||
keystore.thread = TaskThread(window, on_error=partial(self.on_task_thread_error, window, keystore))
|
||||
self.add_show_address_on_hw_device_button_for_receive_addr(wallet, keystore, window)
|
||||
# Trigger a pairing
|
||||
keystore.thread.add(partial(self.get_client, keystore))
|
||||
# Trigger pairings
|
||||
def trigger_pairings():
|
||||
devmgr = self.device_manager()
|
||||
devices = devmgr.scan_devices()
|
||||
# first pair with all devices that can be auto-selected
|
||||
for keystore in relevant_keystores:
|
||||
try:
|
||||
self.get_client(keystore=keystore,
|
||||
force_pair=True,
|
||||
allow_user_interaction=False,
|
||||
devices=devices)
|
||||
except UserCancelled:
|
||||
pass
|
||||
# now do manual selections
|
||||
for keystore in relevant_keystores:
|
||||
try:
|
||||
self.get_client(keystore=keystore,
|
||||
force_pair=True,
|
||||
allow_user_interaction=True,
|
||||
devices=devices)
|
||||
except UserCancelled:
|
||||
pass
|
||||
|
||||
some_keystore = relevant_keystores[0]
|
||||
some_keystore.thread.add(trigger_pairings)
|
||||
|
||||
def _on_status_bar_button_click(self, *, window: ElectrumWindow, keystore: 'Hardware_KeyStore'):
|
||||
try:
|
||||
|
||||
@@ -179,10 +179,11 @@ class KeepKeyPlugin(HW_PluginBase):
|
||||
|
||||
return client
|
||||
|
||||
def get_client(self, keystore, force_pair=True) -> Optional['KeepKeyClient']:
|
||||
devmgr = self.device_manager()
|
||||
handler = keystore.handler
|
||||
client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
|
||||
def get_client(self, keystore, force_pair=True, *,
|
||||
devices=None, allow_user_interaction=True) -> Optional['KeepKeyClient']:
|
||||
client = super().get_client(keystore, force_pair,
|
||||
devices=devices,
|
||||
allow_user_interaction=allow_user_interaction)
|
||||
# returns the client for a given keystore. can use xpub
|
||||
if client:
|
||||
client.used()
|
||||
|
||||
@@ -612,11 +612,12 @@ class LedgerPlugin(HW_PluginBase):
|
||||
xpub = client.get_xpub(derivation, xtype)
|
||||
return xpub
|
||||
|
||||
def get_client(self, keystore, force_pair=True):
|
||||
def get_client(self, keystore, force_pair=True, *,
|
||||
devices=None, allow_user_interaction=True):
|
||||
# All client interaction should not be in the main GUI thread
|
||||
devmgr = self.device_manager()
|
||||
handler = keystore.handler
|
||||
client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
|
||||
client = super().get_client(keystore, force_pair,
|
||||
devices=devices,
|
||||
allow_user_interaction=allow_user_interaction)
|
||||
# returns the client for a given keystore. can use xpub
|
||||
#if client:
|
||||
# client.used()
|
||||
|
||||
@@ -141,10 +141,11 @@ class SafeTPlugin(HW_PluginBase):
|
||||
|
||||
return client
|
||||
|
||||
def get_client(self, keystore, force_pair=True) -> Optional['SafeTClient']:
|
||||
devmgr = self.device_manager()
|
||||
handler = keystore.handler
|
||||
client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
|
||||
def get_client(self, keystore, force_pair=True, *,
|
||||
devices=None, allow_user_interaction=True) -> Optional['SafeTClient']:
|
||||
client = super().get_client(keystore, force_pair,
|
||||
devices=devices,
|
||||
allow_user_interaction=allow_user_interaction)
|
||||
# returns the client for a given keystore. can use xpub
|
||||
if client:
|
||||
client.used()
|
||||
|
||||
@@ -177,10 +177,11 @@ class TrezorPlugin(HW_PluginBase):
|
||||
# note that this call can still raise!
|
||||
return TrezorClientBase(transport, handler, self)
|
||||
|
||||
def get_client(self, keystore, force_pair=True) -> Optional['TrezorClientBase']:
|
||||
devmgr = self.device_manager()
|
||||
handler = keystore.handler
|
||||
client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
|
||||
def get_client(self, keystore, force_pair=True, *,
|
||||
devices=None, allow_user_interaction=True) -> Optional['TrezorClientBase']:
|
||||
client = super().get_client(keystore, force_pair,
|
||||
devices=devices,
|
||||
allow_user_interaction=allow_user_interaction)
|
||||
# returns the client for a given keystore. can use xpub
|
||||
if client:
|
||||
client.used()
|
||||
|
||||
Reference in New Issue
Block a user