Browse Source

qt: pay_lightning_invoice - attempt paying multiple times in case of failure

regtest_lnd
SomberNight 7 years ago
parent
commit
44f1b29852
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 36
      electrum/gui/qt/main_window.py
  2. 4
      electrum/lnaddr.py
  3. 2
      electrum/lnhtlc.py
  4. 8
      electrum/lnworker.py

36
electrum/gui/qt/main_window.py

@ -1664,13 +1664,37 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self.do_send(preview = True) self.do_send(preview = True)
def pay_lightning_invoice(self, invoice): def pay_lightning_invoice(self, invoice):
try: amount = self.amount_e.get_amount()
amount = self.amount_e.get_amount() LN_NUM_PAYMENT_ATTEMPTS = 1 # TODO increase
f = self.wallet.lnworker.pay(invoice, amount_sat=amount)
except InvoiceError as e: def on_success(result):
self.show_error(str(e)) self.print_error('ln payment success', result)
else:
self.do_clear() self.do_clear()
def on_failure(exc_info):
type_, e, traceback = exc_info
if isinstance(e, PaymentFailure):
self.show_error(_('Payment failed. Tried {} times:\n{}')
.format(LN_NUM_PAYMENT_ATTEMPTS, e))
elif isinstance(e, InvoiceError):
self.show_error(_('InvoiceError: {}').format(e))
else:
raise e
def task():
failure_list = []
for i in range(LN_NUM_PAYMENT_ATTEMPTS):
try:
future = self.wallet.lnworker.pay(invoice, amount_sat=amount)
future.result()
break
except PaymentFailure as e:
failure_list.append(e)
# try again
else:
msg = '\n'.join(str(e) for e in failure_list)
raise PaymentFailure(msg)
msg = _('Sending lightning payment...')
WaitingDialog(self, msg, task, on_success, on_failure)
def do_send(self, preview = False): def do_send(self, preview = False):
if self.payto_e.is_lightning: if self.payto_e.is_lightning:

4
electrum/lnaddr.py

@ -248,7 +248,9 @@ class LnAddr(object):
", ".join([k + '=' + str(v) for k, v in self.tags]) ", ".join([k + '=' + str(v) for k, v in self.tags])
) )
def lndecode(a, verbose=False, expected_hrp=constants.net.SEGWIT_HRP): def lndecode(a, verbose=False, expected_hrp=None):
if expected_hrp is None:
expected_hrp = constants.net.SEGWIT_HRP
hrp, data = bech32_decode(a, ignore_long_length=True) hrp, data = bech32_decode(a, ignore_long_length=True)
if not hrp: if not hrp:
raise ValueError("Bad bech32 checksum") raise ValueError("Bad bech32 checksum")

2
electrum/lnhtlc.py

@ -511,10 +511,12 @@ class HTLCStateMachine(PrintError):
@property @property
def htlcs_in_local(self): def htlcs_in_local(self):
"""in the local log. 'offered by us'"""
return self.gen_htlc_indices("local") return self.gen_htlc_indices("local")
@property @property
def htlcs_in_remote(self): def htlcs_in_remote(self):
"""in the remote log. 'offered by them'"""
return self.gen_htlc_indices("remote") return self.gen_htlc_indices("remote")
def settle_htlc(self, preimage, htlc_id): def settle_htlc(self, preimage, htlc_id):

8
electrum/lnworker.py

@ -16,7 +16,8 @@ from .lnbase import Peer, privkey_to_pubkey, aiosafe
from .lnaddr import lnencode, LnAddr, lndecode from .lnaddr import lnencode, LnAddr, lndecode
from .ecc import der_sig_from_sig_string from .ecc import der_sig_from_sig_string
from .lnhtlc import HTLCStateMachine from .lnhtlc import HTLCStateMachine
from .lnutil import Outpoint, calc_short_channel_id, LNPeerAddr, get_compressed_pubkey_from_bech32 from .lnutil import (Outpoint, calc_short_channel_id, LNPeerAddr, get_compressed_pubkey_from_bech32,
PaymentFailure)
from .lnwatcher import LNChanCloseHandler from .lnwatcher import LNChanCloseHandler
from .i18n import _ from .i18n import _
@ -175,7 +176,6 @@ class LNWorker(PrintError):
return asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop) return asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
def pay(self, invoice, amount_sat=None): def pay(self, invoice, amount_sat=None):
# TODO try some number of paths (e.g. 10) in case of failures
addr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP) addr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP)
payment_hash = addr.paymenthash payment_hash = addr.paymenthash
invoice_pubkey = addr.pubkey.serialize() invoice_pubkey = addr.pubkey.serialize()
@ -185,7 +185,7 @@ class LNWorker(PrintError):
amount_msat = int(amount_sat * 1000) amount_msat = int(amount_sat * 1000)
path = self.network.path_finder.find_path_for_payment(self.pubkey, invoice_pubkey, amount_msat) path = self.network.path_finder.find_path_for_payment(self.pubkey, invoice_pubkey, amount_msat)
if path is None: if path is None:
raise Exception("No path found") raise PaymentFailure(_("No path found"))
node_id, short_channel_id = path[0] node_id, short_channel_id = path[0]
peer = self.peers[node_id] peer = self.peers[node_id]
with self.lock: with self.lock:
@ -194,7 +194,7 @@ class LNWorker(PrintError):
if chan.short_channel_id == short_channel_id: if chan.short_channel_id == short_channel_id:
break break
else: else:
raise Exception("ChannelDB returned path with short_channel_id that is not in channel list") raise Exception("ChannelDB returned path with short_channel_id {} that is not in channel list".format(bh2u(short_channel_id)))
coro = peer.pay(path, chan, amount_msat, payment_hash, invoice_pubkey, addr.min_final_cltv_expiry) coro = peer.pay(path, chan, amount_msat, payment_hash, invoice_pubkey, addr.min_final_cltv_expiry)
return asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop) return asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)

Loading…
Cancel
Save