URI: 
       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