lnpeer: simplify channel_type, as it is now "assumed"
What changed, and why it matters
This commit tightens how Electrum's Lightning code handles channel setup messages. Previously, the code tolerated missing optional fields (channel_type) in some situations; now it requires them to be present and rejects the connection if they are missing. This is a defensive hardening change that reduces the chance of a peer tricking the wallet into opening a channel with unexpected or unsafe settings.
Review is sufficient; this is a hardening patch. Ensure test coverage exists for missing channel_type TLVs in both open_channel and accept_channel messages, and consider whether any backward compatibility with older peers not supporting option_channel_type is intentionally being dropped.
Security signals we found
Mandatory field enforcement for protocol TLVs
Removal of optional-handling branches that could allow downgrade or ambiguity
Exception-based rejection of malformed Lightning channel messages
Defensive consistency check between sent and received channel_type
Evidence from the diff
In electrum/lnpeer.py, the accept_channel and open_channel handlers are modified to treat the channel_type TLV as mandatory. Previously, accept_channel_tlvs and channel_type within it were optional; the new code raises an Exception if either is missing. Similarly, open_channel_tlvs and its channel_type are now required. The equality check for channel_type in accept_channel is also simplified by removing the guard that only compared when channel_type was sent in open_channel, because it is now always assumed to be present. This aligns behavior with BOLT requirements once option_channel_type has been negotiated.
Changed components
electrum/lnpeer.pyLightning channel opening handshake (open_channel / accept_channel)ChannelType TLV parsing and validationInspect captured patch +14 / −11
diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py
index e8f8d73..f295e5e 100644
--- a/electrum/lnpeer.py
+++ b/electrum/lnpeer.py
@@ -1088,13 +1088,16 @@ class Peer(Logger, EventListener):
payload, 'accept')
accept_channel_tlvs = payload.get('accept_channel_tlvs')
- their_channel_type = accept_channel_tlvs.get('channel_type') if accept_channel_tlvs else None
- if their_channel_type:
- their_channel_type = ChannelType.from_bytes(their_channel_type['type'], byteorder='big').discard_unknown_and_check()
- # if channel_type is set, and channel_type was set in open_channel,
- # and they are not equal types: MUST reject the channel.
- if open_channel_tlvs.get('channel_type') is not None and their_channel_type != our_channel_type:
- raise Exception("Channel type is not the one that we sent.")
+ if accept_channel_tlvs is None:
+ raise Exception("accept_channel_tlvs MUST be present in accept_channel, but missing")
+ their_channel_type = accept_channel_tlvs.get('channel_type')
+ if their_channel_type is None:
+ raise Exception("channel_type MUST be present in accept_channel, but missing")
+ their_channel_type = ChannelType.from_bytes(their_channel_type['type'], byteorder='big').discard_unknown_and_check()
+ # if channel_type is set, and channel_type was set in open_channel,
+ # and they are not equal types: MUST reject the channel.
+ if their_channel_type != our_channel_type:
+ raise Exception("channel_type is not the one that we sent.")
remote_config = RemoteConfig(
payment_basepoint=OnlyPubkeyKeypair(payload['payment_basepoint']),
@@ -1237,11 +1240,11 @@ class Peer(Logger, EventListener):
raise Exception('wrong chain_hash')
open_channel_tlvs = payload.get('open_channel_tlvs')
- channel_type = open_channel_tlvs.get('channel_type') if open_channel_tlvs else None
- # The receiving node MAY fail the channel if:
- # option_channel_type was negotiated but the message doesn't include a channel_type
+ if open_channel_tlvs is None:
+ raise Exception("open_channel_tlvs MUST be present in open_channel, but missing")
+ channel_type = open_channel_tlvs.get('channel_type')
if channel_type is None:
- raise Exception("sender has advertised option_channel_type, but hasn't sent the channel type")
+ raise Exception("channel_type MUST be present in open_channel, but missing")
# MUST fail the channel if it supports channel_type,
# channel_type was set, and the type is not suitable.
else:
Why this scored 26/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.