What changed, and why it matters
This change makes Electrum's Lightning onion-message code more resilient: when the wallet wants to publish a blinded payment path through one of its channels but lacks the remote peer's latest channel policy, it now skips that channel instead of crashing. The most likely user-visible effect before the fix was an unhandled exception that could prevent creating or advertising blinded paths, possibly breaking offers/receive functionality. There is no direct evidence in the commit that this was exploitable by an attacker to steal funds.
Treat as a stability/robustness fix. Users running Lightning with blinded paths/offers should upgrade to avoid failures when a peer's channel_update is temporarily unavailable. No emergency action is warranted based on the diff alone.
Security signals we found
Unhandled exception in path-advertisement code path
Missing input validation for remote channel policy
Crash/DoS potential in Lightning onion-message/blinded-path construction
No evidence of memory corruption, key leakage, or unauthorized spending
Evidence from the diff
In electrum/onion_message.py, _get_payinfo_for_blinded_path previously assumed get_mychannel_policy always returned a channel policy. If the remote channel_update was missing, cp was None and dereferencing cp.cltv_delta raised an AttributeError. The patch raises a new NoChannelPolicy exception, catches it in get_blinded_paths_to_me, logs a warning, and continues to the next candidate channel. This is a robustness fix rather than a cryptographic or protocol vulnerability fix.
Changed components
electrum/onion_message.pyLightning blinded path / offer receive pathget_blinded_paths_to_me_get_payinfo_for_blinded_pathInspect captured patch +8 / −2
diff --git a/electrum/onion_message.py b/electrum/onion_message.py
index 6f216d5..1bd7d29 100644
--- a/electrum/onion_message.py
+++ b/electrum/onion_message.py
@@ -33,7 +33,7 @@ from typing import TYPE_CHECKING, Optional, Sequence, NamedTuple, Tuple, Union
import electrum_ecc as ecc
from electrum.channel_db import get_mychannel_policy
-from electrum.lnrouter import PathEdge
+from electrum.lnrouter import PathEdge, NoChannelPolicy
from electrum.logging import get_logger, Logger
from electrum.crypto import sha256, get_ecdh
from electrum.lnmsg import OnionWireSerializer
@@ -422,7 +422,11 @@ def get_blinded_paths_to_me(
for chan in rchans[:max_paths]:
hop_extras = None
if not onion_message: # add hop_extras and payinfo, assumption: len(blinded_path) == 2 (us and peer)
- payinfo, hop_extras = _get_payinfo_for_blinded_path(chan, lnwallet)
+ try:
+ payinfo, hop_extras = _get_payinfo_for_blinded_path(chan, lnwallet)
+ except NoChannelPolicy:
+ logger.warning(f"missing remote channel_update for {chan.short_channel_id}")
+ continue
payinfos.append(payinfo)
blinded_path = create_blinded_path(
session_key=os.urandom(32),
@@ -447,6 +451,8 @@ def get_blinded_paths_to_me(
def _get_payinfo_for_blinded_path(chan: 'Channel', lnwallet: 'LNWallet'):
cp = get_mychannel_policy(chan.short_channel_id, chan.node_id, {chan.short_channel_id: chan})
+ if not cp:
+ raise NoChannelPolicy(chan.short_channel_id)
sum_cltv_expiry_delta = cp.cltv_delta
sum_fee_base_msat = cp.fee_base_msat
sum_fee_proportional_millionths = cp.fee_proportional_millionths
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.