From 286fc4b86e4d23cb9af15b9061b3d709e7592bcb Mon Sep 17 00:00:00 2001 From: f321x Date: Fri, 26 Sep 2025 16:11:20 +0200 Subject: [PATCH] lnworker: enforce creation of PaymentInfo for b11 Enforce that the information used to create a bolt11 invoice using `get_bolt11_invoice()` is similar to the related instance of PaymentInfo by requiring a PaymentInfo as argument for `get_bolt11_invoice()`. This way the invoice cannot differ from the created PaymentInfo. This allows to use the information in PaymentInfo for validation of incoming htlcs more reliably. To cover all required information for the creation of a b11 invoice the PaymentInfo class has to be extended with a expiry and min_final_cltv_expiry. This requires a db upgrade. --- electrum/commands.py | 24 ++++----- electrum/gui/qt/main_window.py | 4 +- electrum/lnutil.py | 7 +-- electrum/lnworker.py | 86 +++++++++++++++++++++---------- electrum/plugins/nwc/nwcserver.py | 15 +++--- electrum/submarine_swaps.py | 31 ++++++----- electrum/wallet.py | 12 +++-- electrum/wallet_db.py | 15 +++++- tests/test_lnpeer.py | 20 +++---- 9 files changed, 134 insertions(+), 80 deletions(-) diff --git a/electrum/commands.py b/electrum/commands.py index 09bc31a14..c66169813 100644 --- a/electrum/commands.py +++ b/electrum/commands.py @@ -68,7 +68,7 @@ from .wallet import ( ) from .address_synchronizer import TX_HEIGHT_LOCAL from .mnemonic import Mnemonic -from .lnutil import (channel_id_from_funding_tx, LnFeatures, SENT, MIN_FINAL_CLTV_DELTA_FOR_INVOICE, +from .lnutil import (channel_id_from_funding_tx, LnFeatures, SENT, MIN_FINAL_CLTV_DELTA_ACCEPTED, PaymentFeeBudget, NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE) from .plugin import run_hook, DeviceMgr, Plugins from .version import ELECTRUM_VERSION @@ -1382,7 +1382,7 @@ class Commands(Logger): amount: Optional[Decimal] = None, memo: str = "", expiry: int = 3600, - min_final_cltv_expiry_delta: int = MIN_FINAL_CLTV_DELTA_FOR_INVOICE * 2, + min_final_cltv_expiry_delta: int = MIN_FINAL_CLTV_DELTA_ACCEPTED * 2, wallet: Abstract_Wallet = None ) -> dict: """ @@ -1399,23 +1399,23 @@ class Commands(Logger): assert payment_hash not in wallet.lnworker.payment_info, "Payment hash already used!" assert payment_hash not in wallet.lnworker.dont_settle_htlcs, "Payment hash already used!" assert wallet.lnworker.get_preimage(bfh(payment_hash)) is None, "Already got a preimage for this payment hash!" - assert MIN_FINAL_CLTV_DELTA_FOR_INVOICE < min_final_cltv_expiry_delta < 576, "Use a sane min_final_cltv_expiry_delta value" + assert MIN_FINAL_CLTV_DELTA_ACCEPTED < min_final_cltv_expiry_delta < 576, "Use a sane min_final_cltv_expiry_delta value" amount = amount if amount and satoshis(amount) > 0 else None # make amount either >0 or None inbound_capacity = wallet.lnworker.num_sats_can_receive() assert inbound_capacity > satoshis(amount or 0), \ f"Not enough inbound capacity [{inbound_capacity} sat] to receive this payment" - lnaddr, invoice = wallet.lnworker.get_bolt11_invoice( - payment_hash=bfh(payment_hash), - amount_msat=satoshis(amount) * 1000 if amount else None, - message=memo, - expiry=expiry, - min_final_cltv_expiry_delta=min_final_cltv_expiry_delta, - fallback_address=None - ) wallet.lnworker.add_payment_info_for_hold_invoice( bfh(payment_hash), - satoshis(amount) if amount else None, + lightning_amount_sat=satoshis(amount) if amount else None, + min_final_cltv_delta=min_final_cltv_expiry_delta, + exp_delay=expiry, + ) + info = wallet.lnworker.get_payment_info(bfh(payment_hash)) + lnaddr, invoice = wallet.lnworker.get_bolt11_invoice( + payment_info=info, + message=memo, + fallback_address=None ) wallet.lnworker.dont_settle_htlcs[payment_hash] = None wallet.set_label(payment_hash, memo) diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index 9fbbcb5d9..31f0b8446 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -2758,7 +2758,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): self.closing_warning_callbacks.append(warning_callback) def _check_ongoing_force_closures(self) -> Optional[str]: - from electrum.lnutil import MIN_FINAL_CLTV_DELTA_FOR_INVOICE + from electrum.lnutil import MIN_FINAL_CLTV_DELTA_ACCEPTED if not self.wallet.has_lightning(): return None if not self.network: @@ -2767,7 +2767,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): if not force_closes: return # fixme: this is inaccurate, we need local_height - cltv_of_htlc - cltv_delta = MIN_FINAL_CLTV_DELTA_FOR_INVOICE + cltv_delta = MIN_FINAL_CLTV_DELTA_ACCEPTED msg = '\n\n'.join([ _("Pending channel force-close"), messages.MSG_FORCE_CLOSE_WARNING.format(cltv_delta), diff --git a/electrum/lnutil.py b/electrum/lnutil.py index 025cc9ad8..c21d9271b 100644 --- a/electrum/lnutil.py +++ b/electrum/lnutil.py @@ -505,9 +505,10 @@ MIN_FUNDING_SAT = 200_000 # the minimum cltv_expiry accepted for newly received HTLCs # note: when changing, consider Blockchain.is_tip_stale() MIN_FINAL_CLTV_DELTA_ACCEPTED = 144 -# set it a tiny bit higher for invoices as blocks could get mined -# during forward path of payment -MIN_FINAL_CLTV_DELTA_FOR_INVOICE = MIN_FINAL_CLTV_DELTA_ACCEPTED + 3 + +# buffer added to min_final_cltv_delta of created bolt11 invoices to make verifying the cltv delta +# of incoming payment htlcs reliable even if some blocks have been mined during forwarding +MIN_FINAL_CLTV_DELTA_BUFFER_INVOICE = 3 # the deadline for offered HTLCs: # the deadline after which the channel has to be failed and timed out on-chain diff --git a/electrum/lnworker.py b/electrum/lnworker.py index a4f667808..39f2013cb 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -65,11 +65,11 @@ from .lnchannel import Channel, AbstractChannel, ChannelState, PeerState, HTLCWi from .lnrater import LNRater from .lnutil import ( get_compressed_pubkey_from_bech32, serialize_htlc_key, deserialize_htlc_key, PaymentFailure, generate_keypair, - LnKeyFamily, LOCAL, REMOTE, MIN_FINAL_CLTV_DELTA_FOR_INVOICE, SENT, RECEIVED, HTLCOwner, UpdateAddHtlc, LnFeatures, + LnKeyFamily, LOCAL, REMOTE, MIN_FINAL_CLTV_DELTA_ACCEPTED, SENT, RECEIVED, HTLCOwner, UpdateAddHtlc, LnFeatures, ShortChannelID, HtlcLog, NoPathFound, InvalidGossipMsg, FeeBudgetExceeded, ImportedChannelBackupStorage, OnchainChannelBackupStorage, ln_compare_features, IncompatibleLightningFeatures, PaymentFeeBudget, NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE, GossipForwardingMessage, MIN_FUNDING_SAT, - RecvMPPResolution, ReceivedMPPStatus, + MIN_FINAL_CLTV_DELTA_BUFFER_INVOICE, ReceivedMPPStatus, RecvMPPResolution, ) from .lnonion import ( decode_onion_error, OnionFailureCode, OnionRoutingFailure, OnionPacket, @@ -119,12 +119,22 @@ class PaymentInfo: amount_msat: Optional[int] direction: int status: int + min_final_cltv_delta: int + expiry_delay: int + creation_ts: int = dataclasses.field(default_factory=lambda: int(time.time())) + + @property + def expiration_ts(self): + return self.creation_ts + self.expiry_delay def validate(self): assert isinstance(self.payment_hash, bytes) and len(self.payment_hash) == 32 assert self.amount_msat is None or isinstance(self.amount_msat, int) assert isinstance(self.direction, int) assert isinstance(self.status, int) + assert isinstance(self.min_final_cltv_delta, int) + assert isinstance(self.expiry_delay, int) and self.expiry_delay > 0 + assert isinstance(self.creation_ts, int) def __post_init__(self): self.validate() @@ -864,7 +874,8 @@ class LNWallet(LNWorker): LNWorker.__init__(self, self.node_keypair, features, config=self.config) self.lnwatcher = LNWatcher(self) self.lnrater: LNRater = None - self.payment_info = self.db.get_dict('lightning_payments') # RHASH -> amount, direction, is_paid + # lightning_payments: RHASH -> amount_msat, direction, status, min_final_cltv_delta, expiry_delay, creation_ts + self.payment_info = self.db.get_dict('lightning_payments') # type: dict[str, Tuple[Optional[int], int, int, int, int, int]] self._preimages = self.db.get_dict('lightning_preimages') # RHASH -> preimage self._bolt11_cache = {} # note: this sweep_address is only used as fallback; as it might result in address-reuse @@ -1567,6 +1578,8 @@ class LNWallet(LNWorker): amount_msat=amount_to_pay, direction=SENT, status=PR_UNPAID, + min_final_cltv_delta=min_final_cltv_delta, + expiry_delay=LN_EXPIRY_NEVER, ) self.save_payment_info(info) self.wallet.set_label(key, lnaddr.get_description()) @@ -2238,17 +2251,13 @@ class LNWallet(LNWorker): def get_bolt11_invoice( self, *, - payment_hash: bytes, - amount_msat: Optional[int], + payment_info: PaymentInfo, message: str, - expiry: int, # expiration of invoice (in seconds, relative) fallback_address: Optional[str], channels: Optional[Sequence[Channel]] = None, - min_final_cltv_expiry_delta: Optional[int] = None, ) -> Tuple[LnAddr, str]: - assert isinstance(payment_hash, bytes), f"expected bytes, but got {type(payment_hash)}" - - pair = self._bolt11_cache.get(payment_hash) + amount_msat = payment_info.amount_msat + pair = self._bolt11_cache.get(payment_info.payment_hash) if pair: lnaddr, invoice = pair assert lnaddr.get_amount_msat() == amount_msat @@ -2265,19 +2274,16 @@ class LNWallet(LNWorker): if needs_jit: # jit only works with single htlcs, mpp will cause LSP to open channels for each htlc invoice_features &= ~ LnFeatures.BASIC_MPP_OPT & ~ LnFeatures.BASIC_MPP_REQ - payment_secret = self.get_payment_secret(payment_hash) + payment_secret = self.get_payment_secret(payment_info.payment_hash) amount_btc = amount_msat/Decimal(COIN*1000) if amount_msat else None - if expiry == 0: - expiry = LN_EXPIRY_NEVER - if min_final_cltv_expiry_delta is None: - min_final_cltv_expiry_delta = MIN_FINAL_CLTV_DELTA_FOR_INVOICE + min_final_cltv_delta = payment_info.min_final_cltv_delta + MIN_FINAL_CLTV_DELTA_BUFFER_INVOICE lnaddr = LnAddr( - paymenthash=payment_hash, + paymenthash=payment_info.payment_hash, amount=amount_btc, tags=[ ('d', message), - ('c', min_final_cltv_expiry_delta), - ('x', expiry), + ('c', min_final_cltv_delta), + ('x', payment_info.expiry_delay), ('9', invoice_features), ('f', fallback_address), ] + routing_hints, @@ -2285,7 +2291,7 @@ class LNWallet(LNWorker): payment_secret=payment_secret) invoice = lnencode(lnaddr, self.node_keypair.privkey) pair = lnaddr, invoice - self._bolt11_cache[payment_hash] = pair + self._bolt11_cache[payment_info.payment_hash] = pair return pair def get_payment_secret(self, payment_hash): @@ -2299,14 +2305,23 @@ class LNWallet(LNWorker): payment_secret = self.get_payment_secret(payment_hash) return payment_hash + payment_secret - def create_payment_info(self, *, amount_msat: Optional[int], write_to_disk=True) -> bytes: + def create_payment_info( + self, *, + amount_msat: Optional[int], + min_final_cltv_delta: Optional[int] = None, + exp_delay: int = LN_EXPIRY_NEVER, + write_to_disk=True + ) -> bytes: payment_preimage = os.urandom(32) payment_hash = sha256(payment_preimage) + min_final_cltv_delta = min_final_cltv_delta or MIN_FINAL_CLTV_DELTA_ACCEPTED info = PaymentInfo( payment_hash=payment_hash, amount_msat=amount_msat, direction=RECEIVED, status=PR_UNPAID, + min_final_cltv_delta=min_final_cltv_delta, + expiry_delay=exp_delay ) self.save_preimage(payment_hash, payment_preimage, write_to_disk=False) self.save_payment_info(info, write_to_disk=False) @@ -2380,22 +2395,34 @@ class LNWallet(LNWorker): key = payment_hash.hex() with self.lock: if key in self.payment_info: - amount_msat, direction, status = self.payment_info[key] + stored_tuple = self.payment_info[key] + amount_msat, direction, status, min_final_cltv_delta, expiry_delay, creation_ts = stored_tuple return PaymentInfo( payment_hash=payment_hash, amount_msat=amount_msat, direction=direction, status=status, + min_final_cltv_delta=min_final_cltv_delta, + expiry_delay=expiry_delay, + creation_ts=creation_ts, ) return None - def add_payment_info_for_hold_invoice(self, payment_hash: bytes, lightning_amount_sat: Optional[int]): + def add_payment_info_for_hold_invoice( + self, + payment_hash: bytes, *, + lightning_amount_sat: Optional[int], + min_final_cltv_delta: int, + exp_delay: int, + ): amount = lightning_amount_sat * 1000 if lightning_amount_sat else None info = PaymentInfo( payment_hash=payment_hash, amount_msat=amount, direction=RECEIVED, status=PR_UNPAID, + min_final_cltv_delta=min_final_cltv_delta, + expiry_delay=exp_delay, ) self.save_payment_info(info, write_to_disk=False) @@ -2411,9 +2438,12 @@ class LNWallet(LNWorker): if old_info := self.get_payment_info(payment_hash=info.payment_hash): if info == old_info: return # already saved + if info.direction == SENT: + # allow saving of newer PaymentInfo if it is a sending attempt + old_info = dataclasses.replace(old_info, creation_ts=info.creation_ts) if info != dataclasses.replace(old_info, status=info.status): # differs more than in status. let's fail - raise Exception("payment_hash already in use") + raise Exception(f"payment_hash already in use: {info=} != {old_info=}") key = info.payment_hash.hex() self.payment_info[key] = dataclasses.astuple(info)[1:] # drop the payment hash at index 0 if write_to_disk: @@ -3031,12 +3061,14 @@ class LNWallet(LNWorker): raise Exception('Rebalance requires two different channels') if self.uses_trampoline() and chan1.node_id == chan2.node_id: raise Exception('Rebalance requires channels from different trampolines') - payment_hash = self.create_payment_info(amount_msat=amount_msat) - lnaddr, invoice = self.get_bolt11_invoice( - payment_hash=payment_hash, + payment_hash = self.create_payment_info( amount_msat=amount_msat, + exp_delay=3600, + ) + info = self.get_payment_info(payment_hash) + lnaddr, invoice = self.get_bolt11_invoice( + payment_info=info, message='rebalance', - expiry=3600, fallback_address=None, channels=[chan2], ) diff --git a/electrum/plugins/nwc/nwcserver.py b/electrum/plugins/nwc/nwcserver.py index 64b16a56b..c2cd925f5 100644 --- a/electrum/plugins/nwc/nwcserver.py +++ b/electrum/plugins/nwc/nwcserver.py @@ -480,12 +480,11 @@ class NWCServer(Logger, EventListener): address=None ) req: Request = self.wallet.get_request(key) + info = self.wallet.lnworker.get_payment_info(req.payment_hash) try: lnaddr, b11 = self.wallet.lnworker.get_bolt11_invoice( - payment_hash=req.payment_hash, - amount_msat=amount_msat, + payment_info=info, message=description, - expiry=expiry, fallback_address=None ) except Exception: @@ -538,11 +537,10 @@ class NWCServer(Logger, EventListener): b11 = invoice.lightning_invoice elif self.wallet.get_request(invoice.rhash): direction = "incoming" + info = self.wallet.lnworker.get_payment_info(invoice.payment_hash) _, b11 = self.wallet.lnworker.get_bolt11_invoice( - payment_hash=bytes.fromhex(invoice.rhash), - amount_msat=invoice.amount_msat, + payment_info=info, message=invoice.message, - expiry=invoice.exp, fallback_address=None ) @@ -749,11 +747,10 @@ class NWCServer(Logger, EventListener): request: Optional[Request] = self.wallet.get_request(key) if not request or not request.is_lightning() or not status == PR_PAID: return + info = self.wallet.lnworker.get_payment_info(request.payment_hash) _, b11 = self.wallet.lnworker.get_bolt11_invoice( - payment_hash=request.payment_hash, - amount_msat=request.get_amount_msat(), + payment_info=info, message=request.message, - expiry=request.exp, fallback_address=None ) diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py index 99468eb2e..b0b623083 100644 --- a/electrum/submarine_swaps.py +++ b/electrum/submarine_swaps.py @@ -36,7 +36,8 @@ from .util import ( run_sync_function_on_asyncio_thread, trigger_callback, NoDynamicFeeEstimates, UserFacingException, ) from . import lnutil -from .lnutil import hex_to_bytes, REDEEM_AFTER_DOUBLE_SPENT_DELAY, Keypair +from .lnutil import (hex_to_bytes, REDEEM_AFTER_DOUBLE_SPENT_DELAY, Keypair, + MIN_FINAL_CLTV_DELTA_ACCEPTED) from .lnaddr import lndecode from .json_db import StoredObject, stored_in from . import constants @@ -66,7 +67,6 @@ MAX_LOCKTIME_DELTA = 100 MIN_FINAL_CLTV_DELTA_FOR_CLIENT = 3 * 144 # note: put in invoice, but is not enforced by receiver in lnpeer.py assert MIN_LOCKTIME_DELTA <= LOCKTIME_DELTA_REFUND <= MAX_LOCKTIME_DELTA assert MAX_LOCKTIME_DELTA < lnutil.MIN_FINAL_CLTV_DELTA_ACCEPTED -assert MAX_LOCKTIME_DELTA < lnutil.MIN_FINAL_CLTV_DELTA_FOR_INVOICE assert MAX_LOCKTIME_DELTA < MIN_FINAL_CLTV_DELTA_FOR_CLIENT @@ -645,33 +645,38 @@ class SwapManager(Logger): else: invoice_amount_sat = lightning_amount_sat + # add payment info to lnworker + self.lnworker.add_payment_info_for_hold_invoice( + payment_hash, + lightning_amount_sat=invoice_amount_sat, + min_final_cltv_delta=min_final_cltv_expiry_delta or MIN_FINAL_CLTV_DELTA_ACCEPTED, + exp_delay=300, + ) + info = self.lnworker.get_payment_info(payment_hash) lnaddr1, invoice = self.lnworker.get_bolt11_invoice( - payment_hash=payment_hash, - amount_msat=invoice_amount_sat * 1000, + payment_info=info, message='Submarine swap', - expiry=300, fallback_address=None, channels=channels, - min_final_cltv_expiry_delta=min_final_cltv_expiry_delta, ) margin_to_get_refund_tx_mined = MIN_LOCKTIME_DELTA if not (locktime + margin_to_get_refund_tx_mined < self.network.get_local_height() + lnaddr1.get_min_final_cltv_delta()): raise Exception( f"onchain locktime ({locktime}+{margin_to_get_refund_tx_mined}) " f"too close to LN-htlc-expiry ({self.network.get_local_height()+lnaddr1.get_min_final_cltv_delta()})") - # add payment info to lnworker - self.lnworker.add_payment_info_for_hold_invoice(payment_hash, invoice_amount_sat) if prepay: - prepay_hash = self.lnworker.create_payment_info(amount_msat=prepay_amount_sat*1000) + prepay_hash = self.lnworker.create_payment_info( + amount_msat=prepay_amount_sat*1000, + min_final_cltv_delta=min_final_cltv_expiry_delta or MIN_FINAL_CLTV_DELTA_ACCEPTED, + exp_delay=300, + ) + info = self.lnworker.get_payment_info(prepay_hash) lnaddr2, prepay_invoice = self.lnworker.get_bolt11_invoice( - payment_hash=prepay_hash, - amount_msat=prepay_amount_sat * 1000, + payment_info=info, message='Submarine swap prepayment', - expiry=300, fallback_address=None, channels=channels, - min_final_cltv_expiry_delta=min_final_cltv_expiry_delta, ) self.lnworker.bundle_payments([payment_hash, prepay_hash]) self._prepayments[prepay_hash] = payment_hash diff --git a/electrum/wallet.py b/electrum/wallet.py index 7bf807e61..9bd1e9c22 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -3011,11 +3011,11 @@ class Abstract_Wallet(ABC, Logger, EventListener): return '' amount_msat = req.get_amount_msat() or None assert (amount_msat is None or amount_msat > 0), amount_msat + info = self.lnworker.get_payment_info(payment_hash) + assert info.amount_msat == amount_msat, f"{info.amount_msat=} != {amount_msat=}" lnaddr, invoice = self.lnworker.get_bolt11_invoice( - payment_hash=payment_hash, - amount_msat=amount_msat, + payment_info=info, message=req.message, - expiry=req.exp, fallback_address=None) return invoice @@ -3031,7 +3031,11 @@ class Abstract_Wallet(ABC, Logger, EventListener): timestamp = int(Request._get_cur_time()) if address is None: assert self.has_lightning() - payment_hash = self.lnworker.create_payment_info(amount_msat=amount_msat, write_to_disk=False) + payment_hash = self.lnworker.create_payment_info( + amount_msat=amount_msat, + exp_delay=exp_delay, + write_to_disk=False, + ) else: payment_hash = None outputs = [PartialTxOutput.from_address_and_value(address, amount_sat)] if address else [] diff --git a/electrum/wallet_db.py b/electrum/wallet_db.py index f527d5d47..95d038e9a 100644 --- a/electrum/wallet_db.py +++ b/electrum/wallet_db.py @@ -73,7 +73,7 @@ class WalletUnfinished(WalletFileException): # seed_version is now used for the version of the wallet file OLD_SEED_VERSION = 4 # electrum versions < 2.0 NEW_SEED_VERSION = 11 # electrum versions >= 2.0 -FINAL_SEED_VERSION = 60 # electrum >= 2.7 will set this to prevent +FINAL_SEED_VERSION = 61 # electrum >= 2.7 will set this to prevent # old versions from overwriting new format @@ -236,6 +236,7 @@ class WalletDBUpgrader(Logger): self._convert_version_58() self._convert_version_59() self._convert_version_60() + self._convert_version_61() self.put('seed_version', FINAL_SEED_VERSION) # just to be sure def _convert_wallet_type(self): @@ -1157,6 +1158,18 @@ class WalletDBUpgrader(Logger): cb['multisig_funding_privkey'] = None self.data['seed_version'] = 60 + def _convert_version_61(self): + if not self._is_upgrade_method_needed(60, 60): + return + # adding additional fields to PaymentInfo + lightning_payments = self.data.get('lightning_payments', {}) + expiry_never = 100 * 365 * 24 * 60 * 60 + migration_time = int(time.time()) + for rhash, (amount_msat, direction, is_paid) in list(lightning_payments.items()): + new = (amount_msat, direction, is_paid, 147, expiry_never, migration_time) + lightning_payments[rhash] = new + self.data['seed_version'] = 61 + def _convert_imported(self): if not self._is_upgrade_method_needed(0, 13): return diff --git a/tests/test_lnpeer.py b/tests/test_lnpeer.py index 85c241520..f7d84020a 100644 --- a/tests/test_lnpeer.py +++ b/tests/test_lnpeer.py @@ -41,7 +41,7 @@ from electrum.lnworker import PaymentInfo, RECEIVED from electrum.lnonion import OnionFailureCode, OnionRoutingFailure from electrum.lnutil import UpdateAddHtlc from electrum.lnutil import LOCAL, REMOTE -from electrum.invoices import PR_PAID, PR_UNPAID, Invoice +from electrum.invoices import PR_PAID, PR_UNPAID, Invoice, LN_EXPIRY_NEVER from electrum.interface import GracefulDisconnect from electrum.simple_config import SimpleConfig from electrum.fee_policy import FeeTimeEstimates, FEE_ETA_TARGETS @@ -563,15 +563,8 @@ class TestPeer(ElectrumTestCase): payment_preimage = os.urandom(32) if payment_hash is None: payment_hash = sha256(payment_preimage) - info = PaymentInfo( - payment_hash=payment_hash, - amount_msat=amount_msat, - direction=RECEIVED, - status=PR_UNPAID, - ) if payment_preimage: w2.save_preimage(payment_hash, payment_preimage) - w2.save_payment_info(info) if include_routing_hints: routing_hints = w2.calc_routing_hints_for_invoice(amount_msat) else: @@ -584,7 +577,16 @@ class TestPeer(ElectrumTestCase): else: payment_secret = None if min_final_cltv_delta is None: - min_final_cltv_delta = lnutil.MIN_FINAL_CLTV_DELTA_FOR_INVOICE + min_final_cltv_delta = lnutil.MIN_FINAL_CLTV_DELTA_ACCEPTED + info = PaymentInfo( + payment_hash=payment_hash, + amount_msat=amount_msat, + direction=RECEIVED, + status=PR_UNPAID, + min_final_cltv_delta=min_final_cltv_delta, + expiry_delay=LN_EXPIRY_NEVER, + ) + w2.save_payment_info(info) lnaddr1 = LnAddr( paymenthash=payment_hash, amount=amount_btc,