LNWallet: set trampoline invoice feature independently
What changed, and why it matters
This commit changes how Electrum decides whether to advertise 'trampoline routing' support on Lightning invoices and stops automatically freezing non-trampoline channels for receiving when trampoline mode is on. Previously, enabling trampoline could silently block payments through non-trampoline channels; now the wallet only advertises trampoline if every usable channel supports it, and users can receive through non-trampoline channels again. The change is more of a bug fix / behavior correction than a critical security patch, but it could affect payment routing and user expectations.
Review whether the new all-channels-must-be-trampoline condition correctly handles edge cases such as closed, force-closing, or offline channels, and verify that invoice feature bits accurately reflect the wallet's current routing capabilities. No urgent security patch appears required based solely on this diff.
Security signals we found
Behavior change in Lightning invoice feature signaling
Removal of automatic channel freezing based on trampoline configuration
Potential for invoice feature bits to no longer match actual routing capabilities if logic is incomplete
Evidence from the diff
The patch removes the automatic freeze logic in Channel.is_frozen_for_receiving() that returned True whenever trampoline was enabled and the peer was not a trampoline peer. It also changes LNWallet._get_invoice_features() so that the OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM feature bit is cleared unless all open, receiving-enabled channels are with trampoline peers. This decouples trampoline configuration from invoice signaling and receiving-channel freezing.
Changed components
electrum/lnchannel.pyelectrum/lnworker.pyLightning Network invoice generationChannel receiving freeze logicInspect captured patch +2 / −3
diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py
index 06f2a94..bf9b807 100644
--- a/electrum/lnchannel.py
+++ b/electrum/lnchannel.py
@@ -1101,8 +1101,6 @@ class Channel(AbstractChannel):
util.trigger_callback('channel', self.lnworker.wallet, self)
def is_frozen_for_receiving(self) -> bool:
- if self.lnworker.uses_trampoline() and not self.lnworker.is_trampoline_peer(self.node_id):
- return True
return self.storage.get('frozen_for_receiving', False)
def set_frozen_for_receiving(self, b: bool) -> None:
diff --git a/electrum/lnworker.py b/electrum/lnworker.py
index c647cf0..b532e5a 100644
--- a/electrum/lnworker.py
+++ b/electrum/lnworker.py
@@ -2545,7 +2545,8 @@ class LNWallet(Logger):
def _get_invoice_features(self, amount_msat: Optional[int]) -> LnFeatures:
invoice_features = self.features.for_invoice()
- if not self.uses_trampoline():
+ if not all((not c.is_open() or c.is_frozen_for_receiving()) or self.is_trampoline_peer(c.node_id) \
+ for c in self.channels.values()):
invoice_features &= ~ LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM
needs_jit: bool = self.receive_requires_jit_channel(amount_msat)
if needs_jit:
Why this scored 35/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.