From 03dd38bfb8cd14db4d61f5664793ac1262b7ef35 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Wed, 15 Nov 2023 13:36:36 +0100 Subject: [PATCH] qml: add support for ln address and openalias, fix lnurl finalize step, avoid overloading invoiceParser with new payment identifiers while potentially long-running resolve/finalize steps are ongoing, show errors inresolve/finalize steps to user. --- .../gui/qml/components/WalletMainView.qml | 2 + electrum/gui/qml/qeinvoice.py | 58 ++++++++++++++----- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/electrum/gui/qml/components/WalletMainView.qml b/electrum/gui/qml/components/WalletMainView.qml index efd0464c8..8dfd9c1de 100644 --- a/electrum/gui/qml/components/WalletMainView.qml +++ b/electrum/gui/qml/components/WalletMainView.qml @@ -309,6 +309,7 @@ Item { Layout.preferredWidth: 1 icon.source: '../../icons/tab_send.png' text: qsTr('Send') + enabled: !invoiceParser.busy onClicked: openSendDialog() onPressAndHold: { Config.userKnowsPressAndHold = true @@ -329,6 +330,7 @@ Item { wallet: Daemon.currentWallet onValidationError: (code, message) => { var dialog = app.messageDialog.createObject(app, { + title: qsTr('Error'), text: message }) dialog.closed.connect(function() { diff --git a/electrum/gui/qml/qeinvoice.py b/electrum/gui/qml/qeinvoice.py index 6d1580a0a..700c65f3d 100644 --- a/electrum/gui/qml/qeinvoice.py +++ b/electrum/gui/qml/qeinvoice.py @@ -327,9 +327,9 @@ class QEInvoice(QObject, QtEventListener): self.userinfo = { PR_EXPIRED: _('This invoice has expired'), PR_PAID: _('This invoice was already paid'), - PR_BROADCASTING: _('Payment in progress...') + ' (' + _('broadcasting') + ')', - PR_BROADCAST: _('Payment in progress...') + ' (' + _('broadcast successfully') + ')', - PR_UNCONFIRMED: _('Payment in progress...') + ' (' + _('waiting for confirmation') + ')', + PR_BROADCASTING: _('Payment in progress...') + ' (' + _('broadcasting') + ')', + PR_BROADCAST: _('Payment in progress...') + ' (' + _('broadcast successfully') + ')', + PR_UNCONFIRMED: _('Payment in progress...') + ' (' + _('waiting for confirmation') + ')', PR_UNKNOWN: _('Invoice has unknown status'), }[self.status] @@ -395,13 +395,15 @@ class QEInvoiceParser(QEInvoice): _logger = get_logger(__name__) validationSuccess = pyqtSignal() - validationWarning = pyqtSignal([str,str], arguments=['code', 'message']) - validationError = pyqtSignal([str,str], arguments=['code', 'message']) + validationWarning = pyqtSignal([str, str], arguments=['code', 'message']) + validationError = pyqtSignal([str, str], arguments=['code', 'message']) - invoiceCreateError = pyqtSignal([str,str], arguments=['code', 'message']) + invoiceCreateError = pyqtSignal([str, str], arguments=['code', 'message']) lnurlRetrieved = pyqtSignal() - lnurlError = pyqtSignal([str,str], arguments=['code', 'message']) + lnurlError = pyqtSignal([str, str], arguments=['code', 'message']) + + busyChanged = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) @@ -409,6 +411,7 @@ class QEInvoiceParser(QEInvoice): self._recipient = '' self._pi = None self._lnurlData = None + self._busy = False self.clear() @@ -434,6 +437,10 @@ class QEInvoiceParser(QEInvoice): def isLnurlPay(self): return self._lnurlData is not None + @pyqtProperty(bool, notify=busyChanged) + def busy(self): + return self._busy + @pyqtSlot() def clear(self): self.recipient = '' @@ -492,7 +499,9 @@ class QEInvoiceParser(QEInvoice): self._pi = PaymentIdentifier(self._wallet.wallet, recipient) if not self._pi.is_valid() or self._pi.type not in [PaymentIdentifierType.SPK, PaymentIdentifierType.BIP21, PaymentIdentifierType.BIP70, PaymentIdentifierType.BOLT11, - PaymentIdentifierType.LNURLP]: + PaymentIdentifierType.LNURLP, + PaymentIdentifierType.EMAILLIKE, + PaymentIdentifierType.DOMAINLIKE]: self.validationError.emit('unknown', _('Unknown invoice')) return @@ -509,7 +518,7 @@ class QEInvoiceParser(QEInvoice): self.resolve_pi() return - if self._pi.type == PaymentIdentifierType.LNURLP: + if self._pi.type in [PaymentIdentifierType.LNURLP, PaymentIdentifierType.LNADDR]: self.on_lnurl(self._pi.lnurl_data) return @@ -518,7 +527,7 @@ class QEInvoiceParser(QEInvoice): return if self._pi.is_available(): - if self._pi.type == PaymentIdentifierType.SPK: + if self._pi.type in [PaymentIdentifierType.SPK, PaymentIdentifierType.OPENALIAS]: outputs = [PartialTxOutput(scriptpubkey=self._pi.spk, value=0)] invoice = self.create_onchain_invoice(outputs, None, None, None) self._logger.debug(repr(invoice)) @@ -563,11 +572,25 @@ class QEInvoiceParser(QEInvoice): assert self._pi.need_resolve() def on_finished(pi): + self._busy = False + self.busyChanged.emit() + if pi.is_error(): - pass + if pi.type in [PaymentIdentifierType.EMAILLIKE, PaymentIdentifierType.DOMAINLIKE]: + msg = _('Could not resolve address') + elif pi.type == PaymentIdentifierType.LNURLP: + msg = _('Could not resolve LNURL') + elif pi.type == PaymentIdentifierType.BIP70: + msg = _('Could not resolve BIP70 payment request: {}').format(pi.error) + else: + msg = _('Could not resolve') + self.validationError.emit('resolve', msg) else: self._update_from_payment_identifier() + self._busy = True + self.busyChanged.emit() + self._pi.resolve(on_finished=on_finished) def on_lnurl(self, lnurldata): @@ -576,7 +599,7 @@ class QEInvoiceParser(QEInvoice): self._lnurlData = { 'domain': urlparse(lnurldata.callback_url).netloc, - 'callback_url' : lnurldata.callback_url, + 'callback_url': lnurldata.callback_url, 'min_sendable_sat': lnurldata.min_sendable_sat, 'max_sendable_sat': lnurldata.max_sendable_sat, 'metadata_plaintext': lnurldata.metadata_plaintext, @@ -598,6 +621,9 @@ class QEInvoiceParser(QEInvoice): comment = None def on_finished(pi): + self._busy = False + self.busyChanged.emit() + if pi.is_error(): if pi.state == PaymentIdentifierState.INVALID_AMOUNT: self.lnurlError.emit('amount', pi.get_error()) @@ -606,6 +632,9 @@ class QEInvoiceParser(QEInvoice): else: self.on_lnurl_invoice(self.amountOverride.satsInt, pi.bolt11) + self._busy = True + self.busyChanged.emit() + self._pi.finalize(amount_sat=amount, comment=comment, on_finished=on_finished) def on_lnurl_invoice(self, orig_amount, invoice): @@ -613,11 +642,10 @@ class QEInvoiceParser(QEInvoice): self._logger.debug(f'{repr(invoice)}') # assure no shenanigans with the bolt11 invoice we get back - lninvoice = Invoice.from_bech32(invoice) - if orig_amount * 1000 != lninvoice.amount_msat: # TODO msat precision can cause trouble here + if orig_amount * 1000 != invoice.amount_msat: # TODO msat precision can cause trouble here raise Exception('Unexpected amount in invoice, differs from lnurl-pay specified amount') - self.recipient = invoice + self.recipient = invoice.lightning_invoice @pyqtSlot() def saveInvoice(self):