ttrezor: single passphrase entry - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 535956149a8a6b2801b434d2d00ecb3ba2e9bde1 DIR parent 2bbe829f32daeef0993cedb88ef7618992d6a98b HTML Author: Neil Booth <kyuupichan@gmail.com> Date: Thu, 11 Feb 2016 19:51:56 +0900 ttrezor: single passphrase entry Only require the user to input the passphrase once, unless creating a wallet. Should they mis-enter the passphrase, they will be warned Electrum couldn't pair the device, and when they actually need to use it tthey will be prompted again. Fixes #1672 Diffstat: M lib/plugins.py | 9 +++++---- M plugins/hw_wallet/qt.py | 27 +++++++++++++++++++-------- M plugins/keepkey/cmdline.py | 2 +- M plugins/trezor/clientbase.py | 19 +++++++++++++++++-- M plugins/trezor/cmdline.py | 2 +- M plugins/trezor/plugin.py | 3 ++- 6 files changed, 45 insertions(+), 17 deletions(-) --- DIR diff --git a/lib/plugins.py b/lib/plugins.py t@@ -418,10 +418,11 @@ class DeviceMgr(ThreadJob, PrintError): # The user input has wrong PIN or passphrase, or cancelled input, # or it is not pairable raise DeviceUnpairableError( - _('Unable to pair with your %s.\n\n' - 'Ensure you are able to pair it, or you have the seed phrase, ' - 'before you request bitcoins to be sent to this wallet.' - ) % plugin.device) + _('Electrum cannot pair with your %s.\n\n' + 'Before you request bitcoins to be sent to addresses in this ' + 'wallet, ensure you can pair with your device, or that you have ' + 'its seed (and passphrase, if any). Otherwise all bitcoins you ' + 'receive will be unspendable.') % plugin.device) def unpaired_device_infos(self, handler, plugin, devices=None): '''Returns a list of DeviceInfo objects: one for each connected, DIR diff --git a/plugins/hw_wallet/qt.py b/plugins/hw_wallet/qt.py t@@ -25,7 +25,6 @@ from electrum_gui.qt.util import * from electrum.i18n import _ from electrum.util import PrintError -from electrum.wallet import BIP44_Wallet # The trickiest thing about this handler was getting windows properly # parented on MacOSX. t@@ -80,17 +79,29 @@ class QtHandlerBase(QObject, PrintError): self.done.wait() return self.word - def get_passphrase(self, msg): + def get_passphrase(self, msg, confirm): self.done.clear() - self.win.emit(SIGNAL('passphrase_dialog'), msg) + self.win.emit(SIGNAL('passphrase_dialog'), msg, confirm) self.done.wait() return self.passphrase - def passphrase_dialog(self, msg): - d = PasswordDialog(self.top_level_window(), None, msg, PW_PASSPHRASE) - confirmed, p, passphrase = d.run() - if confirmed: - passphrase = BIP44_Wallet.normalize_passphrase(passphrase) + def passphrase_dialog(self, msg, confirm): + # If confirm is true, require the user to enter the passphrase twice + parent = self.top_level_window() + if confirm: + d = PasswordDialog(parent, None, msg, PW_PASSPHRASE) + confirmed, p, passphrase = d.run() + else: + d = WindowModalDialog(parent, _("Enter Passphrase")) + pw = QLineEdit() + pw.setEchoMode(2) + pw.setMinimumWidth(200) + vbox = QVBoxLayout() + vbox.addWidget(WWLabel(msg)) + vbox.addWidget(pw) + vbox.addLayout(Buttons(CancelButton(d), OkButton(d))) + d.setLayout(vbox) + passphrase = unicode(pw.text()) if d.exec_() else None self.passphrase = passphrase self.done.set() DIR diff --git a/plugins/keepkey/cmdline.py b/plugins/keepkey/cmdline.py t@@ -3,7 +3,7 @@ from electrum.util import print_msg class KeepKeyCmdLineHandler: - def get_passphrase(self, msg): + def get_passphrase(self, msg, confirm): import getpass print_msg(msg) return getpass.getpass('') DIR diff --git a/plugins/trezor/clientbase.py b/plugins/trezor/clientbase.py t@@ -2,6 +2,7 @@ import time from electrum.i18n import _ from electrum.util import PrintError, UserCancelled +from electrum.wallet import BIP44_Wallet class GuiMixin(object): t@@ -52,10 +53,17 @@ class GuiMixin(object): 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) + if self.creating_wallet: + msg = _("Enter a passphrase to generate this wallet. Each time " + "you use this wallet your %s will prompt you for the " + "passphrase. If you forget the passphrase you cannot " + "access the bitcoins in the wallet.") % self.device + else: + msg = _("Enter the passphrase to unlock this wallet:") + passphrase = self.handler.get_passphrase(msg, self.creating_wallet) if passphrase is None: return self.proto.Cancel() + passphrase = BIP44_Wallet.normalize_passphrase(passphrase) return self.proto.PassphraseAck(passphrase=passphrase) def callback_WordRequest(self, msg): t@@ -72,6 +80,7 @@ class GuiMixin(object): return self.proto.Cancel() return self.proto.CharacterAck(**char_info) + class TrezorClientBase(GuiMixin, PrintError): def __init__(self, handler, plugin, proto): t@@ -82,6 +91,7 @@ class TrezorClientBase(GuiMixin, PrintError): self.tx_api = plugin self.types = plugin.types self.msg = None + self.creating_wallet = False self.used() def __str__(self): t@@ -175,6 +185,10 @@ class TrezorClientBase(GuiMixin, PrintError): self.print_error("clear_session: ignoring error", str(e)) pass + def get_public_node(self, address_n, creating): + self.creating_wallet = creating + return super(TrezorClientBase, self).get_public_node(address_n) + def close(self): '''Called when Our wallet was closed or the device removed.''' self.print_error("closing client") t@@ -200,6 +214,7 @@ class TrezorClientBase(GuiMixin, PrintError): finally: self.used() self.handler.finished() + self.creating_wallet = False self.msg = None return wrapped DIR diff --git a/plugins/trezor/cmdline.py b/plugins/trezor/cmdline.py t@@ -3,7 +3,7 @@ from electrum.util import print_msg class TrezorCmdLineHandler: - def get_passphrase(self, msg): + def get_passphrase(self, msg, confirm): import getpass print_msg(msg) return getpass.getpass('') DIR diff --git a/plugins/trezor/plugin.py b/plugins/trezor/plugin.py t@@ -24,7 +24,8 @@ class TrezorCompatibleWallet(BIP44_HW_Wallet): def get_public_key(self, bip32_path): client = self.get_client() address_n = client.expand_path(bip32_path) - node = client.get_public_node(address_n).node + creating = self.next_account_number() == 0 + node = client.get_public_node(address_n, creating).node xpub = ("0488B21E".decode('hex') + chr(node.depth) + self.i4b(node.fingerprint) + self.i4b(node.child_num) + node.chain_code + node.public_key)