Add an extra state for invoices where our tx has been broadcast
successfully, but it is not in our history yet.
(follow-up 159646fe54)
This commit is contained in:
@@ -10,7 +10,7 @@ from electrum import lnutil
|
||||
from electrum.i18n import _
|
||||
from electrum.invoices import Invoice
|
||||
from electrum.invoices import (PR_UNPAID, PR_EXPIRED, PR_UNKNOWN, PR_PAID, PR_INFLIGHT,
|
||||
PR_FAILED, PR_ROUTING, PR_UNCONFIRMED, LN_EXPIRY_NEVER)
|
||||
PR_FAILED, PR_ROUTING, PR_UNCONFIRMED, PR_BROADCASTING, PR_BROADCAST, LN_EXPIRY_NEVER)
|
||||
from electrum.lnaddr import LnInvoiceException
|
||||
from electrum.logging import get_logger
|
||||
from electrum.transaction import PartialTxOutput
|
||||
@@ -95,7 +95,7 @@ class QEInvoice(QObject, QtEventListener):
|
||||
|
||||
@event_listener
|
||||
def on_event_invoice_status(self, wallet, key, status):
|
||||
if wallet == self._wallet.wallet and key == self.key:
|
||||
if self._wallet and wallet == self._wallet.wallet and key == self.key:
|
||||
self.update_userinfo()
|
||||
self.determine_can_pay()
|
||||
self.statusChanged.emit()
|
||||
@@ -330,7 +330,8 @@ class QEInvoice(QObject, QtEventListener):
|
||||
self.userinfo = {
|
||||
PR_EXPIRED: _('This invoice has expired'),
|
||||
PR_PAID: _('This invoice was already paid'),
|
||||
PR_INFLIGHT: _('Payment in progress...') + ' (' + _('broadcasting') + ')',
|
||||
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]
|
||||
|
||||
@@ -9,7 +9,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer, QM
|
||||
|
||||
from electrum import bitcoin
|
||||
from electrum.i18n import _
|
||||
from electrum.invoices import InvoiceError, PR_DEFAULT_EXPIRATION_WHEN_CREATING, PR_PAID
|
||||
from electrum.invoices import InvoiceError, PR_DEFAULT_EXPIRATION_WHEN_CREATING, PR_PAID, PR_BROADCASTING, PR_BROADCAST
|
||||
from electrum.logging import get_logger
|
||||
from electrum.network import TxBroadcastError, BestEffortRequestFailed
|
||||
from electrum.transaction import PartialTxOutput, PartialTransaction
|
||||
@@ -549,21 +549,23 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
|
||||
assert tx.is_complete()
|
||||
|
||||
def broadcast_thread():
|
||||
self.wallet.set_broadcasting(tx, True)
|
||||
self.wallet.set_broadcasting(tx, PR_BROADCASTING)
|
||||
try:
|
||||
self._logger.info('running broadcast in thread')
|
||||
self.wallet.network.run_from_another_thread(self.wallet.network.broadcast_transaction(tx))
|
||||
except TxBroadcastError as e:
|
||||
self._logger.error(repr(e))
|
||||
self.broadcastFailed.emit(tx.txid(), '', e.get_message_for_gui())
|
||||
self.wallet.set_broadcasting(tx, None)
|
||||
except BestEffortRequestFailed as e:
|
||||
self._logger.error(repr(e))
|
||||
self.broadcastFailed.emit(tx.txid(), '', repr(e))
|
||||
self.wallet.set_broadcasting(tx, None)
|
||||
else:
|
||||
self._logger.info('broadcast success')
|
||||
self.broadcastSucceeded.emit(tx.txid())
|
||||
self.historyModel.requestRefresh.emit() # via qt thread
|
||||
self.wallet.set_broadcasting(tx, False)
|
||||
self.wallet.set_broadcasting(tx, PR_BROADCAST)
|
||||
|
||||
threading.Thread(target=broadcast_thread, daemon=True).start()
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ from electrum.i18n import _
|
||||
from electrum.util import (get_asyncio_loop, FailedToParsePaymentIdentifier,
|
||||
InvalidBitcoinURI, maybe_extract_lightning_payment_identifier, NotEnoughFunds,
|
||||
NoDynamicFeeEstimates, InvoiceError, parse_max_spend)
|
||||
from electrum.invoices import PR_PAID, Invoice
|
||||
from electrum.invoices import PR_PAID, Invoice, PR_BROADCASTING, PR_BROADCAST
|
||||
from electrum.transaction import Transaction, PartialTxInput, PartialTransaction, PartialTxOutput
|
||||
from electrum.network import TxBroadcastError, BestEffortRequestFailed
|
||||
from electrum.logging import Logger
|
||||
@@ -761,19 +761,20 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
|
||||
# Capture current TL window; override might be removed on return
|
||||
parent = self.window.top_level_window(lambda win: isinstance(win, MessageBoxMixin))
|
||||
|
||||
self.wallet.set_broadcasting(tx, True)
|
||||
self.wallet.set_broadcasting(tx, PR_BROADCASTING)
|
||||
|
||||
def broadcast_done(result):
|
||||
self.wallet.set_broadcasting(tx, False)
|
||||
# GUI thread
|
||||
if result:
|
||||
success, msg = result
|
||||
if success:
|
||||
parent.show_message(_('Payment sent.') + '\n' + msg)
|
||||
self.invoice_list.update()
|
||||
self.wallet.set_broadcasting(tx, PR_BROADCAST)
|
||||
else:
|
||||
msg = msg or ''
|
||||
parent.show_error(msg)
|
||||
self.wallet.set_broadcasting(tx, None)
|
||||
|
||||
WaitingDialog(self, _('Broadcasting transaction...'),
|
||||
broadcast_thread, broadcast_done, self.window.on_error)
|
||||
|
||||
@@ -31,7 +31,7 @@ from PyQt5.QtWidgets import (QPushButton, QLabel, QMessageBox, QHBoxLayout,
|
||||
from electrum.i18n import _, languages
|
||||
from electrum.util import FileImportFailed, FileExportFailed, make_aiohttp_session, resource_path
|
||||
from electrum.util import EventListener, event_listener
|
||||
from electrum.invoices import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN, PR_FAILED, PR_ROUTING, PR_UNCONFIRMED
|
||||
from electrum.invoices import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN, PR_FAILED, PR_ROUTING, PR_UNCONFIRMED, PR_BROADCASTING, PR_BROADCAST
|
||||
from electrum.logging import Logger
|
||||
from electrum.qrreader import MissingQrDetectionLib
|
||||
|
||||
@@ -60,6 +60,8 @@ pr_icons = {
|
||||
PR_FAILED:"warning.png",
|
||||
PR_ROUTING:"unconfirmed.png",
|
||||
PR_UNCONFIRMED:"unconfirmed.png",
|
||||
PR_BROADCASTING:"unconfirmed.png",
|
||||
PR_BROADCAST:"unconfirmed.png",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ PR_INFLIGHT = 4 # only for LN. payment attempt in progress
|
||||
PR_FAILED = 5 # only for LN. we attempted to pay it, but all attempts failed
|
||||
PR_ROUTING = 6 # only for LN. *unused* atm.
|
||||
PR_UNCONFIRMED = 7 # only onchain. invoice is satisfied but tx is not mined yet.
|
||||
|
||||
PR_BROADCASTING = 8 # onchain, tx is being broadcast
|
||||
PR_BROADCAST = 9 # onchain, tx was broadcast, is not yet in our history
|
||||
|
||||
pr_color = {
|
||||
PR_UNPAID: (.7, .7, .7, 1),
|
||||
@@ -38,7 +39,9 @@ pr_color = {
|
||||
PR_EXPIRED: (.9, .2, .2, 1),
|
||||
PR_INFLIGHT: (.9, .6, .3, 1),
|
||||
PR_FAILED: (.9, .2, .2, 1),
|
||||
PR_ROUTING: (.9, .6, .3, 1),
|
||||
PR_ROUTING: (.9, .6, .3, 1),
|
||||
PR_BROADCASTING: (.9, .6, .3, 1),
|
||||
PR_BROADCAST: (.9, .6, .3, 1),
|
||||
PR_UNCONFIRMED: (.9, .6, .3, 1),
|
||||
}
|
||||
|
||||
@@ -48,6 +51,8 @@ pr_tooltips = {
|
||||
PR_UNKNOWN:_('Unknown'),
|
||||
PR_EXPIRED:_('Expired'),
|
||||
PR_INFLIGHT:_('In progress'),
|
||||
PR_BROADCASTING:_('Broadcasting'),
|
||||
PR_BROADCAST:_('Broadcast successfully'),
|
||||
PR_FAILED:_('Failed'),
|
||||
PR_ROUTING: _('Computing route...'),
|
||||
PR_UNCONFIRMED: _('Unconfirmed'),
|
||||
@@ -243,11 +248,14 @@ class BaseInvoice(StoredObject):
|
||||
class Invoice(BaseInvoice):
|
||||
lightning_invoice = attr.ib(type=str, kw_only=True) # type: Optional[str]
|
||||
__lnaddr = None
|
||||
_is_broadcasting = False
|
||||
_broadcasting_status = None # can be None or PR_BROADCASTING or PR_BROADCAST
|
||||
|
||||
def is_lightning(self):
|
||||
return self.lightning_invoice is not None
|
||||
|
||||
def get_broadcasting_status(self):
|
||||
return self._broadcasting_status
|
||||
|
||||
def get_address(self) -> Optional[str]:
|
||||
address = None
|
||||
if self.outputs:
|
||||
|
||||
@@ -1157,6 +1157,9 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
||||
self._invoices_from_txid_map[txid].add(invoice_key)
|
||||
for txout in invoice.get_outputs():
|
||||
self._invoices_from_scriptpubkey_map[txout.scriptpubkey].add(invoice_key)
|
||||
# update invoice status
|
||||
status = self.get_invoice_status(invoice)
|
||||
util.trigger_callback('invoice_status', self, invoice_key, status)
|
||||
|
||||
def _is_onchain_invoice_paid(self, invoice: BaseInvoice) -> Tuple[bool, Optional[int], Sequence[str]]:
|
||||
"""Returns whether on-chain invoice/request is satisfied, num confs required txs have,
|
||||
@@ -2405,8 +2408,6 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
||||
|
||||
def get_invoice_status(self, invoice: BaseInvoice):
|
||||
"""Returns status of (incoming) request or (outgoing) invoice."""
|
||||
if isinstance(invoice, Invoice) and invoice._is_broadcasting:
|
||||
return PR_INFLIGHT
|
||||
# lightning invoices can be paid onchain
|
||||
if invoice.is_lightning() and self.lnworker:
|
||||
status = self.lnworker.get_invoice_status(invoice)
|
||||
@@ -2414,6 +2415,9 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
||||
return self.check_expired_status(invoice, status)
|
||||
paid, conf = self.is_onchain_invoice_paid(invoice)
|
||||
if not paid:
|
||||
if isinstance(invoice, Invoice):
|
||||
if status:=invoice.get_broadcasting_status():
|
||||
return status
|
||||
status = PR_UNPAID
|
||||
elif conf == 0:
|
||||
status = PR_UNCONFIRMED
|
||||
@@ -2532,7 +2536,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
||||
invoice = self._invoices.get(key)
|
||||
if not invoice:
|
||||
continue
|
||||
invoice._is_broadcasting = b
|
||||
invoice._broadcasting_status = b
|
||||
status = self.get_invoice_status(invoice)
|
||||
util.trigger_callback('invoice_status', self, key, status)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user