From 20c95b26ea16d9ffda9548758f95ee0bf5c96783 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Thu, 29 Sep 2022 18:05:06 +0200 Subject: [PATCH] wip otp on pay --- electrum/gui/qml/components/OtpDialog.qml | 53 +++++++++++++++++++ .../gui/qml/components/WalletMainView.qml | 23 ++++++++ electrum/gui/qml/qewallet.py | 25 +++++++++ electrum/plugins/trustedcoin/qml.py | 27 +++++----- 4 files changed, 114 insertions(+), 14 deletions(-) create mode 100644 electrum/gui/qml/components/OtpDialog.qml diff --git a/electrum/gui/qml/components/OtpDialog.qml b/electrum/gui/qml/components/OtpDialog.qml new file mode 100644 index 000000000..70f9c6f65 --- /dev/null +++ b/electrum/gui/qml/components/OtpDialog.qml @@ -0,0 +1,53 @@ +import QtQuick 2.6 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Material 2.0 + +import org.electrum 1.0 + +import "controls" + +ElDialog { + id: dialog + + title: qsTr('OTP auth') + + property string otpauth + + // property var lnurlData + // property InvoiceParser invoiceParser + // property alias lnurlData: dialog.invoiceParser.lnurlData + + standardButtons: Dialog.Cancel + + modal: true + parent: Overlay.overlay + Overlay.modal: Rectangle { + color: "#aa000000" + } + + GridLayout { + columns: 2 + implicitWidth: parent.width + + Label { + text: qsTr('code') + } + + TextField { + id: otpEdit + } + + Button { + Layout.columnSpan: 2 + Layout.alignment: Qt.AlignHCenter + text: qsTr('Proceed') + onClicked: { + // dialog.close() + otpauth = otpEdit.text + dialog.accept() + } + } + + } +} diff --git a/electrum/gui/qml/components/WalletMainView.qml b/electrum/gui/qml/components/WalletMainView.qml index e7eb1455b..21523695b 100644 --- a/electrum/gui/qml/components/WalletMainView.qml +++ b/electrum/gui/qml/components/WalletMainView.qml @@ -256,6 +256,19 @@ Item { } } + Connections { + target: Daemon.currentWallet + function onOtpRequested() { + console.log('OTP requested') + var dialog = otpDialog.createObject(mainView) + dialog.accepted.connect(function() { + console.log('accepted ' + dialog.otpauth) + Daemon.currentWallet.finish_otp(dialog.otpauth) + }) + dialog.open() + } + } + Component { id: sendDialog SendDialog { @@ -304,5 +317,15 @@ Item { onClosed: destroy() } } + + Component { + id: otpDialog + OtpDialog { + width: parent.width * 0.9 + anchors.centerIn: parent + + onClosed: destroy() + } + } } diff --git a/electrum/gui/qml/qewallet.py b/electrum/gui/qml/qewallet.py index 8c38a326e..9080f9e84 100644 --- a/electrum/gui/qml/qewallet.py +++ b/electrum/gui/qml/qewallet.py @@ -3,6 +3,7 @@ import queue import threading import time from typing import Optional, TYPE_CHECKING +from functools import partial from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer @@ -13,6 +14,7 @@ from electrum.logging import get_logger from electrum.network import TxBroadcastError, BestEffortRequestFailed from electrum.transaction import PartialTxOutput from electrum.util import (parse_max_spend, InvalidPassword, event_listener) +from electrum.plugin import run_hook from .auth import AuthMixin, auth_protect from .qeaddresslistmodel import QEAddressListModel @@ -63,6 +65,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener): #broadcastSucceeded = pyqtSignal([str], arguments=['txid']) broadcastFailed = pyqtSignal([str,str,str], arguments=['txid','code','reason']) labelsUpdated = pyqtSignal() + otpRequested = pyqtSignal() _network_signal = pyqtSignal(str, object) @@ -423,6 +426,16 @@ class QEWallet(AuthMixin, QObject, QtEventListener): @auth_protect def sign(self, tx, *, broadcast: bool = False): + sign_hook = run_hook('tc_sign_wrapper', self.wallet, tx, partial(self.on_sign_complete, broadcast), None) + if sign_hook: + self.do_sign(tx, False) + self._logger.debug('plugin needs to sign tx too') + sign_hook(tx) + return + + self.do_sign(tx, broadcast) + + def do_sign(self, tx, broadcast): tx = self.wallet.sign_transaction(tx, self.password) if tx is None: @@ -441,6 +454,18 @@ class QEWallet(AuthMixin, QObject, QtEventListener): if broadcast: self.broadcast(tx) + def on_sign_complete(self, broadcast, tx): + if broadcast: + self.broadcast(tx) + + def request_otp(self, on_submit): + self._otp_on_submit = on_submit + self.otpRequested.emit() + + @pyqtSlot(str) + def finish_otp(self, otp): + self._otp_on_submit(otp) + def broadcast(self, tx): assert tx.is_complete() diff --git a/electrum/plugins/trustedcoin/qml.py b/electrum/plugins/trustedcoin/qml.py index 1b98cf66d..7b7204dcc 100644 --- a/electrum/plugins/trustedcoin/qml.py +++ b/electrum/plugins/trustedcoin/qml.py @@ -6,6 +6,7 @@ from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot from electrum.i18n import _ from electrum.plugin import hook from electrum.bip32 import xpub_type +from electrum.util import UserFacingException from electrum import keystore from electrum.gui.qml.qewallet import QEWallet @@ -18,8 +19,6 @@ from .trustedcoin import (TrustedCoinPlugin, server, ErrorConnectingServer, class Plugin(TrustedCoinPlugin): class QSignalObject(PluginQObject): - requestView = pyqtSignal([str], arguments=['component']) - canSignWithoutServerChanged = pyqtSignal() _canSignWithoutServer = False termsAndConditionsChanged = pyqtSignal() @@ -37,6 +36,8 @@ class Plugin(TrustedCoinPlugin): shortIdChanged = pyqtSignal() _shortId = '' + requestOtp = pyqtSignal() + def __init__(self, plugin, parent): super().__init__(plugin, parent) @@ -307,26 +308,24 @@ class Plugin(TrustedCoinPlugin): def prompt_user_for_otp(self, wallet, tx, on_success, on_failure): self.logger.debug('prompt_user_for_otp') self.on_success = on_success - self.on_failure = on_failure + self.on_failure = on_failure if on_failure else lambda x: self.logger.error(x) self.wallet = wallet self.tx = tx - self.so.requestView.emit('../../../../plugins/trustedcoin/qml/OTP.qml') + qewallet = QEWallet.getInstanceFor(wallet) + qewallet.request_otp(self.on_otp) def on_otp(self, otp): + self.logger.debug(f'on_otp {otp} for tx {repr(self.tx)}') try: self.wallet.on_otp(self.tx, otp) + except UserFacingException as e: + self.on_failure(_('Invalid one-time password.')) except TrustedCoinException as e: if e.status_code == 400: # invalid OTP -# Clock.schedule_once(lambda dt: - self.on_failure(_('Invalid one-time password.')) -# ) + self.on_failure(_('Invalid one-time password.')) else: -# Clock.schedule_once(lambda dt, bound_e=e: - self.on_failure(_('Error') + ':\n' + str(bound_e)) -# ) + self.on_failure(_('Error') + ':\n' + str(e)) except Exception as e: -# Clock.schedule_once(lambda dt, bound_e=e: - self.on_failure(_('Error') + ':\n' + str(bound_e)) -# ) + self.on_failure(_('Error') + ':\n' + str(e)) else: - self.on_success(tx) + self.on_success(self.tx)