Browse Source

Add an extra state for invoices where our tx has been broadcast

successfully, but it is not in our history yet.

(follow-up 159646fe54)
master
ThomasV 3 years ago
parent
commit
f04e2e2e6f
  1. 7
      electrum/gui/qml/qeinvoice.py
  2. 8
      electrum/gui/qml/qewallet.py
  3. 7
      electrum/gui/qt/send_tab.py
  4. 4
      electrum/gui/qt/util.py
  5. 12
      electrum/invoices.py
  6. 10
      electrum/wallet.py

7
electrum/gui/qml/qeinvoice.py

@ -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]

8
electrum/gui/qml/qewallet.py

@ -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()

7
electrum/gui/qt/send_tab.py

@ -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)

4
electrum/gui/qt/util.py

@ -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",
}

12
electrum/invoices.py

@ -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),
@ -39,6 +40,8 @@ pr_color = {
PR_INFLIGHT: (.9, .6, .3, 1),
PR_FAILED: (.9, .2, .2, 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:

10
electrum/wallet.py

@ -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)

Loading…
Cancel
Save