From ceae43afe52496b1f19febe81a99116955f86e36 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Tue, 8 May 2018 20:04:36 +0200 Subject: [PATCH] trustedcoin: sign first, then prompt for OTP --- gui/qt/main_window.py | 2 -- lib/commands.py | 1 - plugins/trustedcoin/cmdline.py | 4 +-- plugins/trustedcoin/qt.py | 50 ++++++++++++++++++++++-------- plugins/trustedcoin/trustedcoin.py | 3 ++ 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py index a3a944875..82363233b 100644 --- a/gui/qt/main_window.py +++ b/gui/qt/main_window.py @@ -1584,8 +1584,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) diff --git a/lib/commands.py b/lib/commands.py index e9c6cf277..7ae0bea64 100644 --- a/lib/commands.py +++ b/lib/commands.py @@ -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 diff --git a/plugins/trustedcoin/cmdline.py b/plugins/trustedcoin/cmdline.py index 51e34fda1..d70b96905 100644 --- a/plugins/trustedcoin/cmdline.py +++ b/plugins/trustedcoin/cmdline.py @@ -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(): diff --git a/plugins/trustedcoin/qt.py b/plugins/trustedcoin/qt.py index 693fe1575..2712b4d18 100644 --- a/plugins/trustedcoin/qt.py +++ b/plugins/trustedcoin/qt.py @@ -24,6 +24,7 @@ # SOFTWARE. from functools import partial +import threading from threading import Thread import re from decimal import Decimal @@ -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 @@ -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): @@ -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.'), @@ -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) diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py index 0dab526aa..4d49640be 100644 --- a/plugins/trustedcoin/trustedcoin.py +++ b/plugins/trustedcoin/trustedcoin.py @@ -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() @@ -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 @@ -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