From 6c65161d27100f074b7654371447d051f2a0d0de Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Tue, 4 Apr 2023 16:13:00 +0200 Subject: [PATCH] qml: refactor qeinvoice.py QEInvoice/QEInvoiceParser now properly split for mapping to Invoice type (QEInvoice) and parsing/resolving of payment identifiers (QEInvoiceParser). additionally, old, unused QEUserEnteredPayment was removed. invoices are now never saved with user-entered amount if the original invoice did not specify an amount (e.g. address-only, no-amount bip21 uri, or no-amount lightning invoice). Furthermore, QEInvoice now adds an isSaved property so the UI doesn't need to infer that from the existence of the invoice key. Payments of lightning invoices are now triggered through QEInvoice.pay_lightning_invoice(), using the internally kept Invoice instance. This replaces the old call path of QEWallet.pay_lightning_invoice(invoice_key) which required the invoice to be saved in the backend wallet before payment. The LNURLpay flow arriving on InvoiceDialog implicitly triggered payment, this is now indicated by InvoiceDialog.payImmediately property instead of inferrred from the QEInvoiceParser isLnurlPay property. --- electrum/gui/qml/components/InvoiceDialog.qml | 27 +- .../gui/qml/components/WalletMainView.qml | 28 +- electrum/gui/qml/qeapp.py | 3 +- electrum/gui/qml/qeinvoice.py | 454 +++++++----------- electrum/gui/qml/qewallet.py | 10 +- 5 files changed, 210 insertions(+), 312 deletions(-) diff --git a/electrum/gui/qml/components/InvoiceDialog.qml b/electrum/gui/qml/components/InvoiceDialog.qml index 0518214f2..cb86a2c8d 100644 --- a/electrum/gui/qml/components/InvoiceDialog.qml +++ b/electrum/gui/qml/components/InvoiceDialog.qml @@ -11,7 +11,7 @@ ElDialog { id: dialog property Invoice invoice - property string invoice_key + property bool payImmediately: false signal doPay signal invoiceAmountChanged @@ -392,13 +392,13 @@ ElDialog { Layout.preferredWidth: 1 text: qsTr('Save') icon.source: '../../icons/save.png' - enabled: invoice_key == '' && invoice.canSave + enabled: !invoice.isSaved && invoice.canSave onClicked: { - app.stack.push(Qt.resolvedUrl('Invoices.qml')) if (invoice.amount.isEmpty) { - invoice.amount = amountMax.checked ? MAX : Config.unitsToSats(amountBtc.text) + invoice.amountOverride = amountMax.checked ? MAX : Config.unitsToSats(amountBtc.text) } invoice.save_invoice() + app.stack.push(Qt.resolvedUrl('Invoices.qml')) dialog.close() } } @@ -410,15 +410,10 @@ ElDialog { enabled: invoice.invoiceType != Invoice.Invalid && invoice.canPay onClicked: { if (invoice.amount.isEmpty) { - invoice.amount = amountMax.checked ? MAX : Config.unitsToSats(amountBtc.text) - if (invoice_key != '') { - // delete the existing invoice because this affects get_id() - invoice.wallet.delete_invoice(invoice_key) - invoice_key = '' - } + invoice.amountOverride = amountMax.checked ? MAX : Config.unitsToSats(amountBtc.text) } - if (invoice_key == '') { - // save invoice if new or modified + if (!invoice.isSaved) { + // save invoice if newly parsed invoice.save_invoice() } doPay() // only signal here @@ -429,18 +424,14 @@ ElDialog { } Component.onCompleted: { - if (invoice_key != '') { - invoice.initFromKey(invoice_key) - } if (invoice.amount.isEmpty && !invoice.status == Invoice.Expired) { amountContainer.editmode = true } else if (invoice.amount.isMax) { amountMax.checked = true } - if (invoice.isLnurlPay) { - // we arrive from a lnurl-pay confirm dialog where the user already indicated the intent to pay. + if (payImmediately) { if (invoice.canPay) { - if (invoice_key == '') { + if (!invoice.isSaved) { invoice.save_invoice() } doPay() diff --git a/electrum/gui/qml/components/WalletMainView.qml b/electrum/gui/qml/components/WalletMainView.qml index 776264432..30d56158f 100644 --- a/electrum/gui/qml/components/WalletMainView.qml +++ b/electrum/gui/qml/components/WalletMainView.qml @@ -21,7 +21,8 @@ Item { property string _request_expiry function openInvoice(key) { - var dialog = invoiceDialog.createObject(app, { invoice: invoiceParser, invoice_key: key }) + invoice.key = key + var dialog = invoiceDialog.createObject(app, { invoice: invoice }) dialog.open() return dialog } @@ -195,6 +196,7 @@ Item { Config.userKnowsPressAndHold = true Daemon.currentWallet.delete_expired_requests() app.stack.push(Qt.resolvedUrl('ReceiveRequests.qml')) + AppController.haptic() } } FlatButton { @@ -207,11 +209,17 @@ Item { onPressAndHold: { Config.userKnowsPressAndHold = true app.stack.push(Qt.resolvedUrl('Invoices.qml')) + AppController.haptic() } } } } + Invoice { + id: invoice + wallet: Daemon.currentWallet + } + InvoiceParser { id: invoiceParser wallet: Daemon.currentWallet @@ -232,7 +240,7 @@ Item { } onValidationSuccess: { closeSendDialog() - var dialog = invoiceDialog.createObject(app, { invoice: invoiceParser }) + var dialog = invoiceDialog.createObject(app, { invoice: invoiceParser, payImmediately: invoiceParser.isLnurlPay }) dialog.open() } onInvoiceCreateError: console.log(code + ' ' + message) @@ -307,10 +315,16 @@ Item { height: parent.height onDoPay: { - if (invoice.invoiceType == Invoice.OnchainInvoice || (invoice.invoiceType == Invoice.LightningInvoice && invoice.amount.satsInt > Daemon.currentWallet.lightningCanSend ) ) { + if (invoice.invoiceType == Invoice.OnchainInvoice + || (invoice.invoiceType == Invoice.LightningInvoice + && invoice.amountOverride.isEmpty + ? invoice.amount.satsInt > Daemon.currentWallet.lightningCanSend + : invoice.amountOverride.satsInt > Daemon.currentWallet.lightningCanSend + )) + { var dialog = confirmPaymentDialog.createObject(mainView, { address: invoice.address, - satoshis: invoice.amount, + satoshis: invoice.amountOverride.isEmpty ? invoice.amount : invoice.amountOverride, message: invoice.message }) var canComplete = !Daemon.currentWallet.isWatchOnly && Daemon.currentWallet.canSignWithoutCosigner @@ -328,11 +342,7 @@ Item { dialog.open() } else if (invoice.invoiceType == Invoice.LightningInvoice) { console.log('About to pay lightning invoice') - if (invoice.key == '') { - console.log('No invoice key, aborting') - return - } - Daemon.currentWallet.pay_lightning_invoice(invoice.key) + invoice.pay_lightning_invoice() } } diff --git a/electrum/gui/qml/qeapp.py b/electrum/gui/qml/qeapp.py index e6a8a49a5..31f62b4d1 100644 --- a/electrum/gui/qml/qeapp.py +++ b/electrum/gui/qml/qeapp.py @@ -29,7 +29,7 @@ from .qewalletdb import QEWalletDB from .qebitcoin import QEBitcoin from .qefx import QEFX from .qetxfinalizer import QETxFinalizer, QETxRbfFeeBumper, QETxCpfpFeeBumper, QETxCanceller -from .qeinvoice import QEInvoice, QEInvoiceParser, QEUserEnteredPayment +from .qeinvoice import QEInvoice, QEInvoiceParser from .qerequestdetails import QERequestDetails from .qetypes import QEAmount from .qeaddressdetails import QEAddressDetails @@ -315,7 +315,6 @@ class ElectrumQmlApplication(QGuiApplication): qmlRegisterType(QETxFinalizer, 'org.electrum', 1, 0, 'TxFinalizer') qmlRegisterType(QEInvoice, 'org.electrum', 1, 0, 'Invoice') qmlRegisterType(QEInvoiceParser, 'org.electrum', 1, 0, 'InvoiceParser') - qmlRegisterType(QEUserEnteredPayment, 'org.electrum', 1, 0, 'UserEnteredPayment') qmlRegisterType(QEAddressDetails, 'org.electrum', 1, 0, 'AddressDetails') qmlRegisterType(QETxDetails, 'org.electrum', 1, 0, 'TxDetails') qmlRegisterType(QEChannelOpener, 'org.electrum', 1, 0, 'ChannelOpener') diff --git a/electrum/gui/qml/qeinvoice.py b/electrum/gui/qml/qeinvoice.py index 2b93dab37..917a1b6b2 100644 --- a/electrum/gui/qml/qeinvoice.py +++ b/electrum/gui/qml/qeinvoice.py @@ -26,7 +26,7 @@ from .qewallet import QEWallet from .util import status_update_timer_interval, QtEventListener, event_listener -class QEInvoice(QObject): +class QEInvoice(QObject, QtEventListener): class Type: Invalid = -1 OnchainInvoice = 0 @@ -48,118 +48,31 @@ class QEInvoice(QObject): _logger = get_logger(__name__) - def __init__(self, parent=None): - super().__init__(parent) - - self._wallet = None # type: Optional[QEWallet] - self._canSave = False - self._canPay = False - self._key = None - - walletChanged = pyqtSignal() - @pyqtProperty(QEWallet, notify=walletChanged) - def wallet(self): - return self._wallet - - @wallet.setter - def wallet(self, wallet: QEWallet): - if self._wallet != wallet: - self._wallet = wallet - self.walletChanged.emit() - - canSaveChanged = pyqtSignal() - @pyqtProperty(bool, notify=canSaveChanged) - def canSave(self): - return self._canSave - - @canSave.setter - def canSave(self, canSave): - if self._canSave != canSave: - self._canSave = canSave - self.canSaveChanged.emit() - - canPayChanged = pyqtSignal() - @pyqtProperty(bool, notify=canPayChanged) - def canPay(self): - return self._canPay - - @canPay.setter - def canPay(self, canPay): - if self._canPay != canPay: - self._canPay = canPay - self.canPayChanged.emit() - - keyChanged = pyqtSignal() - @pyqtProperty(str, notify=keyChanged) - def key(self): - return self._key - - @key.setter - def key(self, key): - if self._key != key: - self._key = key - self.keyChanged.emit() - - userinfoChanged = pyqtSignal() - @pyqtProperty(str, notify=userinfoChanged) - def userinfo(self): - return self._userinfo - - @userinfo.setter - def userinfo(self, userinfo): - if self._userinfo != userinfo: - self._userinfo = userinfo - self.userinfoChanged.emit() - - def get_max_spendable_onchain(self): - spendable = self._wallet.confirmedBalance.satsInt - if not self._wallet.wallet.config.get('confirmed_only', False): - spendable += self._wallet.unconfirmedBalance.satsInt - return spendable - - def get_max_spendable_lightning(self): - return self._wallet.wallet.lnworker.num_sats_can_send() if self._wallet.wallet.lnworker else 0 - -class QEInvoiceParser(QEInvoice, QtEventListener): - _logger = get_logger(__name__) - invoiceChanged = pyqtSignal() invoiceSaved = pyqtSignal([str], arguments=['key']) - - validationSuccess = pyqtSignal() - validationWarning = pyqtSignal([str,str], arguments=['code', 'message']) - validationError = pyqtSignal([str,str], arguments=['code', 'message']) - - invoiceCreateError = pyqtSignal([str,str], arguments=['code', 'message']) - - lnurlRetrieved = pyqtSignal() - lnurlError = pyqtSignal([str,str], arguments=['code', 'message']) - amountOverrideChanged = pyqtSignal() - _bip70PrResolvedSignal = pyqtSignal([PaymentRequest], arguments=['pr']) - def __init__(self, parent=None): super().__init__(parent) + self._wallet = None # type: Optional[QEWallet] + self._isSaved = False + self._canSave = False + self._canPay = False + self._key = None self._invoiceType = QEInvoice.Type.Invalid - self._recipient = '' self._effectiveInvoice = None - self._amount = QEAmount() self._userinfo = '' self._lnprops = {} + self._amount = QEAmount() + self._amountOverride = QEAmount() self._timer = QTimer(self) self._timer.setSingleShot(True) self._timer.timeout.connect(self.updateStatusString) - self._amountOverride = QEAmount() self._amountOverride.valueChanged.connect(self._on_amountoverride_value_changed) - self._bip70PrResolvedSignal.connect(self._bip70_payment_request_resolved) - - self.clear() - self.register_callbacks() self.destroyed.connect(lambda: self.on_destroy()) @@ -188,39 +101,40 @@ class QEInvoiceParser(QEInvoice, QtEventListener): self.determine_can_pay() self.userinfo = _('In progress...') + walletChanged = pyqtSignal() + @pyqtProperty(QEWallet, notify=walletChanged) + def wallet(self): + return self._wallet + + @wallet.setter + def wallet(self, wallet: QEWallet): + if self._wallet != wallet: + self._wallet = wallet + self.walletChanged.emit() + @pyqtProperty(int, notify=invoiceChanged) def invoiceType(self): return self._invoiceType # not a qt setter, don't let outside set state - def setInvoiceType(self, invoiceType: QEInvoice.Type): + def setInvoiceType(self, invoiceType: Type): self._invoiceType = invoiceType - recipientChanged = pyqtSignal() - @pyqtProperty(str, notify=recipientChanged) - def recipient(self): - return self._recipient - - @recipient.setter - def recipient(self, recipient: str): - self.canPay = False - self._recipient = recipient - self.amountOverride = QEAmount() - if recipient: - self.validateRecipient(recipient) - self.recipientChanged.emit() + @pyqtProperty(str, notify=invoiceChanged) + def message(self): + return self._effectiveInvoice.message if self._effectiveInvoice else '' - @pyqtProperty('QVariantMap', notify=lnurlRetrieved) - def lnurlData(self): - return self._lnurlData + @pyqtProperty('quint64', notify=invoiceChanged) + def time(self): + return self._effectiveInvoice.time if self._effectiveInvoice else 0 - @pyqtProperty(bool, notify=lnurlRetrieved) - def isLnurlPay(self): - return self._lnurlData is not None + @pyqtProperty('quint64', notify=invoiceChanged) + def expiration(self): + return self._effectiveInvoice.exp if self._effectiveInvoice else 0 @pyqtProperty(str, notify=invoiceChanged) - def message(self): - return self._effectiveInvoice.message if self._effectiveInvoice else '' + def address(self): + return self._effectiveInvoice.get_address() if self._effectiveInvoice else '' @pyqtProperty(QEAmount, notify=invoiceChanged) def amount(self): @@ -230,16 +144,6 @@ class QEInvoiceParser(QEInvoice, QtEventListener): self._amount.copyFrom(QEAmount(from_invoice=self._effectiveInvoice)) return self._amount - @amount.setter - def amount(self, new_amount): - self._logger.debug(f'set new amount {repr(new_amount)}') - if self._effectiveInvoice: - self._effectiveInvoice.amount_msat = '!' if new_amount.isMax else int(new_amount.satsInt * 1000) - - self.update_userinfo() - self.determine_can_pay() - self.invoiceChanged.emit() - @pyqtProperty(QEAmount, notify=amountOverrideChanged) def amountOverride(self): return self._amountOverride @@ -255,14 +159,6 @@ class QEInvoiceParser(QEInvoice, QtEventListener): self.update_userinfo() self.determine_can_pay() - @pyqtProperty('quint64', notify=invoiceChanged) - def time(self): - return self._effectiveInvoice.time if self._effectiveInvoice else 0 - - @pyqtProperty('quint64', notify=invoiceChanged) - def expiration(self): - return self._effectiveInvoice.exp if self._effectiveInvoice else 0 - statusChanged = pyqtSignal() @pyqtProperty(int, notify=statusChanged) def status(self): @@ -277,9 +173,59 @@ class QEInvoiceParser(QEInvoice, QtEventListener): status = self._wallet.wallet.get_invoice_status(self._effectiveInvoice) return self._effectiveInvoice.get_status_str(status) - @pyqtProperty(str, notify=invoiceChanged) - def address(self): - return self._effectiveInvoice.get_address() if self._effectiveInvoice else '' + isSavedChanged = pyqtSignal() + @pyqtProperty(bool, notify=isSavedChanged) + def isSaved(self): + return self._isSaved + + canSaveChanged = pyqtSignal() + @pyqtProperty(bool, notify=canSaveChanged) + def canSave(self): + return self._canSave + + @canSave.setter + def canSave(self, canSave): + if self._canSave != canSave: + self._canSave = canSave + self.canSaveChanged.emit() + + canPayChanged = pyqtSignal() + @pyqtProperty(bool, notify=canPayChanged) + def canPay(self): + return self._canPay + + @canPay.setter + def canPay(self, canPay): + if self._canPay != canPay: + self._canPay = canPay + self.canPayChanged.emit() + + keyChanged = pyqtSignal() + @pyqtProperty(str, notify=keyChanged) + def key(self): + return self._key + + @key.setter + def key(self, key): + if self._key != key: + self._key = key + if self._effectiveInvoice and self._effectiveInvoice.get_id() == key: + return + invoice = self._wallet.wallet.get_invoice(key) + self._logger.debug(f'invoice from key {key}: {repr(invoice)}') + self.set_effective_invoice(invoice) + self.keyChanged.emit() + + userinfoChanged = pyqtSignal() + @pyqtProperty(str, notify=userinfoChanged) + def userinfo(self): + return self._userinfo + + @userinfo.setter + def userinfo(self, userinfo): + if self._userinfo != userinfo: + self._userinfo = userinfo + self.userinfoChanged.emit() @pyqtProperty('QVariantMap', notify=invoiceChanged) def lnprops(self): @@ -304,38 +250,19 @@ class QEInvoiceParser(QEInvoice, QtEventListener): } def name_for_node_id(self, node_id): - node_alias = self._wallet.wallet.lnworker.get_node_alias(node_id) or node_id.hex() - return node_alias - - @pyqtSlot() - def clear(self): - self.recipient = '' - self.setInvoiceType(QEInvoice.Type.Invalid) - self._bip21 = None - self._lnurlData = None - self.canSave = False - self.canPay = False - self.userinfo = '' - self.invoiceChanged.emit() - - # don't parse the recipient string, but init qeinvoice from an invoice key - # this should not emit validation signals - @pyqtSlot(str) - def initFromKey(self, key): - self.clear() - invoice = self._wallet.wallet.get_invoice(key) - self._logger.debug(repr(invoice)) - if invoice: - self.set_effective_invoice(invoice) - self.key = key + return self._wallet.wallet.lnworker.get_node_alias(node_id) or node_id.hex() def set_effective_invoice(self, invoice: Invoice): self._effectiveInvoice = invoice - if invoice.is_lightning(): - self.setInvoiceType(QEInvoice.Type.LightningInvoice) + if invoice is None: + self.setInvoiceType(QEInvoice.Type.Invalid) else: - self.setInvoiceType(QEInvoice.Type.OnchainInvoice) + if invoice.is_lightning(): + self.setInvoiceType(QEInvoice.Type.LightningInvoice) + else: + self.setInvoiceType(QEInvoice.Type.OnchainInvoice) + self._isSaved = self._wallet.wallet.get_invoice(invoice.get_id()) is not None self.set_lnprops() @@ -344,6 +271,7 @@ class QEInvoiceParser(QEInvoice, QtEventListener): self.invoiceChanged.emit() self.statusChanged.emit() + self.isSavedChanged.emit() self.set_status_timer() @@ -440,6 +368,86 @@ class QEInvoiceParser(QEInvoice, QtEventListener): # TODO: subtract fee? self.canPay = True + @pyqtSlot() + def pay_lightning_invoice(self): + if not self.canPay: + raise Exception('can not pay invoice, canPay is false') + + if self.invoiceType != QEInvoice.Type.LightningInvoice: + raise Exception('pay_lightning_invoice can only pay lightning invoices') + + if self.amount.isEmpty: + if self.amountOverride.isEmpty: + raise Exception('can not pay 0 amount') + # TODO: is update amount_msat for overrideAmount sufficient? + self._effectiveInvoice.amount_msat = self.amountOverride.satsInt * 1000 + + self._wallet.pay_lightning_invoice(self._effectiveInvoice) + + def get_max_spendable_onchain(self): + spendable = self._wallet.confirmedBalance.satsInt + if not self._wallet.wallet.config.get('confirmed_only', False): + spendable += self._wallet.unconfirmedBalance.satsInt + return spendable + + def get_max_spendable_lightning(self): + return self._wallet.wallet.lnworker.num_sats_can_send() if self._wallet.wallet.lnworker else 0 + +class QEInvoiceParser(QEInvoice): + _logger = get_logger(__name__) + + validationSuccess = pyqtSignal() + validationWarning = pyqtSignal([str,str], arguments=['code', 'message']) + validationError = pyqtSignal([str,str], arguments=['code', 'message']) + + invoiceCreateError = pyqtSignal([str,str], arguments=['code', 'message']) + + lnurlRetrieved = pyqtSignal() + lnurlError = pyqtSignal([str,str], arguments=['code', 'message']) + + _bip70PrResolvedSignal = pyqtSignal([PaymentRequest], arguments=['pr']) + + def __init__(self, parent=None): + super().__init__(parent) + + self._recipient = '' + self._bip70PrResolvedSignal.connect(self._bip70_payment_request_resolved) + + self.clear() + + recipientChanged = pyqtSignal() + @pyqtProperty(str, notify=recipientChanged) + def recipient(self): + return self._recipient + + @recipient.setter + def recipient(self, recipient: str): + self.canPay = False + self._recipient = recipient + self.amountOverride = QEAmount() + if recipient: + self.validateRecipient(recipient) + self.recipientChanged.emit() + + @pyqtProperty('QVariantMap', notify=lnurlRetrieved) + def lnurlData(self): + return self._lnurlData + + @pyqtProperty(bool, notify=lnurlRetrieved) + def isLnurlPay(self): + return self._lnurlData is not None + + @pyqtSlot() + def clear(self): + self.recipient = '' + self.setInvoiceType(QEInvoice.Type.Invalid) + self._bip21 = None + self._lnurlData = None + self.canSave = False + self.canPay = False + self.userinfo = '' + self.invoiceChanged.emit() + def setValidOnchainInvoice(self, invoice: Invoice): self._logger.debug('setValidOnchainInvoice') if invoice.is_lightning(): @@ -641,118 +649,14 @@ class QEInvoiceParser(QEInvoice, QtEventListener): @pyqtSlot() def save_invoice(self): - self.canSave = False if not self._effectiveInvoice: return - - self.key = self._effectiveInvoice.get_id() - if self._wallet.wallet.get_invoice(self.key): - self._logger.info(f'invoice {self.key} already exists') - else: - self._wallet.wallet.save_invoice(self._effectiveInvoice) - self._wallet.invoiceModel.addInvoice(self.key) - self.invoiceSaved.emit(self.key) - - -class QEUserEnteredPayment(QEInvoice): - _logger = get_logger(__name__) - - validationError = pyqtSignal([str,str], arguments=['code','message']) - invoiceCreateError = pyqtSignal([str,str], arguments=['code', 'message']) - invoiceSaved = pyqtSignal() - - def __init__(self, parent=None): - super().__init__(parent) - - self._amount = QEAmount() - self.clear() - - recipientChanged = pyqtSignal() - @pyqtProperty(str, notify=recipientChanged) - def recipient(self): - return self._recipient - - @recipient.setter - def recipient(self, recipient: str): - if self._recipient != recipient: - self._recipient = recipient - self.validate() - self.recipientChanged.emit() - - messageChanged = pyqtSignal() - @pyqtProperty(str, notify=messageChanged) - def message(self): - return self._message - - @message.setter - def message(self, message): - if self._message != message: - self._message = message - self.messageChanged.emit() - - amountChanged = pyqtSignal() - @pyqtProperty(QEAmount, notify=amountChanged) - def amount(self): - return self._amount - - @amount.setter - def amount(self, amount): - if self._amount != amount: - self._amount.copyFrom(amount) - self.validate() - self.amountChanged.emit() - - - def validate(self): - self.canPay = False - self.canSave = False - self._logger.debug('validate') - - if not self._recipient: - self.validationError.emit('recipient', _('Recipient not specified.')) - return - - if not bitcoin.is_address(self._recipient): - self.validationError.emit('recipient', _('Invalid Bitcoin address')) - return - - self.canSave = True - - if self._amount.isEmpty: - self.validationError.emit('amount', _('Invalid amount')) + if self.isSaved: return - if self._amount.isMax: - self.canPay = True - else: - if self.get_max_spendable_onchain() >= self._amount.satsInt: - self.canPay = True - - @pyqtSlot() - def save_invoice(self): - assert self.canSave - assert not self._amount.isMax - - self._logger.debug('saving invoice to %s, amount=%s, message=%s' % (self._recipient, repr(self._amount), self._message)) - - inv_amt = self._amount.satsInt - - try: - outputs = [PartialTxOutput.from_address_and_value(self._recipient, inv_amt)] - self._logger.debug(repr(outputs)) - invoice = self._wallet.wallet.create_invoice(outputs=outputs, message=self._message, pr=None, URI=None) - except InvoiceError as e: - self.invoiceCreateError.emit('fatal', _('Error creating payment') + ':\n' + str(e)) - return - - self.key = invoice.get_id() - self._wallet.wallet.save_invoice(invoice) - self.invoiceSaved.emit() - - @pyqtSlot() - def clear(self): - self._recipient = None - self._amount.clear() - self._message = None self.canSave = False - self.canPay = False + + self.key = self._effectiveInvoice.get_id() + self._wallet.wallet.save_invoice(self._effectiveInvoice) + self._wallet.invoiceModel.addInvoice(self.key) + self.invoiceSaved.emit(self.key) diff --git a/electrum/gui/qml/qewallet.py b/electrum/gui/qml/qewallet.py index 408a9d665..bc2701bc5 100644 --- a/electrum/gui/qml/qewallet.py +++ b/electrum/gui/qml/qewallet.py @@ -28,7 +28,7 @@ from .util import QtEventListener, qt_event_listener if TYPE_CHECKING: from electrum.wallet import Abstract_Wallet - + from .qeinvoice import QEInvoice class QEWallet(AuthMixin, QObject, QtEventListener): __instances = [] @@ -587,14 +587,8 @@ class QEWallet(AuthMixin, QObject, QtEventListener): def ln_auth_rejected(self): self.paymentAuthRejected.emit() - @pyqtSlot(str) @auth_protect(reject='ln_auth_rejected') - def pay_lightning_invoice(self, invoice_key): - self._logger.debug('about to pay LN') - invoice = self.wallet.get_invoice(invoice_key) - assert(invoice) - assert(invoice.lightning_invoice) - + def pay_lightning_invoice(self, invoice: 'QEInvoice'): amount_msat = invoice.get_amount_msat() def pay_thread():