diff --git a/electrum/commands.py b/electrum/commands.py index dd04f770c..6058eed70 100644 --- a/electrum/commands.py +++ b/electrum/commands.py @@ -1103,12 +1103,12 @@ class Commands: return invoice.to_debug_json() @command('wnl') - async def lnpay(self, invoice, attempts=1, timeout=30, wallet: Abstract_Wallet = None): + async def lnpay(self, invoice, timeout=120, wallet: Abstract_Wallet = None): lnworker = wallet.lnworker lnaddr = lnworker._check_invoice(invoice) payment_hash = lnaddr.paymenthash wallet.save_invoice(Invoice.from_bech32(invoice)) - success, log = await lnworker.pay_invoice(invoice, attempts=attempts) + success, log = await lnworker.pay_invoice(invoice) return { 'payment_hash': payment_hash.hex(), 'success': success, @@ -1347,7 +1347,6 @@ command_options = { 'domain': ("-D", "List of addresses"), 'memo': ("-m", "Description of the request"), 'expiration': (None, "Time in seconds"), - 'attempts': (None, "Number of payment attempts"), 'timeout': (None, "Timeout in seconds"), 'force': (None, "Create new address beyond gap limit, if no more addresses are available."), 'pending': (None, "Show only pending requests."), @@ -1394,7 +1393,6 @@ arg_types = { 'encrypt_file': eval_bool, 'rbf': eval_bool, 'timeout': float, - 'attempts': int, } config_variables = { diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py index 7c6439f80..c54565187 100644 --- a/electrum/gui/kivy/uix/screens.py +++ b/electrum/gui/kivy/uix/screens.py @@ -361,7 +361,7 @@ class SendScreen(CScreen, Logger): amount_msat = invoice.get_amount_msat() def pay_thread(): try: - coro = self.app.wallet.lnworker.pay_invoice(invoice.lightning_invoice, amount_msat=amount_msat, attempts=10) + coro = self.app.wallet.lnworker.pay_invoice(invoice.lightning_invoice, amount_msat=amount_msat) fut = asyncio.run_coroutine_threadsafe(coro, self.app.network.asyncio_loop) fut.result() except Exception as e: diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index 97e1b7abb..0a230fddd 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -1724,7 +1724,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): if not self.question(msg): return self.save_pending_invoice() - coro = self.wallet.lnworker.pay_invoice(invoice.lightning_invoice, amount_msat=amount_msat, attempts=LN_NUM_PAYMENT_ATTEMPTS) + coro = self.wallet.lnworker.pay_invoice(invoice.lightning_invoice, amount_msat=amount_msat) self.run_coroutine_from_thread(coro) def on_request_status(self, wallet, key, status): diff --git a/electrum/lnworker.py b/electrum/lnworker.py index 3c3bff478..42098ad32 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -607,6 +607,7 @@ class LNWallet(LNWorker): lnwatcher: Optional['LNWalletWatcher'] MPP_EXPIRY = 120 TIMEOUT_SHUTDOWN_FAIL_PENDING_HTLCS = 3 # seconds + PAYMENT_TIMEOUT = 120 def __init__(self, wallet: 'Abstract_Wallet', xprv): self.wallet = wallet @@ -1091,7 +1092,7 @@ class LNWallet(LNWorker): async def _pay_scheduled_invoices(self): for invoice in self.wallet.get_scheduled_invoices(): if invoice.is_lightning() and self.can_pay_invoice(invoice): - await self.pay_invoice(invoice.lightning_invoice, attempts=10) + await self.pay_invoice(invoice.lightning_invoice) def can_pay_invoice(self, invoice: Invoice) -> bool: assert invoice.is_lightning() @@ -1131,7 +1132,7 @@ class LNWallet(LNWorker): async def pay_invoice( self, invoice: str, *, amount_msat: int = None, - attempts: int = 1, + attempts: int = None, # used only in unit tests full_path: LNPaymentPath = None) -> Tuple[bool, List[HtlcLog]]: lnaddr = self._check_invoice(invoice, amount_msat=amount_msat) @@ -1192,7 +1193,7 @@ class LNWallet(LNWorker): min_cltv_expiry: int, r_tags, invoice_features: int, - attempts: int = 1, + attempts: int = None, full_path: LNPaymentPath = None, fwd_trampoline_onion=None, fwd_trampoline_fee=None, @@ -1213,7 +1214,7 @@ class LNWallet(LNWorker): use_two_trampolines = True trampoline_fee_level = self.INITIAL_TRAMPOLINE_FEE_LEVEL - + start_time = time.time() amount_inflight = 0 # what we sent in htlcs (that receiver gets, without fees) while True: amount_to_send = amount_to_pay - amount_inflight @@ -1269,7 +1270,7 @@ class LNWallet(LNWorker): self.network.path_finder.update_inflight_htlcs(htlc_log.route, add_htlcs=False) return # htlc failed - if len(log) >= attempts: + if (attempts is not None and len(log) >= attempts) or (attempts is None and time.time() - start_time > self.PAYMENT_TIMEOUT): raise PaymentFailure('Giving up after %d attempts'%len(log)) # if we get a tmp channel failure, it might work to split the amount and try more routes # if we get a channel update, we might retry the same route and amount diff --git a/electrum/tests/test_lnpeer.py b/electrum/tests/test_lnpeer.py index 6cf4f1be4..9ba096a1c 100644 --- a/electrum/tests/test_lnpeer.py +++ b/electrum/tests/test_lnpeer.py @@ -117,6 +117,7 @@ class MockWallet: class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]): MPP_EXPIRY = 2 # HTLC timestamps are cast to int, so this cannot be 1 + PAYMENT_TIMEOUT = 120 TIMEOUT_SHUTDOWN_FAIL_PENDING_HTLCS = 0 INITIAL_TRAMPOLINE_FEE_LEVEL = 0