mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-01 17:55:20 +00:00
trezor: more user friendly when cannot connect
Tell the user and ask if they want to try again. If they say no, raise a silent exception. Apply this more friendly behaviour to the install wizard too (see issue #1668).
This commit is contained in:
parent
317e6cea32
commit
16397b1ed7
9 changed files with 51 additions and 44 deletions
|
@ -1,4 +1,4 @@
|
||||||
from sys import stdout
|
import sys
|
||||||
|
|
||||||
from PyQt4.QtGui import *
|
from PyQt4.QtGui import *
|
||||||
from PyQt4.QtCore import *
|
from PyQt4.QtCore import *
|
||||||
|
@ -14,8 +14,8 @@ from password_dialog import PasswordLayout, PW_NEW, PW_PASSPHRASE
|
||||||
|
|
||||||
from electrum.wallet import Wallet
|
from electrum.wallet import Wallet
|
||||||
from electrum.mnemonic import prepare_seed
|
from electrum.mnemonic import prepare_seed
|
||||||
from electrum.util import SilentException
|
from electrum.util import UserCancelled
|
||||||
from electrum.wizard import (WizardBase, UserCancelled,
|
from electrum.wizard import (WizardBase,
|
||||||
MSG_ENTER_PASSWORD, MSG_RESTORE_PASSPHRASE,
|
MSG_ENTER_PASSWORD, MSG_RESTORE_PASSPHRASE,
|
||||||
MSG_COSIGNER, MSG_ENTER_SEED_OR_MPK,
|
MSG_COSIGNER, MSG_ENTER_SEED_OR_MPK,
|
||||||
MSG_SHOW_MPK, MSG_VERIFY_SEED,
|
MSG_SHOW_MPK, MSG_VERIFY_SEED,
|
||||||
|
@ -119,7 +119,7 @@ class InstallWizard(QDialog, MessageBoxMixin, WizardBase):
|
||||||
self.refresh_gui()
|
self.refresh_gui()
|
||||||
|
|
||||||
def on_error(self, exc_info):
|
def on_error(self, exc_info):
|
||||||
if not isinstance(exc_info[1], SilentException):
|
if not isinstance(exc_info[1], UserCancelled):
|
||||||
traceback.print_exception(*exc_info)
|
traceback.print_exception(*exc_info)
|
||||||
self.show_error(str(exc_info[1]))
|
self.show_error(str(exc_info[1]))
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ class InstallWizard(QDialog, MessageBoxMixin, WizardBase):
|
||||||
self.print_error("wallet creation cancelled by user")
|
self.print_error("wallet creation cancelled by user")
|
||||||
self.accept() # For when called from menu
|
self.accept() # For when called from menu
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
self.show_error(str(e))
|
self.on_error(sys.exc_info())
|
||||||
raise
|
raise
|
||||||
return wallet
|
return wallet
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ from electrum.i18n import _
|
||||||
from electrum.util import (block_explorer, block_explorer_info, format_time,
|
from electrum.util import (block_explorer, block_explorer_info, format_time,
|
||||||
block_explorer_URL, format_satoshis, PrintError,
|
block_explorer_URL, format_satoshis, PrintError,
|
||||||
format_satoshis_plain, NotEnoughFunds, StoreDict,
|
format_satoshis_plain, NotEnoughFunds, StoreDict,
|
||||||
SilentException)
|
UserCancelled)
|
||||||
from electrum import Transaction, mnemonic
|
from electrum import Transaction, mnemonic
|
||||||
from electrum import util, bitcoin, commands
|
from electrum import util, bitcoin, commands
|
||||||
from electrum import SimpleConfig, COIN_CHOOSERS, paymentrequest
|
from electrum import SimpleConfig, COIN_CHOOSERS, paymentrequest
|
||||||
|
@ -214,7 +214,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||||
self.raise_()
|
self.raise_()
|
||||||
|
|
||||||
def on_error(self, exc_info):
|
def on_error(self, exc_info):
|
||||||
if not isinstance(exc_info[1], SilentException):
|
if not isinstance(exc_info[1], UserCancelled):
|
||||||
traceback.print_exception(*exc_info)
|
traceback.print_exception(*exc_info)
|
||||||
self.show_error(str(exc_info[1]))
|
self.show_error(str(exc_info[1]))
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import time
|
||||||
|
|
||||||
from util import *
|
from util import *
|
||||||
from i18n import _
|
from i18n import _
|
||||||
from util import profiler, PrintError, DaemonThread
|
from util import profiler, PrintError, DaemonThread, UserCancelled
|
||||||
import wallet
|
import wallet
|
||||||
|
|
||||||
class Plugins(DaemonThread):
|
class Plugins(DaemonThread):
|
||||||
|
@ -386,26 +386,20 @@ class DeviceMgr(PrintError):
|
||||||
# The wallet has not been previously paired, so let the user
|
# The wallet has not been previously paired, so let the user
|
||||||
# choose an unpaired device and compare its first address.
|
# choose an unpaired device and compare its first address.
|
||||||
info = self.select_device(wallet, plugin, devices)
|
info = self.select_device(wallet, plugin, devices)
|
||||||
if info:
|
|
||||||
client = self.client_lookup(info.device.id_)
|
|
||||||
if client and client.is_pairable():
|
|
||||||
# See comment above for same code
|
|
||||||
client.handler = wallet.handler
|
|
||||||
# This will trigger a PIN/passphrase entry request
|
|
||||||
client_first_address = client.first_address(derivation)
|
|
||||||
if client_first_address == first_address:
|
|
||||||
self.pair_wallet(wallet, info.device.id_)
|
|
||||||
return client
|
|
||||||
|
|
||||||
if info and client:
|
client = self.client_lookup(info.device.id_)
|
||||||
# The user input has wrong PIN or passphrase
|
if client and client.is_pairable():
|
||||||
raise DeviceUnpairableError(
|
# See comment above for same code
|
||||||
_('Unable to pair with your %s.') % plugin.device)
|
client.handler = wallet.handler
|
||||||
|
# This will trigger a PIN/passphrase entry request
|
||||||
|
client_first_address = client.first_address(derivation)
|
||||||
|
if client_first_address == first_address:
|
||||||
|
self.pair_wallet(wallet, info.device.id_)
|
||||||
|
return client
|
||||||
|
|
||||||
raise DeviceNotFoundError(
|
# The user input has wrong PIN or passphrase, or it is not pairable
|
||||||
_('Could not connect to your %s. Verify the cable is '
|
raise DeviceUnpairableError(
|
||||||
'connected and that no other application is using it.')
|
_('Unable to pair with your %s.') % plugin.device)
|
||||||
% plugin.device)
|
|
||||||
|
|
||||||
def unpaired_device_infos(self, handler, plugin, devices=None):
|
def unpaired_device_infos(self, handler, plugin, devices=None):
|
||||||
'''Returns a list of DeviceInfo objects: one for each connected,
|
'''Returns a list of DeviceInfo objects: one for each connected,
|
||||||
|
@ -432,9 +426,17 @@ class DeviceMgr(PrintError):
|
||||||
def select_device(self, wallet, plugin, devices=None):
|
def select_device(self, wallet, plugin, devices=None):
|
||||||
'''Ask the user to select a device to use if there is more than one,
|
'''Ask the user to select a device to use if there is more than one,
|
||||||
and return the DeviceInfo for the device.'''
|
and return the DeviceInfo for the device.'''
|
||||||
infos = self.unpaired_device_infos(wallet.handler, plugin, devices)
|
while True:
|
||||||
if not infos:
|
infos = self.unpaired_device_infos(wallet.handler, plugin, devices)
|
||||||
return None
|
if infos:
|
||||||
|
break
|
||||||
|
msg = _('Could not connect to your %s. Verify the cable is '
|
||||||
|
'connected and that no other application is using it.\n\n'
|
||||||
|
'Try to connect again?') % plugin.device
|
||||||
|
if not wallet.handler.yes_no_question(msg):
|
||||||
|
raise UserCancelled()
|
||||||
|
devices = None
|
||||||
|
|
||||||
if len(infos) == 1:
|
if len(infos) == 1:
|
||||||
return infos[0]
|
return infos[0]
|
||||||
msg = _("Please select which %s device to use:") % plugin.device
|
msg = _("Please select which %s device to use:") % plugin.device
|
||||||
|
|
|
@ -21,8 +21,10 @@ class InvalidPassword(Exception):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _("Incorrect password")
|
return _("Incorrect password")
|
||||||
|
|
||||||
class SilentException(Exception):
|
# Throw this exception to unwind the stack like when an error occurs.
|
||||||
'''An exception that should probably be suppressed from the user'''
|
# However unlike other exceptions the user won't be informed.
|
||||||
|
class UserCancelled(Exception):
|
||||||
|
'''An exception that is suppressed from the user'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class MyEncoder(json.JSONEncoder):
|
class MyEncoder(json.JSONEncoder):
|
||||||
|
|
|
@ -36,9 +36,6 @@ MSG_RESTORE_PASSPHRASE = \
|
||||||
"Note this is NOT a password. Enter nothing if you did not use "
|
"Note this is NOT a password. Enter nothing if you did not use "
|
||||||
"one or are unsure.")
|
"one or are unsure.")
|
||||||
|
|
||||||
class UserCancelled(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class WizardBase(PrintError):
|
class WizardBase(PrintError):
|
||||||
'''Base class for gui-specific install wizards.'''
|
'''Base class for gui-specific install wizards.'''
|
||||||
user_actions = ('create', 'restore')
|
user_actions = ('create', 'restore')
|
||||||
|
|
|
@ -34,6 +34,7 @@ class QtHandlerBase(QObject, PrintError):
|
||||||
logic for handling I/O.'''
|
logic for handling I/O.'''
|
||||||
|
|
||||||
qcSig = pyqtSignal(object, object)
|
qcSig = pyqtSignal(object, object)
|
||||||
|
ynSig = pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, win, device):
|
def __init__(self, win, device):
|
||||||
super(QtHandlerBase, self).__init__()
|
super(QtHandlerBase, self).__init__()
|
||||||
|
@ -43,6 +44,7 @@ class QtHandlerBase(QObject, PrintError):
|
||||||
win.connect(win, SIGNAL('passphrase_dialog'), self.passphrase_dialog)
|
win.connect(win, SIGNAL('passphrase_dialog'), self.passphrase_dialog)
|
||||||
win.connect(win, SIGNAL('word_dialog'), self.word_dialog)
|
win.connect(win, SIGNAL('word_dialog'), self.word_dialog)
|
||||||
self.qcSig.connect(self.win_query_choice)
|
self.qcSig.connect(self.win_query_choice)
|
||||||
|
self.ynSig.connect(self.win_yes_no_question)
|
||||||
self.win = win
|
self.win = win
|
||||||
self.device = device
|
self.device = device
|
||||||
self.dialog = None
|
self.dialog = None
|
||||||
|
@ -60,6 +62,12 @@ class QtHandlerBase(QObject, PrintError):
|
||||||
self.done.wait()
|
self.done.wait()
|
||||||
return self.choice
|
return self.choice
|
||||||
|
|
||||||
|
def yes_no_question(self, msg):
|
||||||
|
self.done.clear()
|
||||||
|
self.ynSig.emit(msg)
|
||||||
|
self.done.wait()
|
||||||
|
return self.ok
|
||||||
|
|
||||||
def show_message(self, msg, on_cancel=None):
|
def show_message(self, msg, on_cancel=None):
|
||||||
self.win.emit(SIGNAL('message_dialog'), msg, on_cancel)
|
self.win.emit(SIGNAL('message_dialog'), msg, on_cancel)
|
||||||
|
|
||||||
|
@ -126,3 +134,7 @@ class QtHandlerBase(QObject, PrintError):
|
||||||
def win_query_choice(self, msg, labels):
|
def win_query_choice(self, msg, labels):
|
||||||
self.choice = self.win.query_choice(msg, labels)
|
self.choice = self.win.query_choice(msg, labels)
|
||||||
self.done.set()
|
self.done.set()
|
||||||
|
|
||||||
|
def win_yes_no_question(self, msg):
|
||||||
|
self.ok = self.top_level_window().question(msg)
|
||||||
|
self.done.set()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from sys import stderr
|
from sys import stderr
|
||||||
|
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.util import PrintError, SilentException
|
from electrum.util import PrintError, UserCancelled
|
||||||
|
|
||||||
|
|
||||||
class GuiMixin(object):
|
class GuiMixin(object):
|
||||||
|
@ -27,7 +27,7 @@ class GuiMixin(object):
|
||||||
# gets old very quickly, so we suppress those.
|
# gets old very quickly, so we suppress those.
|
||||||
if msg.code in (self.types.Failure_PinCancelled,
|
if msg.code in (self.types.Failure_PinCancelled,
|
||||||
self.types.Failure_ActionCancelled):
|
self.types.Failure_ActionCancelled):
|
||||||
raise SilentException()
|
raise UserCancelled()
|
||||||
raise RuntimeError(msg.message)
|
raise RuntimeError(msg.message)
|
||||||
|
|
||||||
def callback_ButtonRequest(self, msg):
|
def callback_ButtonRequest(self, msg):
|
||||||
|
|
|
@ -220,8 +220,6 @@ class TrezorCompatiblePlugin(HW_PluginBase):
|
||||||
process. Then create the wallet accounts.'''
|
process. Then create the wallet accounts.'''
|
||||||
devmgr = self.device_manager()
|
devmgr = self.device_manager()
|
||||||
device_info = devmgr.select_device(wallet, self)
|
device_info = devmgr.select_device(wallet, self)
|
||||||
if not device_info:
|
|
||||||
raise RuntimeError(_("No devices found"))
|
|
||||||
devmgr.pair_wallet(wallet, device_info.device.id_)
|
devmgr.pair_wallet(wallet, device_info.device.id_)
|
||||||
if device_info.initialized:
|
if device_info.initialized:
|
||||||
task = partial(wallet.create_hd_account, None)
|
task = partial(wallet.create_hd_account, None)
|
||||||
|
|
|
@ -11,9 +11,8 @@ from ..hw_wallet.qt import QtHandlerBase
|
||||||
|
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.plugins import hook, DeviceMgr
|
from electrum.plugins import hook, DeviceMgr
|
||||||
from electrum.util import PrintError
|
from electrum.util import PrintError, UserCancelled
|
||||||
from electrum.wallet import Wallet, BIP44_Wallet
|
from electrum.wallet import Wallet, BIP44_Wallet
|
||||||
from electrum.wizard import UserCancelled
|
|
||||||
|
|
||||||
PASSPHRASE_HELP_SHORT =_(
|
PASSPHRASE_HELP_SHORT =_(
|
||||||
"Passphrases allow you to access new wallets, each "
|
"Passphrases allow you to access new wallets, each "
|
||||||
|
@ -317,10 +316,7 @@ def qt_plugin_class(base_plugin_class):
|
||||||
device_id = self.device_manager().wallet_id(window.wallet)
|
device_id = self.device_manager().wallet_id(window.wallet)
|
||||||
if not device_id:
|
if not device_id:
|
||||||
info = self.device_manager().select_device(window.wallet, self)
|
info = self.device_manager().select_device(window.wallet, self)
|
||||||
if info:
|
device_id = info.device.id_
|
||||||
device_id = info.device.id_
|
|
||||||
else:
|
|
||||||
window.wallet.handler.show_error(_("No devices found"))
|
|
||||||
return device_id
|
return device_id
|
||||||
|
|
||||||
def query_choice(self, window, msg, choices):
|
def query_choice(self, window, msg, choices):
|
||||||
|
|
Loading…
Add table
Reference in a new issue