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

2
electrum/gui/qml/qewallet.py

@ -549,6 +549,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
assert tx.is_complete() assert tx.is_complete()
def broadcast_thread(): def broadcast_thread():
self.wallet.set_broadcasting(tx, True)
try: try:
self._logger.info('running broadcast in thread') self._logger.info('running broadcast in thread')
self.wallet.network.run_from_another_thread(self.wallet.network.broadcast_transaction(tx)) 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._logger.info('broadcast success')
self.broadcastSucceeded.emit(tx.txid()) self.broadcastSucceeded.emit(tx.txid())
self.historyModel.requestRefresh.emit() # via qt thread self.historyModel.requestRefresh.emit() # via qt thread
self.wallet.set_broadcasting(tx, False)
threading.Thread(target=broadcast_thread, daemon=True).start() 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 # Capture current TL window; override might be removed on return
parent = self.window.top_level_window(lambda win: isinstance(win, MessageBoxMixin)) parent = self.window.top_level_window(lambda win: isinstance(win, MessageBoxMixin))
self.wallet.set_broadcasting(tx, True)
def broadcast_done(result): def broadcast_done(result):
self.wallet.set_broadcasting(tx, False)
# GUI thread # GUI thread
if result: if result:
success, msg = result success, msg = result

1
electrum/invoices.py

@ -243,6 +243,7 @@ class BaseInvoice(StoredObject):
class Invoice(BaseInvoice): class Invoice(BaseInvoice):
lightning_invoice = attr.ib(type=str, kw_only=True) # type: Optional[str] lightning_invoice = attr.ib(type=str, kw_only=True) # type: Optional[str]
__lnaddr = None __lnaddr = None
_is_broadcasting = False
def is_lightning(self): def is_lightning(self):
return self.lightning_invoice is not None 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, from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL,
TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE) TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE)
from .invoices import BaseInvoice, Invoice, Request 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 .contacts import Contacts
from .interface import NetworkException from .interface import NetworkException
from .mnemonic import Mnemonic from .mnemonic import Mnemonic
@ -2405,6 +2405,8 @@ class Abstract_Wallet(ABC, Logger, EventListener):
def get_invoice_status(self, invoice: BaseInvoice): def get_invoice_status(self, invoice: BaseInvoice):
"""Returns status of (incoming) request or (outgoing) invoice.""" """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 # lightning invoices can be paid onchain
if invoice.is_lightning() and self.lnworker: if invoice.is_lightning() and self.lnworker:
status = self.lnworker.get_invoice_status(invoice) status = self.lnworker.get_invoice_status(invoice)
@ -2496,6 +2498,18 @@ class Abstract_Wallet(ABC, Logger, EventListener):
d['bip70'] = x.bip70 d['bip70'] = x.bip70
return d 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: 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. # 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 # 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) tx = self.db.get_transaction(tx_hash)
if tx is None: if tx is None:
return return
relevant_invoice_keys = set() request_keys, invoice_keys = self.get_invoices_and_requests_touched_by_tx(tx)
with self.lock, self.transaction_lock: for key in request_keys:
for txo in tx.outputs(): request = self.get_request(key)
addr = txo.address if not request:
if request:=self.get_request_by_addr(addr): continue
status = self.get_invoice_status(request) status = self.get_invoice_status(request)
util.trigger_callback('request_status', self, request.get_id(), status) util.trigger_callback('request_status', self, request.get_id(), status)
for invoice_key in self._invoices_from_scriptpubkey_map.get(txo.scriptpubkey, set()): self._update_onchain_invoice_paid_detection(invoice_keys)
relevant_invoice_keys.add(invoice_key)
self._update_onchain_invoice_paid_detection(relevant_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: def get_bolt11_invoice(self, req: Request) -> str:
if not self.lnworker: if not self.lnworker:

Loading…
Cancel
Save