diff --git a/electrum/gui/qt/wizard/wallet.py b/electrum/gui/qt/wizard/wallet.py index 66214f162..9ca28100a 100644 --- a/electrum/gui/qt/wizard/wallet.py +++ b/electrum/gui/qt/wizard/wallet.py @@ -17,7 +17,7 @@ from electrum.keystore import bip44_derivation, bip39_to_seed, purpose48_derivat from electrum.plugin import run_hook, HardwarePluginLibraryUnavailable from electrum.storage import StorageReadWriteError from electrum.util import WalletFileException, get_new_wallet_name, UserFacingException, InvalidPassword -from electrum.util import is_subpath, ChoiceItem, multisig_type +from electrum.util import is_subpath, ChoiceItem, multisig_type, UserCancelled from electrum.wallet import wallet_types from .wizard import QEAbstractWizard, WizardComponent from electrum.logging import get_logger, Logger @@ -91,6 +91,7 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard, MessageBoxMixin): # attach gui classes to views self.navmap_merge({ 'wallet_name': {'gui': WCWalletName}, + 'hw_unlock': {'gui': WCChooseHWDevice}, 'wallet_type': {'gui': WCWalletType}, 'keystore_type': {'gui': WCKeystoreType}, 'create_seed': {'gui': WCCreateSeed}, @@ -114,16 +115,12 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard, MessageBoxMixin): 'wallet_password_hardware': {'gui': WCWalletPasswordHardware} }) - # add open existing wallet from wizard, incl hw unlock + # add open existing wallet from wizard self.navmap_merge({ 'wallet_name': { 'next': lambda d: 'hw_unlock' if d['wallet_needs_hw_unlock'] else 'wallet_type', 'last': lambda d: d['wallet_exists'] and not d['wallet_needs_hw_unlock'] }, - 'hw_unlock': { - 'gui': WCChooseHWDevice, - 'next': lambda d: self.on_hardware_device(d, new_wallet=False) - } }) run_hook('init_wallet_wizard', self) @@ -1333,6 +1330,8 @@ class WCHWUnlock(WalletWizardComponent, Logger): def unlock_task(client): try: self.password = client.get_password_for_storage_encryption() + except UserCancelled as e: + self.error = repr(e) except Exception as e: self.error = repr(e) # TODO: handle user interaction exceptions (e.g. invalid pin) more gracefully self.logger.exception(repr(e)) diff --git a/electrum/wizard.py b/electrum/wizard.py index 96713b4b6..48a3cee9f 100644 --- a/electrum/wizard.py +++ b/electrum/wizard.py @@ -382,7 +382,10 @@ class NewWalletWizard(KeystoreWizard): KeystoreWizard.__init__(self, plugins) self.navmap = { 'wallet_name': { - 'next': 'wallet_type' + 'next': lambda d: 'hw_unlock' if d.get('wallet_needs_hw_unlock') else 'wallet_type', + }, + 'hw_unlock': { + 'next': lambda d: self.on_hardware_device(d, new_wallet=False), }, 'wallet_type': { 'next': self.on_wallet_type diff --git a/tests/test_wizard.py b/tests/test_wizard.py index 8992aaf4d..db2bdd26b 100644 --- a/tests/test_wizard.py +++ b/tests/test_wizard.py @@ -126,8 +126,6 @@ class WalletWizardTestCase(WizardTestCase): # TODO imported addresses # TODO imported WIF keys - # TODO hardware signer std wallet (e.g. Trezor) - # TODO encrypt with hardware (xpub) password # TODO multisig # TODO slip39 @@ -523,3 +521,28 @@ class WalletWizardTestCase(WizardTestCase): self.assertEqual("bc1q7ltf4aq95rj695fu5aaa5mx5m9p55xyr2fy6y0", wallet.get_receiving_addresses()[0]) self.assertTrue(wallet.has_password()) self.assertTrue(wallet.has_storage_encryption()) + + async def test_unlock_hw_trezor(self): + # bip39 seed for trezor: "history six okay anchor sheriff flock atom tomorrow foster aerobic eternal foam" + w = NewWalletWizard(DaemonMock(self.config), self.plugins) + v = w.start() + self.assertEqual('wallet_name', v.view) + d = { + 'wallet_name': 'mywallet', + 'wallet_exists': True, 'wallet_is_open': False, 'wallet_needs_hw_unlock': True,} + self.assertFalse(w.is_last_view(v.view, d)) + v = w.resolve_next(v.view, d) + self.assertEqual('hw_unlock', v.view) + + d.update({ + 'hardware_device': ( + 'trezor', + DeviceInfo( + device=Device(path='webusb:002:1', interface_number=-1, id_='webusb:002:1', product_key='Trezor', usage_page=0, transport_ui_string='webusb:002:1'), + label='trezor_unittests', initialized=True, exception=None, plugin_name='trezor', soft_device_id='088C3F260B66F60E15DE0FA5', model_name='Trezor T'))}) + v = w.resolve_next(v.view, d) + self.assertEqual('trezor_unlock', v.view) + + d.update({'password': '03a580deb85ef85654ed177fc049867ce915a8b392a34a524123870925e48a5b9e'}) + self.assertTrue(w.is_last_view(v.view, d)) + v = w.resolve_next(v.view, d)