lnpeer: send channel update also for private channels, if we are forwarding
What changed, and why it matters
This change adjusts how Electrum's Lightning peer shares routing information. Previously, the wallet only sent a 'channel update' (the data peers need to route payments through a channel) for public channels. Now it also sends that update for private channels when the user has enabled payment forwarding. This is a bug fix for routing functionality, not a security vulnerability, but it touches sensitive Lightning gossip logic.
Review as a normal functional bug fix. No immediate security response required. Verify that private channel updates are only sent to the direct peer and not broadcast, and that the experimental forwarding flag correctly gates the behavior.
Security signals we found
Lightning gossip message handling change
Private channel routing information now transmitted
Experimental forwarding feature gate used
No input validation or cryptographic changes
Evidence from the diff
The commit refactors lnpeer.py so that channel_update messages are sent for private channels too, provided EXPERIMENTAL_LN_FORWARD_PAYMENTS is enabled and the channel has a short_channel_id. The method maybe_send_channel_update is renamed to send_channel_update and is called both after sending a channel_announcement and after channel opening. The change removes the old public-only guard and the periodic sending of channel_update inside the gossip loop, centralizing it on announcement and opening events.
Changed components
electrum/lnpeer.pyLightning channel announcement flowLightning channel update gossipInspect captured patch +6 / −6
diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py
index 6e62455..4202f05 100644
--- a/electrum/lnpeer.py
+++ b/electrum/lnpeer.py
@@ -598,7 +598,6 @@ class Peer(Logger, EventListener):
for chan in public_channels:
if chan.is_open() and chan.peer_state == PeerState.GOOD:
self.maybe_send_channel_announcement(chan)
- self.maybe_send_channel_update(chan)
await asyncio.sleep(600)
def _should_forward_gossip(self) -> bool:
@@ -1816,8 +1815,10 @@ class Peer(Logger, EventListener):
payload['bitcoin_signature_2'] = bitcoin_sigs[1]
raw_msg = encode_msg(message_type, **payload)
self.transport.send_bytes(raw_msg)
+ # also send channel update
+ self.send_channel_update(chan)
- def maybe_send_channel_update(self, chan: Channel):
+ def send_channel_update(self, chan: Channel):
chan_upd = chan.get_outgoing_gossip_channel_update()
self.transport.send_bytes(chan_upd)
@@ -1845,12 +1846,11 @@ class Peer(Logger, EventListener):
if pending_channel_update:
chan.set_remote_update(pending_channel_update)
self.logger.info(f"CHANNEL OPENING COMPLETED ({chan.get_id_for_log()})")
- if chan.is_public():
+ forwarding_enabled = self.network.config.EXPERIMENTAL_LN_FORWARD_PAYMENTS
+ if forwarding_enabled and chan.short_channel_id:
# send channel_update of outgoing edge to peer,
# so that channel can be used to receive payments
- # Note: this is only useful for our unit tests. peers may discard
- # channel updates if the channel has not been announced
- self.maybe_send_channel_update(chan)
+ self.send_channel_update(chan)
def maybe_send_announcement_signatures(self, chan: Channel, is_reply=False):
if not chan.is_public() or chan.short_channel_id is None:
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.