twizard: scan hardware devices directly - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 4781df9d2107ec65181e99073aaac4bbf18ade91 DIR parent 0520eda628eab626b8e698e8166a9d72798d92ce HTML Author: ThomasV <thomasv@electrum.org> Date: Tue, 23 Aug 2016 13:40:11 +0200 wizard: scan hardware devices directly Diffstat: M gui/qt/installwizard.py | 17 ++++++++++++----- M lib/base_wizard.py | 69 ++++++++++++++++++------------- M lib/plugins.py | 11 +++++------ M plugins/trezor/plugin.py | 5 ++--- M plugins/trustedcoin/trustedcoin.py | 4 ++-- 5 files changed, 62 insertions(+), 44 deletions(-) --- DIR diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py t@@ -338,13 +338,13 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): self.show_message(msg) @wizard_dialog - def confirm_dialog(self, msg, run_next): - self.confirm(msg) + def confirm_dialog(self, title, message, run_next): + self.confirm(message, title) - def confirm(self, msg): + def confirm(self, message, title): vbox = QVBoxLayout() - vbox.addWidget(WWLabel(msg)) - self.set_main_layout(vbox) + vbox.addWidget(WWLabel(message)) + self.set_main_layout(vbox, title) @wizard_dialog def action_dialog(self, action, run_next): t@@ -379,6 +379,13 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): self.set_main_layout(vbox, '') return clayout.selected_index() + def get_passphrase(self, msg, confirm): + phrase = self.pw_layout(msg, PW_PASSPHRASE) + if phrase is None: + raise UserCancelled + return phrase + + @wizard_dialog def account_id_dialog(self, run_next): message = '\n'.join([ DIR diff --git a/lib/base_wizard.py b/lib/base_wizard.py t@@ -117,12 +117,12 @@ class BaseWizard(object): ('create_seed', _('Create a new seed')), ('restore_seed', _('I already have a seed')), ('restore_from_key', _('Import keys')), - ('choose_hw', _('Use hardware device')), + ('choose_device', _('Use hardware device')), ] else: choices = [ ('restore_from_key', _('Import cosigner key')), - ('choose_hw', _('Cosign with hardware device')), + ('choose_device', _('Cosign with hardware device')), ] self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run) t@@ -165,46 +165,59 @@ class BaseWizard(object): ]) self.restore_keys_dialog(title=title, message=message, run_next=self.on_restore, is_valid=v) - def choose_hw(self): - hw_wallet_types, choices = self.plugins.hardware_wallets('create') - choices = zip(hw_wallet_types, choices) + def choose_device(self): title = _('Hardware Keystore') - if choices: - msg = _('Select the type of device') + ':' - else: - msg = ' '.join([ + # check available plugins + support = self.plugins.get_hardware_support() + if not support: + msg = '\n'.join([ _('No hardware wallet support found on your system.'), _('Please install the relevant libraries (eg python-trezor for Trezor).'), ]) - self.choice_dialog(title=title, message=msg, choices=choices, run_next=self.on_hardware) - - def on_hardware(self, hw_type): - self.hw_type = hw_type - title = _('Hardware wallet') + ' [%s]' % hw_type - message = _('Do you have a device, or do you want to restore a wallet using an existing seed?') - choices = [ - ('on_hardware_device', _('I have a %s device')%hw_type), - ('on_hardware_seed', _('I have a %s seed')%hw_type), - ] - self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run) - - def on_hardware_device(self): - f = lambda x: self.run('on_hardware_account_id', x) + self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_device()) + return + # scan devices + devices = [] + for name, description, plugin in support: + devmgr = plugin.device_manager() + try: + u = devmgr.unpaired_device_infos(self, plugin) + except: + print "error", name + continue + devices += map(lambda x: (name, x), u) + if not devices: + msg = '\n'.join([ + _('No hardware device detected.'), + _('To trigger a rescan, press \'next\'.'), + ]) + self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_device()) + return + # select device + self.devices = devices + choices = [] + for name, device_info in devices: + choices.append( ((name, device_info), device_info.description) ) + msg = _('Select a device') + ':' + self.choice_dialog(title=title, message=msg, choices=choices, run_next=self.on_device) + + def on_device(self, name, device_info): + f = lambda x: self.run('on_hardware_account_id', name, device_info, x) self.account_id_dialog(run_next=f) - def on_hardware_account_id(self, account_id): + def on_hardware_account_id(self, hw_type, device_info, account_id): from keystore import hardware_keystore, bip44_derivation derivation = bip44_derivation(int(account_id)) - plugin = self.plugins.get_plugin(self.hw_type) - xpub = plugin.setup_device(derivation, self) + plugin = self.plugins.get_plugin(hw_type) + xpub = plugin.setup_device(device_info, derivation, self) # create keystore d = { 'type': 'hardware', - 'hw_type': self.hw_type, + 'hw_type': hw_type, 'derivation': derivation, 'xpub': xpub, } - k = hardware_keystore(self.hw_type, d) + k = hardware_keystore(hw_type, d) self.on_keystore(k, None) def on_hardware_seed(self): DIR diff --git a/lib/plugins.py b/lib/plugins.py t@@ -139,19 +139,18 @@ class Plugins(DaemonThread): requires = d.get('requires_wallet_type', []) return not requires or w.wallet_type in requires - def hardware_wallets(self, action): - wallet_types, descs = [], [] + def get_hardware_support(self): + out = [] for name, (gui_good, details) in self.hw_wallets.items(): if gui_good: try: p = self.get_plugin(name) - if action == 'restore' or p.is_enabled(): - wallet_types.append(details[1]) - descs.append(details[2]) + if p.is_enabled(): + out.append([name, details[2], p]) except: traceback.print_exc() self.print_error("cannot load plugin for:", name) - return wallet_types, descs + return out def register_wallet_type(self, name, gui_good, wallet_type): from wallet import register_wallet_type, register_constructor DIR diff --git a/plugins/trezor/plugin.py b/plugins/trezor/plugin.py t@@ -199,13 +199,12 @@ class TrezorCompatiblePlugin(HW_PluginBase): client.load_device_by_xprv(item, pin, passphrase_protection, label, language) - def setup_device(self, derivation, wizard): + def setup_device(self, device_info, derivation, wizard): '''Called when creating a new wallet. Select the device to use. If the device is uninitialized, go through the intialization process.''' - handler = self.create_handler(wizard) + handler = wizard devmgr = self.device_manager() - device_info = devmgr.select_device(handler, self) device_id = device_info.device.id_ if not device_info.initialized: self.initialize_device(device_id, wizard, handler) DIR diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py t@@ -337,7 +337,7 @@ class TrustedCoinPlugin(BasePlugin): def show_disclaimer(self, wizard): wizard.set_icon(':icons/trustedcoin.png') wizard.stack = [] - wizard.confirm_dialog('\n\n'.join(DISCLAIMER), run_next = lambda x: wizard.run('choose_seed')) + wizard.confirm_dialog(title='Disclaimer', message='\n\n'.join(DISCLAIMER), run_next = lambda x: wizard.run('choose_seed')) def choose_seed(self, wizard): title = _('Create or restore') t@@ -372,7 +372,7 @@ class TrustedCoinPlugin(BasePlugin): ] msg = '\n\n'.join(msg) wizard.stack = [] - wizard.confirm_dialog(msg, run_next = lambda x: wizard.run('create_remote_key')) + wizard.confirm_dialog(title='', message=msg, run_next = lambda x: wizard.run('create_remote_key')) def restore_wallet(self, wizard): title = _("Restore two-factor Wallet")