Browse Source

Catch exceptions raised in LNWorker._pay_to_route

Reset payment status if an exception is caught.
Also, do not pass status to the 'invoice_status' network callback.
This fixes #5869, #5870, #5964.
hard-fail-on-bad-server-string
ThomasV 5 years ago
parent
commit
4dc74870e1
  1. 6
      electrum/gui/kivy/main_window.py
  2. 4
      electrum/gui/kivy/uix/dialogs/invoice_dialog.py
  3. 9
      electrum/gui/qt/invoice_list.py
  4. 8
      electrum/gui/qt/main_window.py
  5. 23
      electrum/lnworker.py
  6. 2
      electrum/wallet.py

6
electrum/gui/kivy/main_window.py

@ -226,7 +226,11 @@ class ElectrumWindow(App):
self.show_info(_('Payment Received') + '\n' + key)
self._trigger_update_history()
def on_invoice_status(self, event, key, status):
def on_invoice_status(self, event, key):
req = self.wallet.get_invoice(key)
if req is None:
return
status = req['status']
# todo: update single item
self.update_tab('send')
if self.invoice_popup and self.invoice_popup.key == key:

4
electrum/gui/kivy/uix/dialogs/invoice_dialog.py

@ -8,7 +8,7 @@ from kivy.clock import Clock
from electrum.gui.kivy.i18n import _
from electrum.util import pr_tooltips, pr_color
from electrum.util import PR_UNKNOWN, PR_UNPAID
from electrum.util import PR_UNKNOWN, PR_UNPAID, PR_FAILED
if TYPE_CHECKING:
from electrum.gui.kivy.main_window import ElectrumWindow
@ -78,7 +78,7 @@ class InvoiceDialog(Factory.Popup):
self.status = status
self.status_str = pr_tooltips[status]
self.status_color = pr_color[status]
self.can_pay = self.status == PR_UNPAID
self.can_pay = self.status in[PR_UNPAID, PR_FAILED]
def on_dismiss(self):
self.app.request_popup = None

9
electrum/gui/qt/invoice_list.py

@ -32,7 +32,7 @@ from PyQt5.QtWidgets import QAbstractItemView
from PyQt5.QtWidgets import QMenu, QVBoxLayout, QTreeWidget, QTreeWidgetItem
from electrum.i18n import _
from electrum.util import format_time, PR_UNPAID, PR_PAID, PR_INFLIGHT
from electrum.util import format_time, PR_UNPAID, PR_PAID, PR_INFLIGHT, PR_FAILED
from electrum.util import get_request_status
from electrum.util import PR_TYPE_ONCHAIN, PR_TYPE_LN
from electrum.lnutil import PaymentAttemptLog
@ -73,10 +73,7 @@ class InvoiceList(MyTreeView):
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.update()
def update_item(self, key, status):
req = self.parent.wallet.get_invoice(key)
if req is None:
return
def update_item(self, key, req):
model = self.model()
for row in range(0, model.rowCount()):
item = model.item(row, 0)
@ -169,6 +166,8 @@ class InvoiceList(MyTreeView):
menu.addAction(_("Details"), lambda: self.parent.show_invoice(key))
if invoice['status'] == PR_UNPAID:
menu.addAction(_("Pay"), lambda: self.parent.do_pay_invoice(invoice))
if invoice['status'] == PR_FAILED:
menu.addAction(_("Retry"), lambda: self.parent.do_pay_invoice(invoice))
if self.parent.wallet.lnworker:
log = self.parent.wallet.lnworker.logs.get(key)
if log:

8
electrum/gui/qt/main_window.py

@ -1439,10 +1439,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self.notify(_('Payment received') + '\n' + key)
self.need_update.set()
def on_invoice_status(self, key, status):
if key not in self.wallet.invoices:
def on_invoice_status(self, key):
req = self.wallet.get_invoice(key)
if req is None:
return
self.invoice_list.update_item(key, status)
status = req['status']
self.invoice_list.update_item(key, req)
if status == PR_PAID:
self.show_message(_('Payment succeeded'))
self.need_update.set()

23
electrum/lnworker.py

@ -818,16 +818,18 @@ class LNWallet(LNWorker):
for i in range(attempts):
try:
route = await self._create_route_from_invoice(decoded_invoice=lnaddr)
except NoPathFound as e:
self.set_payment_status(payment_hash, PR_INFLIGHT)
self.network.trigger_callback('invoice_status', key)
payment_attempt_log = await self._pay_to_route(route, lnaddr)
except Exception as e:
log.append(PaymentAttemptLog(success=False, exception=e))
self.set_payment_status(payment_hash, PR_UNPAID)
break
self.network.trigger_callback('invoice_status', key, PR_INFLIGHT)
payment_attempt_log = await self._pay_to_route(route, lnaddr)
log.append(payment_attempt_log)
success = payment_attempt_log.success
if success:
break
self.network.trigger_callback('invoice_status', key, PR_PAID if success else PR_FAILED)
self.network.trigger_callback('invoice_status', key)
return success
async def _pay_to_route(self, route: LNPaymentRoute, lnaddr: LnAddr) -> PaymentAttemptLog:
@ -837,8 +839,9 @@ class LNWallet(LNWorker):
self.channel_db.remove_channel(short_channel_id)
raise Exception(f"PathFinder returned path with short_channel_id "
f"{short_channel_id} that is not in channel list")
self.set_payment_status(lnaddr.paymenthash, PR_INFLIGHT)
peer = self.peers[route[0].node_id]
peer = self.peers.get(route[0].node_id)
if not peer:
raise Exception('Dropped peer')
htlc = await peer.pay(route, chan, int(lnaddr.amount * COIN * 1000), lnaddr.paymenthash, lnaddr.get_min_final_cltv_expiry())
self.network.trigger_callback('htlc_added', htlc, lnaddr, SENT)
success, preimage, reason = await self.await_payment(lnaddr.paymenthash)
@ -1056,6 +1059,14 @@ class LNWallet(LNWorker):
status = PR_UNPAID
return status
def get_invoice_status(self, key):
# status may be PR_FAILED
status = self.get_payment_status(bfh(key))
log = self.logs[key]
if status == PR_UNPAID and log:
status = PR_FAILED
return status
async def await_payment(self, payment_hash):
success, preimage, reason = await self.pending_payments[payment_hash]
self.pending_payments.pop(payment_hash)

2
electrum/wallet.py

@ -656,7 +656,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
if request_type == PR_TYPE_ONCHAIN:
item['status'] = PR_PAID if self.is_onchain_invoice_paid(item) else PR_UNPAID
elif self.lnworker and request_type == PR_TYPE_LN:
item['status'] = self.lnworker.get_payment_status(bfh(item['rhash']))
item['status'] = self.lnworker.get_invoice_status(key)
else:
return
return item

Loading…
Cancel
Save