Browse Source

Set status of onchain invoices to PR_INFLIGHT while tx is being broadcast

master
ThomasV 3 years ago
parent
commit
159646fe54
  1. 6
      electrum/gui/qml/qeinvoice.py
  2. 2
      electrum/gui/qml/qewallet.py
  3. 3
      electrum/gui/qt/send_tab.py
  4. 1
      electrum/invoices.py
  5. 44
      electrum/wallet.py

6
electrum/gui/qml/qeinvoice.py

@ -96,10 +96,9 @@ class QEInvoice(QObject, QtEventListener):
@event_listener
def on_event_invoice_status(self, wallet, key, status):
if wallet == self._wallet.wallet and key == self.key:
self.update_userinfo()
self.determine_can_pay()
self.statusChanged.emit()
if status in [PR_INFLIGHT, PR_ROUTING]:
self.determine_can_pay()
self.userinfo = _('In progress...')
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@ -331,6 +330,7 @@ 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_UNCONFIRMED: _('Payment in progress...') + ' (' + _('waiting for confirmation') + ')',
PR_UNKNOWN: _('Invoice has unknown status'),
}[self.status]

2
electrum/gui/qml/qewallet.py

@ -549,6 +549,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
assert tx.is_complete()
def broadcast_thread():
self.wallet.set_broadcasting(tx, True)
try:
self._logger.info('running broadcast in thread')
self.wallet.network.run_from_another_thread(self.wallet.network.broadcast_transaction(tx))
@ -562,6 +563,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
self._logger.info('broadcast success')
self.broadcastSucceeded.emit(tx.txid())
self.historyModel.requestRefresh.emit() # via qt thread
self.wallet.set_broadcasting(tx, False)
threading.Thread(target=broadcast_thread, daemon=True).start()

3
electrum/gui/qt/send_tab.py

@ -761,7 +761,10 @@ 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)
def broadcast_done(result):
self.wallet.set_broadcasting(tx, False)
# GUI thread
if result:
success, msg = result

1
electrum/invoices.py

@ -243,6 +243,7 @@ class BaseInvoice(StoredObject):
class Invoice(BaseInvoice):
lightning_invoice = attr.ib(type=str, kw_only=True) # type: Optional[str]
__lnaddr = None
_is_broadcasting = False
def is_lightning(self):
return self.lightning_invoice is not None

44
electrum/wallet.py

@ -75,7 +75,7 @@ from .plugin import run_hook
from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL,
TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE)
from .invoices import BaseInvoice, Invoice, Request
from .invoices import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_UNCONFIRMED
from .invoices import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_UNCONFIRMED, PR_INFLIGHT
from .contacts import Contacts
from .interface import NetworkException
from .mnemonic import Mnemonic
@ -2405,6 +2405,8 @@ 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)
@ -2496,6 +2498,18 @@ class Abstract_Wallet(ABC, Logger, EventListener):
d['bip70'] = x.bip70
return d
def get_invoices_and_requests_touched_by_tx(self, tx):
request_keys = set()
invoice_keys = set()
with self.lock, self.transaction_lock:
for txo in tx.outputs():
addr = txo.address
if request:=self.get_request_by_addr(addr):
request_keys.add(request.get_id())
for invoice_key in self._invoices_from_scriptpubkey_map.get(txo.scriptpubkey, set()):
invoice_keys.add(invoice_key)
return request_keys, invoice_keys
def _update_invoices_and_reqs_touched_by_tx(self, tx_hash: str) -> None:
# FIXME in some cases if tx2 replaces unconfirmed tx1 in the mempool, we are not called.
# For a given receive request, if tx1 touches it but tx2 does not, then
@ -2503,16 +2517,24 @@ class Abstract_Wallet(ABC, Logger, EventListener):
tx = self.db.get_transaction(tx_hash)
if tx is None:
return
relevant_invoice_keys = set()
with self.lock, self.transaction_lock:
for txo in tx.outputs():
addr = txo.address
if request:=self.get_request_by_addr(addr):
status = self.get_invoice_status(request)
util.trigger_callback('request_status', self, request.get_id(), status)
for invoice_key in self._invoices_from_scriptpubkey_map.get(txo.scriptpubkey, set()):
relevant_invoice_keys.add(invoice_key)
self._update_onchain_invoice_paid_detection(relevant_invoice_keys)
request_keys, invoice_keys = self.get_invoices_and_requests_touched_by_tx(tx)
for key in request_keys:
request = self.get_request(key)
if not request:
continue
status = self.get_invoice_status(request)
util.trigger_callback('request_status', self, request.get_id(), status)
self._update_onchain_invoice_paid_detection(invoice_keys)
def set_broadcasting(self, tx: Transaction, b: bool):
request_keys, invoice_keys = self.get_invoices_and_requests_touched_by_tx(tx)
for key in invoice_keys:
invoice = self._invoices.get(key)
if not invoice:
continue
invoice._is_broadcasting = b
status = self.get_invoice_status(invoice)
util.trigger_callback('invoice_status', self, key, status)
def get_bolt11_invoice(self, req: Request) -> str:
if not self.lnworker:

Loading…
Cancel
Save