Trezor: Add wipe device functionality
Also add a chicken box for PIN removal.
This commit is contained in:
parent
9aae66a9d2
commit
a7028176cd
|
@ -55,7 +55,7 @@ class CosignWidget(QWidget):
|
|||
|
||||
|
||||
|
||||
class InstallWizard(WindowModalDialog, MessageBoxMixin, WizardBase):
|
||||
class InstallWizard(WindowModalDialog, WizardBase):
|
||||
|
||||
def __init__(self, config, app, plugins):
|
||||
title = 'Electrum - ' + _('Install Wizard')
|
||||
|
|
|
@ -8,7 +8,7 @@ import qrcode
|
|||
import electrum
|
||||
from electrum import bmp
|
||||
from electrum.i18n import _
|
||||
from util import WindowModalDialog, MessageBoxMixin
|
||||
from util import WindowModalDialog
|
||||
|
||||
|
||||
class QRCodeWidget(QWidget):
|
||||
|
@ -83,7 +83,7 @@ class QRCodeWidget(QWidget):
|
|||
|
||||
|
||||
|
||||
class QRDialog(WindowModalDialog, MessageBoxMixin):
|
||||
class QRDialog(WindowModalDialog):
|
||||
|
||||
def __init__(self, data, parent=None, title = "", show_text=False):
|
||||
WindowModalDialog.__init__(self, parent, title)
|
||||
|
|
|
@ -156,9 +156,10 @@ class CancelButton(QPushButton):
|
|||
self.clicked.connect(dialog.reject)
|
||||
|
||||
class MessageBoxMixin:
|
||||
def question(self, msg, parent=None, title=None):
|
||||
def question(self, msg, parent=None, title=None, icon=None):
|
||||
Yes, No = QMessageBox.Yes, QMessageBox.No
|
||||
return self.msg_box(QMessageBox.Question, parent or self, title or '',
|
||||
return self.msg_box(icon or QMessageBox.Question,
|
||||
parent or self, title or '',
|
||||
msg, buttons=Yes|No, defaultButton=No) == Yes
|
||||
|
||||
def show_warning(self, msg, parent=None, title=None):
|
||||
|
@ -188,7 +189,7 @@ class MessageBoxMixin:
|
|||
d.setDefaultButton(defaultButton)
|
||||
return d.exec_()
|
||||
|
||||
class WindowModalDialog(QDialog):
|
||||
class WindowModalDialog(QDialog, MessageBoxMixin):
|
||||
'''Handy wrapper; window modal dialogs are better for our multi-window
|
||||
daemon model as other wallet windows can still be accessed.'''
|
||||
def __init__(self, parent, title=None):
|
||||
|
|
|
@ -154,7 +154,8 @@ def trezor_client_class(protocol_mixin, base_client, proto):
|
|||
|
||||
cls = TrezorClient
|
||||
for method in ['apply_settings', 'change_pin', 'get_address',
|
||||
'get_public_node', 'sign_message', 'sign_tx']:
|
||||
'get_public_node', 'sign_message', 'sign_tx',
|
||||
'wipe_device']:
|
||||
setattr(cls, method, wrapper(getattr(cls, method)))
|
||||
|
||||
return cls
|
||||
|
|
|
@ -42,6 +42,14 @@ class TrezorCompatibleWallet(BIP44_Wallet):
|
|||
self.print_error("connected")
|
||||
self.handler.watching_only_changed()
|
||||
|
||||
def wiped(self):
|
||||
self.print_error("wiped")
|
||||
self.handler.watching_only_changed()
|
||||
|
||||
def initialized(self):
|
||||
self.print_error("initialized")
|
||||
self.handler.watching_only_changed()
|
||||
|
||||
def get_action(self):
|
||||
pass
|
||||
|
||||
|
@ -316,14 +324,15 @@ class TrezorCompatiblePlugin(BasePlugin):
|
|||
|
||||
@hook
|
||||
def close_wallet(self, wallet):
|
||||
# Don't retain references to a closed wallet
|
||||
self.paired_wallets.discard(wallet)
|
||||
client = self.lookup_client(wallet)
|
||||
if client:
|
||||
self.clear_session(client)
|
||||
# Release the device
|
||||
self.clients.discard(client)
|
||||
client.transport.close()
|
||||
if isinstance(wallet, self.wallet_class):
|
||||
# Don't retain references to a closed wallet
|
||||
self.paired_wallets.discard(wallet)
|
||||
client = self.lookup_client(wallet)
|
||||
if client:
|
||||
self.clear_session(client)
|
||||
# Release the device
|
||||
self.clients.discard(client)
|
||||
client.transport.close()
|
||||
|
||||
def sign_transaction(self, wallet, tx, prev_tx, xpub_path):
|
||||
self.prev_tx = prev_tx
|
||||
|
|
|
@ -145,8 +145,36 @@ def qt_plugin_class(base_plugin_class):
|
|||
lambda: self.show_address(wallet, addrs[0]))
|
||||
|
||||
def settings_dialog(self, window):
|
||||
handler = window.wallet.handler
|
||||
client = self.client(window.wallet)
|
||||
|
||||
def client():
|
||||
return self.client(wallet)
|
||||
|
||||
def add_rows_to_layout(layout, rows):
|
||||
for row_num, items in enumerate(rows):
|
||||
for col_num, txt in enumerate(items):
|
||||
widget = txt if isinstance(txt, QWidget) else QLabel(txt)
|
||||
layout.addWidget(widget, row_num, col_num)
|
||||
|
||||
def refresh():
|
||||
features = client().features
|
||||
bl_hash = features.bootloader_hash.encode('hex').upper()
|
||||
bl_hash = "%s...%s" % (bl_hash[:10], bl_hash[-10:])
|
||||
version = "%d.%d.%d" % (features.major_version,
|
||||
features.minor_version,
|
||||
features.patch_version)
|
||||
|
||||
bl_hash_label.setText(bl_hash)
|
||||
device_label.setText(features.label)
|
||||
device_id_label.setText(features.device_id)
|
||||
initialized_label.setText(noyes[features.initialized])
|
||||
version_label.setText(version)
|
||||
pin_label.setText(noyes[features.pin_protection])
|
||||
passphrase_label.setText(noyes[features.passphrase_protection])
|
||||
language_label.setText(features.language)
|
||||
|
||||
pin_button.setText(_("Change") if features.pin_protection
|
||||
else _("Set"))
|
||||
clear_pin_button.setVisible(features.pin_protection)
|
||||
|
||||
def rename():
|
||||
title = _("Set Device Label")
|
||||
|
@ -154,64 +182,91 @@ def qt_plugin_class(base_plugin_class):
|
|||
response = QInputDialog().getText(dialog, title, msg)
|
||||
if not response[1]:
|
||||
return
|
||||
new_label = str(response[0])
|
||||
client.change_label(new_label)
|
||||
device_label.setText(new_label)
|
||||
client().change_label(str(response[0]))
|
||||
refresh()
|
||||
|
||||
def update_pin_info():
|
||||
features = client.features
|
||||
pin_label.setText(noyes[features.pin_protection])
|
||||
pin_button.setText(_("Change") if features.pin_protection
|
||||
else _("Set"))
|
||||
clear_pin_button.setVisible(features.pin_protection)
|
||||
def set_pin():
|
||||
client().set_pin(remove=False)
|
||||
refresh()
|
||||
|
||||
def set_pin(remove):
|
||||
client.set_pin(remove=remove)
|
||||
update_pin_info()
|
||||
def clear_pin():
|
||||
title = _("Confirm Clear PIN")
|
||||
msg = _("WARNING: if your clear your PIN, anyone with physical "
|
||||
"access to your %s device can spend your bitcoins.\n\n"
|
||||
"Are you certain you want to remove your PIN?") % device
|
||||
if not dialog.question(msg, title=title):
|
||||
return
|
||||
client().set_pin(remove=True)
|
||||
refresh()
|
||||
|
||||
def wipe_device():
|
||||
title = _("Confirm Device Wipe")
|
||||
msg = _("Are you sure you want to wipe the device? "
|
||||
"You should make sure you have a copy of your recovery "
|
||||
"seed and that your wallet holds no bitcoins.")
|
||||
if not dialog.question(msg, title=title):
|
||||
return
|
||||
if sum(wallet.get_balance()):
|
||||
title = _("Confirm Device Wipe")
|
||||
msg = _("Are you SURE you want to wipe the device?\n"
|
||||
"Your wallet still has bitcoins in it!")
|
||||
if not dialog.question(msg, title=title,
|
||||
icon=QMessageBox.Critical):
|
||||
return
|
||||
client().wipe_device()
|
||||
refresh()
|
||||
|
||||
wallet = window.wallet
|
||||
handler = wallet.handler
|
||||
device = self.device
|
||||
|
||||
features = client.features
|
||||
noyes = [_("No"), _("Yes")]
|
||||
bl_hash = features.bootloader_hash.encode('hex').upper()
|
||||
bl_hash = "%s...%s" % (bl_hash[:10], bl_hash[-10:])
|
||||
info_tab = QWidget()
|
||||
layout = QGridLayout(info_tab)
|
||||
device_label = QLabel(features.label)
|
||||
info_layout = QGridLayout(info_tab)
|
||||
noyes = [_("No"), _("Yes")]
|
||||
bl_hash_label = QLabel()
|
||||
device_label = QLabel()
|
||||
passphrase_label = QLabel()
|
||||
initialized_label = QLabel()
|
||||
device_id_label = QLabel()
|
||||
version_label = QLabel()
|
||||
pin_label = QLabel()
|
||||
language_label = QLabel()
|
||||
rename_button = QPushButton(_("Rename"))
|
||||
rename_button.clicked.connect(rename)
|
||||
pin_label = QLabel()
|
||||
pin_button = QPushButton()
|
||||
pin_button.clicked.connect(partial(set_pin, False))
|
||||
pin_button.clicked.connect(set_pin)
|
||||
clear_pin_button = QPushButton(_("Clear"))
|
||||
clear_pin_button.clicked.connect(partial(set_pin, True))
|
||||
update_pin_info()
|
||||
clear_pin_button.clicked.connect(clear_pin)
|
||||
|
||||
version = "%d.%d.%d" % (features.major_version,
|
||||
features.minor_version,
|
||||
features.patch_version)
|
||||
rows = [
|
||||
(_("Bootloader Hash"), bl_hash),
|
||||
(_("Device ID"), features.device_id),
|
||||
add_rows_to_layout(info_layout, [
|
||||
(_("Device Label"), device_label, rename_button),
|
||||
(_("Firmware Version"), version),
|
||||
(_("Language"), features.language),
|
||||
(_("Has Passphrase"), noyes[features.passphrase_protection]),
|
||||
(_("Has PIN"), pin_label, pin_button, clear_pin_button)
|
||||
]
|
||||
(_("Has Passphrase"), passphrase_label),
|
||||
(_("Has PIN"), pin_label, pin_button, clear_pin_button),
|
||||
(_("Initialized"), initialized_label),
|
||||
(_("Device ID"), device_id_label),
|
||||
(_("Bootloader Hash"), bl_hash_label),
|
||||
(_("Firmware Version"), version_label),
|
||||
(_("Language"), language_label),
|
||||
])
|
||||
|
||||
for row_num, items in enumerate(rows):
|
||||
for col_num, item in enumerate(items):
|
||||
widget = item if isinstance(item, QWidget) else QLabel(item)
|
||||
layout.addWidget(widget, row_num, col_num)
|
||||
advanced_tab = QWidget()
|
||||
advanced_layout = QGridLayout(advanced_tab)
|
||||
wipe_device_button = QPushButton(_("Wipe Device"))
|
||||
wipe_device_button.clicked.connect(wipe_device)
|
||||
add_rows_to_layout(advanced_layout, [
|
||||
(wipe_device_button, ),
|
||||
])
|
||||
|
||||
dialog = WindowModalDialog(window, _("%s Settings") % self.device)
|
||||
dialog = WindowModalDialog(window, _("%s Settings") % device)
|
||||
vbox = QVBoxLayout()
|
||||
tabs = QTabWidget()
|
||||
tabs.addTab(info_tab, _("Information"))
|
||||
tabs.addTab(QWidget(), _("Advanced"))
|
||||
tabs.addTab(advanced_tab, _("Advanced"))
|
||||
vbox.addWidget(tabs)
|
||||
vbox.addStretch(1)
|
||||
vbox.addLayout(Buttons(CloseButton(dialog)))
|
||||
|
||||
refresh()
|
||||
dialog.setLayout(vbox)
|
||||
handler.exec_dialog(dialog)
|
||||
|
||||
|
|
Loading…
Reference in New Issue