diff --git a/lib/plugins.py b/lib/plugins.py index 56542e1a2..72a211253 100644 --- a/lib/plugins.py +++ b/lib/plugins.py @@ -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, diff --git a/plugins/hw_wallet/qt.py b/plugins/hw_wallet/qt.py index 86dbbe92c..2eb34bfd9 100644 --- a/plugins/hw_wallet/qt.py +++ b/plugins/hw_wallet/qt.py @@ -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. @@ -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() diff --git a/plugins/keepkey/cmdline.py b/plugins/keepkey/cmdline.py index a8ba3f310..af20eac21 100644 --- a/plugins/keepkey/cmdline.py +++ b/plugins/keepkey/cmdline.py @@ -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('') diff --git a/plugins/trezor/clientbase.py b/plugins/trezor/clientbase.py index 3586711c9..fd84a8b0e 100644 --- a/plugins/trezor/clientbase.py +++ b/plugins/trezor/clientbase.py @@ -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): @@ -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): @@ -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): @@ -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): @@ -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") @@ -200,6 +214,7 @@ class TrezorClientBase(GuiMixin, PrintError): finally: self.used() self.handler.finished() + self.creating_wallet = False self.msg = None return wrapped diff --git a/plugins/trezor/cmdline.py b/plugins/trezor/cmdline.py index 82a1d835e..544b83cdf 100644 --- a/plugins/trezor/cmdline.py +++ b/plugins/trezor/cmdline.py @@ -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('') diff --git a/plugins/trezor/plugin.py b/plugins/trezor/plugin.py index d4efcf279..50cb75c68 100644 --- a/plugins/trezor/plugin.py +++ b/plugins/trezor/plugin.py @@ -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)