electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit b85aea1541098d0284de5b92c1ca997c12c68cdc
parent 4d1785799be2cfc2e34971a48f8c0c5deda21098
Author: SomberNight <somber.night@protonmail.com>
Date:   Wed,  1 Aug 2018 18:32:16 +0200

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

Diffstat:
Melectrum/gui/qt/main_window.py | 36++++++++++++++++++++++++++++++------
Melectrum/lnaddr.py | 4+++-
Melectrum/lnhtlc.py | 2++
Melectrum/lnworker.py | 8++++----
4 files changed, 39 insertions(+), 11 deletions(-)

diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py @@ -1664,13 +1664,37 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): self.do_send(preview = True) def pay_lightning_invoice(self, invoice): - try: - amount = self.amount_e.get_amount() - f = self.wallet.lnworker.pay(invoice, amount_sat=amount) - except InvoiceError as e: - self.show_error(str(e)) - else: + amount = self.amount_e.get_amount() + LN_NUM_PAYMENT_ATTEMPTS = 1 # TODO increase + + def on_success(result): + self.print_error('ln payment success', result) 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): if self.payto_e.is_lightning: diff --git a/electrum/lnaddr.py b/electrum/lnaddr.py @@ -248,7 +248,9 @@ class LnAddr(object): ", ".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) if not hrp: raise ValueError("Bad bech32 checksum") diff --git a/electrum/lnhtlc.py b/electrum/lnhtlc.py @@ -511,10 +511,12 @@ class HTLCStateMachine(PrintError): @property def htlcs_in_local(self): + """in the local log. 'offered by us'""" return self.gen_htlc_indices("local") @property def htlcs_in_remote(self): + """in the remote log. 'offered by them'""" return self.gen_htlc_indices("remote") def settle_htlc(self, preimage, htlc_id): diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -16,7 +16,8 @@ from .lnbase import Peer, privkey_to_pubkey, aiosafe from .lnaddr import lnencode, LnAddr, lndecode from .ecc import der_sig_from_sig_string 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 .i18n import _ @@ -175,7 +176,6 @@ class LNWorker(PrintError): return asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop) 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) payment_hash = addr.paymenthash invoice_pubkey = addr.pubkey.serialize() @@ -185,7 +185,7 @@ class LNWorker(PrintError): amount_msat = int(amount_sat * 1000) path = self.network.path_finder.find_path_for_payment(self.pubkey, invoice_pubkey, amount_msat) if path is None: - raise Exception("No path found") + raise PaymentFailure(_("No path found")) node_id, short_channel_id = path[0] peer = self.peers[node_id] with self.lock: @@ -194,7 +194,7 @@ class LNWorker(PrintError): if chan.short_channel_id == short_channel_id: break 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) return asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)