tMerge pull request #4351 from SomberNight/2fa_sign_then_otp - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 4c234397ec6d6012a32e3247640f94ae2d76d158 DIR parent c113232e8bcc814e4693cb9abfb379819aee156e HTML Author: ThomasV <thomasv@electrum.org> Date: Fri, 18 May 2018 17:36:18 +0200 Merge pull request #4351 from SomberNight/2fa_sign_then_otp ttrustedcoin: sign first, then prompt for OTP Diffstat: M gui/qt/main_window.py | 2 -- M lib/commands.py | 1 - M plugins/trustedcoin/cmdline.py | 4 ++-- M plugins/trustedcoin/qt.py | 50 +++++++++++++++++++++++-------- M plugins/trustedcoin/trustedcoin.py | 3 +++ 5 files changed, 42 insertions(+), 18 deletions(-) --- DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py t@@ -1588,8 +1588,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): # can sign directly task = partial(Transaction.sign, tx, self.tx_external_keypairs) else: - # call hook to see if plugin needs gui interaction - run_hook('sign_tx', self, tx) task = partial(self.wallet.sign_transaction, tx, password) WaitingDialog(self, _('Signing transaction...'), task, on_signed, on_failed) DIR diff --git a/lib/commands.py b/lib/commands.py t@@ -426,7 +426,6 @@ class Commands: if rbf: tx.set_rbf(True) if not unsigned: - run_hook('sign_tx', self.wallet, tx) self.wallet.sign_transaction(tx, password) return tx DIR diff --git a/plugins/trustedcoin/cmdline.py b/plugins/trustedcoin/cmdline.py t@@ -27,10 +27,10 @@ from electrum.i18n import _ from electrum.plugins import hook from .trustedcoin import TrustedCoinPlugin + class Plugin(TrustedCoinPlugin): - @hook - def sign_tx(self, wallet, tx): + def prompt_user_for_otp(self, wallet, tx): if not isinstance(wallet, self.wallet_class): return if not wallet.can_sign_without_server(): DIR diff --git a/plugins/trustedcoin/qt.py b/plugins/trustedcoin/qt.py t@@ -24,6 +24,7 @@ # SOFTWARE. from functools import partial +import threading from threading import Thread import re from decimal import Decimal t@@ -37,6 +38,7 @@ from electrum_gui.qt.amountedit import AmountEdit from electrum_gui.qt.main_window import StatusBarButton from electrum.i18n import _ from electrum.plugins import hook +from electrum.util import PrintError from .trustedcoin import TrustedCoinPlugin, server t@@ -45,6 +47,38 @@ class TOS(QTextEdit): error_signal = pyqtSignal(object) +class HandlerTwoFactor(QObject, PrintError): + otp_start_signal = pyqtSignal(object, object) + + def __init__(self, plugin, window): + super().__init__() + self.plugin = plugin + self.window = window + self.otp_start_signal.connect(self._prompt_user_for_otp) + self.otp_done = threading.Event() + + def prompt_user_for_otp(self, wallet, tx): + self.otp_done.clear() + self.otp_start_signal.emit(wallet, tx) + self.otp_done.wait() + + def _prompt_user_for_otp(self, wallet, tx): + try: + window = self.window.top_level_window() + if not isinstance(wallet, self.plugin.wallet_class): + return + if not wallet.can_sign_without_server(): + self.print_error("twofactor:sign_tx") + auth_code = None + if wallet.keystores['x3/'].get_tx_derivations(tx): + auth_code = self.plugin.auth_dialog(window) + else: + self.print_error("twofactor: xpub3 not needed") + wallet.auth_code = auth_code + finally: + self.otp_done.set() + + class Plugin(TrustedCoinPlugin): def __init__(self, parent, config, name): t@@ -55,6 +89,7 @@ class Plugin(TrustedCoinPlugin): wallet = window.wallet if not isinstance(wallet, self.wallet_class): return + wallet.handler_2fa = HandlerTwoFactor(self, window) if wallet.can_sign_without_server(): msg = ' '.join([ _('This wallet was restored from seed, and it contains two master private keys.'), t@@ -88,19 +123,8 @@ class Plugin(TrustedCoinPlugin): return return pw.get_amount() - @hook - def sign_tx(self, window, tx): - wallet = window.wallet - if not isinstance(wallet, self.wallet_class): - return - if not wallet.can_sign_without_server(): - self.print_error("twofactor:sign_tx") - auth_code = None - if wallet.keystores['x3/'].get_tx_derivations(tx): - auth_code = self.auth_dialog(window) - else: - self.print_error("twofactor: xpub3 not needed") - window.wallet.auth_code = auth_code + def prompt_user_for_otp(self, wallet, tx): + wallet.handler_2fa.prompt_user_for_otp(wallet, tx) def waiting_dialog(self, window, on_finished=None): task = partial(self.request_billing_info, window.wallet) DIR diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py t@@ -215,6 +215,7 @@ class Wallet_2fa(Multisig_Wallet): Deterministic_Wallet.__init__(self, storage) self.is_billing = False self.billing_info = None + self.auth_code = None def can_sign_without_server(self): return not self.keystores['x2/'].is_watching_only() t@@ -272,6 +273,7 @@ class Wallet_2fa(Multisig_Wallet): Multisig_Wallet.sign_transaction(self, tx, password) if tx.is_complete(): return + self.plugin.prompt_user_for_otp(self, tx) if not self.auth_code: self.print_error("sign_transaction: no auth code") return t@@ -285,6 +287,7 @@ class Wallet_2fa(Multisig_Wallet): self.print_error("twofactor: is complete", tx.is_complete()) # reset billing_info self.billing_info = None + self.auth_code = None # Utility functions