1
0

hardware devices: run all device communication on dedicated thread (#6561)

hidapi/libusb etc are not thread-safe.

related: #6554
This commit is contained in:
ghost43
2020-09-08 15:52:53 +00:00
committed by GitHub
parent 53a5a21ee8
commit 21c3572600
12 changed files with 195 additions and 97 deletions

View File

@@ -10,7 +10,7 @@ import struct
from electrum import bip32
from electrum.bip32 import BIP32Node, InvalidMasterKeyVersionBytes
from electrum.i18n import _
from electrum.plugin import Device, hook
from electrum.plugin import Device, hook, runs_in_hwd_thread
from electrum.keystore import Hardware_KeyStore, KeyStoreWithMPK
from electrum.transaction import PartialTransaction
from electrum.wallet import Standard_Wallet, Multisig_Wallet, Abstract_Wallet
@@ -72,9 +72,8 @@ class CKCCClient(HardwareClientBase):
self.dev = ElectrumColdcardDevice(dev_path, encrypt=True)
else:
# open the real HID device
with self.device_manager().hid_lock:
hd = hid.device(path=dev_path)
hd.open_path(dev_path)
hd = hid.device(path=dev_path)
hd.open_path(dev_path)
self.dev = ElectrumColdcardDevice(dev=hd, encrypt=True)
@@ -85,6 +84,7 @@ class CKCCClient(HardwareClientBase):
return '<CKCCClient: xfp=%s label=%r>' % (xfp2str(self.dev.master_fingerprint),
self.label())
@runs_in_hwd_thread
def verify_connection(self, expected_xfp: int, expected_xpub=None):
ex = (expected_xfp, expected_xpub)
@@ -121,14 +121,10 @@ class CKCCClient(HardwareClientBase):
# can't do anything w/ devices that aren't setup (this code not normally reachable)
return bool(self.dev.master_xpub)
def timeout(self, cutoff):
# nothing to do?
pass
@runs_in_hwd_thread
def close(self):
# close the HID device (so can be reused)
with self.device_manager().hid_lock:
self.dev.close()
self.dev.close()
self.dev = None
def is_initialized(self):
@@ -160,6 +156,7 @@ class CKCCClient(HardwareClientBase):
return LabelStr(lab, self.dev.master_fingerprint, self.dev.master_xpub)
@runs_in_hwd_thread
def has_usable_connection_with_device(self):
# Do end-to-end ping test
try:
@@ -168,6 +165,7 @@ class CKCCClient(HardwareClientBase):
except:
return False
@runs_in_hwd_thread
def get_xpub(self, bip32_path, xtype):
assert xtype in ColdcardPlugin.SUPPORTED_XTYPES
_logger.info('Derive xtype = %r' % xtype)
@@ -183,6 +181,7 @@ class CKCCClient(HardwareClientBase):
xpub = node._replace(xtype=xtype).to_xpub()
return xpub
@runs_in_hwd_thread
def ping_check(self):
# check connection is working
assert self.dev.session_key, 'not encrypted?'
@@ -193,26 +192,32 @@ class CKCCClient(HardwareClientBase):
except:
raise RuntimeError("Communication trouble with Coldcard")
@runs_in_hwd_thread
def show_address(self, path, addr_fmt):
# prompt user w/ address, also returns it immediately.
return self.dev.send_recv(CCProtocolPacker.show_address(path, addr_fmt), timeout=None)
@runs_in_hwd_thread
def show_p2sh_address(self, *args, **kws):
# prompt user w/ p2sh address, also returns it immediately.
return self.dev.send_recv(CCProtocolPacker.show_p2sh_address(*args, **kws), timeout=None)
@runs_in_hwd_thread
def get_version(self):
# gives list of strings
return self.dev.send_recv(CCProtocolPacker.version(), timeout=1000).split('\n')
@runs_in_hwd_thread
def sign_message_start(self, path, msg):
# this starts the UX experience.
self.dev.send_recv(CCProtocolPacker.sign_message(msg, path), timeout=None)
@runs_in_hwd_thread
def sign_message_poll(self):
# poll device... if user has approved, will get tuple: (addr, sig) else None
return self.dev.send_recv(CCProtocolPacker.get_signed_msg(), timeout=None)
@runs_in_hwd_thread
def sign_transaction_start(self, raw_psbt: bytes, *, finalize: bool = False):
# Multiple steps to sign:
# - upload binary
@@ -228,10 +233,12 @@ class CKCCClient(HardwareClientBase):
if resp != None:
raise ValueError(resp)
@runs_in_hwd_thread
def sign_transaction_poll(self):
# poll device... if user has approved, will get tuple: (legnth, checksum) else None
return self.dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None)
@runs_in_hwd_thread
def download_file(self, length, checksum, file_number=1):
# get a file
return self.dev.download_file(length, checksum, file_number=file_number)
@@ -317,7 +324,6 @@ class Coldcard_KeyStore(Hardware_KeyStore):
% MSG_SIGNING_MAX_LENGTH)
return b''
client = self.get_client()
path = self.get_derivation_prefix() + ("/%d/%d" % sequence)
try:
cl = self.get_client()
@@ -508,6 +514,7 @@ class ColdcardPlugin(HW_PluginBase):
return []
@runs_in_hwd_thread
def create_client(self, device, handler):
if handler:
self.handler = handler
@@ -539,6 +546,7 @@ class ColdcardPlugin(HW_PluginBase):
xpub = client.get_xpub(derivation, xtype)
return xpub
@runs_in_hwd_thread
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)