Better support for USB devices
Benefits of this rewrite include: - support of disconnecting / reconnecting a device without having to close the wallet, even in a different USB socket - support of multiple keepkey / trezor devices, both during wallet creation and general use - wallet is watching-only dynamically according to whether the associated device is currently plugged in or not
This commit is contained in:
@@ -27,7 +27,7 @@ class GuiMixin(object):
|
||||
else:
|
||||
cancel_callback = None
|
||||
|
||||
self.handler.show_message(message % self.device, cancel_callback)
|
||||
self.handler().show_message(message % self.device, cancel_callback)
|
||||
return self.proto.ButtonAck()
|
||||
|
||||
def callback_PinMatrixRequest(self, msg):
|
||||
@@ -40,14 +40,14 @@ class GuiMixin(object):
|
||||
"Note the numbers have been shuffled!"))
|
||||
else:
|
||||
msg = _("Please enter %s PIN")
|
||||
pin = self.handler.get_pin(msg % self.device)
|
||||
pin = self.handler().get_pin(msg % self.device)
|
||||
if not pin:
|
||||
return self.proto.Cancel()
|
||||
return self.proto.PinMatrixAck(pin=pin)
|
||||
|
||||
def callback_PassphraseRequest(self, req):
|
||||
msg = _("Please enter your %s passphrase")
|
||||
passphrase = self.handler.get_passphrase(msg % self.device)
|
||||
passphrase = self.handler().get_passphrase(msg % self.device)
|
||||
if passphrase is None:
|
||||
return self.proto.Cancel()
|
||||
return self.proto.PassphraseAck(passphrase=passphrase)
|
||||
@@ -65,18 +65,29 @@ def trezor_client_class(protocol_mixin, base_client, proto):
|
||||
|
||||
class TrezorClient(protocol_mixin, GuiMixin, base_client, PrintError):
|
||||
|
||||
def __init__(self, transport, plugin):
|
||||
def __init__(self, transport, path, plugin):
|
||||
base_client.__init__(self, transport)
|
||||
protocol_mixin.__init__(self, transport)
|
||||
self.proto = proto
|
||||
self.device = plugin.device
|
||||
self.handler = None
|
||||
self.path = path
|
||||
self.wallet = None
|
||||
self.plugin = plugin
|
||||
self.tx_api = plugin
|
||||
self.bad = False
|
||||
self.msg_code_override = None
|
||||
self.proper_device = False
|
||||
self.checked_device = False
|
||||
|
||||
def __str__(self):
|
||||
return "%s/%s/%s" % (self.label(), self.device_id(), self.path[0])
|
||||
|
||||
def label(self):
|
||||
return self.features.label
|
||||
|
||||
def device_id(self):
|
||||
return self.features.device_id
|
||||
|
||||
def handler(self):
|
||||
assert self.wallet and self.wallet.handler
|
||||
return self.wallet.handler
|
||||
|
||||
# Copied from trezorlib/client.py as there it is not static, sigh
|
||||
@staticmethod
|
||||
@@ -94,34 +105,8 @@ def trezor_client_class(protocol_mixin, base_client, proto):
|
||||
path.append(abs(int(x)) | prime)
|
||||
return path
|
||||
|
||||
def check_proper_device(self, wallet):
|
||||
try:
|
||||
self.ping('t')
|
||||
except BaseException as e:
|
||||
self.plugin.give_error(
|
||||
__("%s device not detected. Continuing in watching-only "
|
||||
"mode.") % self.device + "\n\n" + str(e))
|
||||
if not self.is_proper_device(wallet):
|
||||
self.plugin.give_error(_('Wrong device or password'))
|
||||
|
||||
def is_proper_device(self, wallet):
|
||||
if not self.checked_device:
|
||||
addresses = wallet.addresses(False)
|
||||
if not addresses: # Wallet being created?
|
||||
return True
|
||||
|
||||
address = addresses[0]
|
||||
address_id = wallet.address_id(address)
|
||||
path = self.expand_path(address_id)
|
||||
self.checked_device = True
|
||||
try:
|
||||
device_address = self.get_address('Bitcoin', path)
|
||||
self.proper_device = (device_address == address)
|
||||
except:
|
||||
self.proper_device = False
|
||||
wallet.proper_device = self.proper_device
|
||||
|
||||
return self.proper_device
|
||||
def address_from_derivation(self, derivation):
|
||||
return self.get_address('Bitcoin', self.expand_path(derivation))
|
||||
|
||||
def change_label(self, label):
|
||||
self.msg_code_override = 'label'
|
||||
@@ -144,12 +129,26 @@ def trezor_client_class(protocol_mixin, base_client, proto):
|
||||
def atleast_version(self, major, minor=0, patch=0):
|
||||
return cmp(self.firmware_version(), (major, minor, patch))
|
||||
|
||||
def call_raw(self, msg):
|
||||
try:
|
||||
return base_client.call_raw(self, msg)
|
||||
except:
|
||||
self.print_error("Marking %s client bad" % self.device)
|
||||
self.bad = True
|
||||
raise
|
||||
|
||||
return TrezorClient
|
||||
def wrapper(func):
|
||||
'''Wrap base class methods to show exceptions and clear
|
||||
any dialog box it opened.'''
|
||||
|
||||
def wrapped(self, *args, **kwargs):
|
||||
handler = self.handler()
|
||||
try:
|
||||
return func(self, *args, **kwargs)
|
||||
except BaseException as e:
|
||||
handler.show_error(str(e))
|
||||
raise e
|
||||
finally:
|
||||
handler.finished()
|
||||
|
||||
return wrapped
|
||||
|
||||
cls = TrezorClient
|
||||
for method in ['apply_settings', 'change_pin', 'get_address',
|
||||
'get_public_node', 'sign_message', 'sign_tx']:
|
||||
setattr(cls, method, wrapper(getattr(cls, method)))
|
||||
|
||||
return cls
|
||||
|
||||
Reference in New Issue
Block a user