1
0

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:
Neil Booth
2016-01-02 09:43:56 +09:00
parent 187b4dc9c1
commit 21bf5a8a84
11 changed files with 345 additions and 225 deletions

View File

@@ -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