From d62eb7ab13ce252605cb9dc2be566bb2f18b7317 Mon Sep 17 00:00:00 2001 From: f321x Date: Fri, 28 Feb 2025 11:35:49 +0100 Subject: [PATCH] disable mpp flags in invoice creation if jit channel is required, check against available liquidity if we need a jit channel --- electrum/lnpeer.py | 2 +- electrum/lnworker.py | 35 +++++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index afe96b5cb..1f5bc1415 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -1870,7 +1870,7 @@ class Peer(Logger, EventListener): next_chan = self.lnworker.get_channel_by_short_id(next_chan_scid) if self.lnworker.features.supports(LnFeatures.OPTION_ZEROCONF_OPT): - next_peer = self.lnworker.get_peer_by_scid_alias(next_chan_scid) + next_peer = self.lnworker.get_peer_by_static_jit_scid_alias(next_chan_scid) else: next_peer = None diff --git a/electrum/lnworker.py b/electrum/lnworker.py index 952f1639b..36b915442 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -1152,7 +1152,7 @@ class LNWallet(LNWorker): self.logger.info('REBROADCASTING CLOSING TX') await self.network.try_broadcasting(force_close_tx, 'force-close') - def get_peer_by_scid_alias(self, scid_alias: bytes) -> Optional[Peer]: + def get_peer_by_static_jit_scid_alias(self, scid_alias: bytes) -> Optional[Peer]: for nodeid, peer in self.peers.items(): if scid_alias == self._scid_alias_of_node(nodeid): return peer @@ -1161,7 +1161,7 @@ class LNWallet(LNWorker): # scid alias for just-in-time channels return sha256(b'Electrum' + nodeid)[0:8] - def get_scid_alias(self) -> bytes: + def get_static_jit_scid_alias(self) -> bytes: return self._scid_alias_of_node(self.node_keypair.pubkey) @log_exceptions @@ -2112,11 +2112,15 @@ class LNWallet(LNWorker): assert amount_msat is None or amount_msat > 0 timestamp = int(time.time()) - routing_hints = self.calc_routing_hints_for_invoice(amount_msat, channels=channels) - self.logger.info(f"creating bolt11 invoice with routing_hints: {routing_hints}") + needs_jit: bool = self.receive_requires_jit_channel(amount_msat) + routing_hints = self.calc_routing_hints_for_invoice(amount_msat, channels=channels, needs_jit=needs_jit) + self.logger.info(f"creating bolt11 invoice with routing_hints: {routing_hints}, jit: {needs_jit}") invoice_features = self.features.for_invoice() if not self.uses_trampoline(): invoice_features &= ~ LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM + 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) amount_btc = amount_msat/Decimal(COIN*1000) if amount_msat else None if expiry == 0: @@ -2505,14 +2509,14 @@ class LNWallet(LNWorker): else: self.logger.info(f"waiting for other htlcs to fail (phash={payment_hash.hex()})") - def calc_routing_hints_for_invoice(self, amount_msat: Optional[int], channels=None): + def calc_routing_hints_for_invoice(self, amount_msat: Optional[int], channels=None, needs_jit=False): """calculate routing hints (BOLT-11 'r' field)""" routing_hints = [] - if self.config.ZEROCONF_TRUSTED_NODE: + if needs_jit: node_id, rest = extract_nodeid(self.config.ZEROCONF_TRUSTED_NODE) - alias_or_scid = self.get_scid_alias() + alias_or_scid = self.get_static_jit_scid_alias() routing_hints.append(('r', [(node_id, alias_or_scid, 0, 0, 144)])) - # no need for more + # no need for more because we cannot receive enough through the others and mpp is disabled for jit channels = [] else: if channels is None: @@ -2665,6 +2669,21 @@ class LNWallet(LNWorker): can_receive_msat = max(recv_chan_msats) return Decimal(can_receive_msat) / 1000 + def receive_requires_jit_channel(self, amount_msat: Optional[int]) -> bool: + """Returns true if we cannot receive the amount and have set up a trusted LSP node. + Cannot work reliably with 0 amount invoices as we don't know if we are able to receive it. + """ + # a trusted zeroconf node is configured + if (self.config.ZEROCONF_TRUSTED_NODE + # the zeroconf node is a peer, it doesn't make sense to request a channel from an offline LSP + and extract_nodeid(self.config.ZEROCONF_TRUSTED_NODE)[0] in self.peers + # we cannot receive the amount specified + and ((amount_msat and self.num_sats_can_receive() < (amount_msat // 1000)) + # or we cannot receive anything, and it's a 0 amount invoice + or (not amount_msat and self.num_sats_can_receive() < 1))): + return True + return False + def _suggest_channels_for_rebalance(self, direction, amount_sat) -> Sequence[Tuple[Channel, int]]: """ Suggest a channel and amount to send/receive with that channel, so that we will be able to receive/send amount_sat