Drop `ChannelHandshakeLimits::max_funding_satoshis`
What changed, and why it matters
This commit removes a default cap on how large an incoming Lightning channel can be. Previously, LDK advertised support for large ('Wumbo') channels but would still reject them unless the user manually raised a hidden limit. The change makes the advertised behavior real: large inbound channels are now accepted by default. It is a behavior change, not a buffer-overflow or key-theft bug, but it could surprise users who relied on the old implicit cap to limit their financial exposure.
Review whether any deployed LDK nodes depended on the old default `max_funding_satoshis` limit for risk control. If so, replace it with explicit risk controls such as the manual `Event::OpenChannelRequest` acceptance flow or per-HTLC interception, as suggested in the commit message. Verify that serialization compatibility is acceptable for your deployment (the field is removed from `ChannelHandshakeLimits`).
Security signals we found
Behavior mismatch between advertised protocol feature (`option_supports_large_channels` / Wumbo) and enforced inbound limit
Removal of a default financial-exposure limit that users may have relied on implicitly
Serialization format change for `ChannelHandshakeLimits` (field removed)
Fuzz corpus seeds updated because serialized config bytes changed
Evidence from the diff
The patch drops ChannelHandshakeLimits::max_funding_satoshis and the inbound-channel check that rejected channel_value_satoshis above that limit. It also removes the serialization field, default value MAX_FUNDING_SATOSHIS_NO_WUMBO, and related tests. The commit message explicitly states the old implementation was inconsistent: LDK already announced option_supports_large_channels via ChannelManager defaults regardless of the configured limit, so the limit caused advertised Wumbo support to be silently denied in practice.
Changed components
lightning/src/util/config.rslightning/src/ln/channel.rslightning/src/ln/channel_open_tests.rsfuzz/src/full_stack.rsInspect captured patch +5 / −46
diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 39588bc..f7f912c 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -1170,7 +1170,7 @@ fn two_peer_forwarding_seed() -> Vec<u8> {
// our network key
ext_from_hex("0100000000000000000000000000000000000000000000000000000000000000", &mut test);
// config
- ext_from_hex("000000000090000000000000000064000100000000000100ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test);
+ ext_from_hex("000000000090000000000000000064000100000000000100ffff00000000000000ffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test);
// new outbound connection with id 0
ext_from_hex("00", &mut test);
@@ -1624,7 +1624,7 @@ fn gossip_exchange_seed() -> Vec<u8> {
// our network key
ext_from_hex("0100000000000000000000000000000000000000000000000000000000000000", &mut test);
// config
- ext_from_hex("000000000090000000000000000064000100000000000100ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test);
+ ext_from_hex("000000000090000000000000000064000100000000000100ffff00000000000000ffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test);
// new outbound connection with id 0
ext_from_hex("00", &mut test);
@@ -1706,7 +1706,7 @@ fn splice_seed() -> Vec<u8> {
// our network key
ext_from_hex("0100000000000000000000000000000000000000000000000000000000000000", &mut test);
// config
- ext_from_hex("000000000090000000000000000064000100000000000100ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test);
+ ext_from_hex("000000000090000000000000000064000100000000000100ffff00000000000000ffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test);
// new outbound connection with id 0
ext_from_hex("00", &mut test);
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 3236ebd..7e6ee7f 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -3563,13 +3563,6 @@ impl<SP: SignerProvider> ChannelContext<SP> {
return Err(ChannelError::close(format!("Configured with an unreasonable our_to_self_delay ({}) putting user funds at risks. It must be greater than {}", config.channel_handshake_config.our_to_self_delay, BREAKDOWN_TIMEOUT)));
}
- // Check sanity of message fields:
- if channel_value_satoshis > config.channel_handshake_limits.max_funding_satoshis {
- return Err(ChannelError::close(format!(
- "Per our config, funding must be at most {}. It was {}. Peer contribution: {}. Our contribution: {}",
- config.channel_handshake_limits.max_funding_satoshis, channel_value_satoshis,
- open_channel_fields.funding_satoshis, our_funding_satoshis)));
- }
if channel_value_satoshis >= TOTAL_BITCOIN_SUPPLY_SATOSHIS {
return Err(ChannelError::close(format!("Funding must be smaller than the total bitcoin supply. It was {}", channel_value_satoshis)));
}
@@ -16046,10 +16039,7 @@ mod tests {
AwaitingChannelReadyFlags, ChannelState, FundedChannel, HTLCCandidate, HTLCInitiator,
HTLCUpdateAwaitingACK, InboundHTLCOutput, InboundHTLCState, InboundUpdateAdd,
InboundV1Channel, OutboundHTLCOutput, OutboundHTLCState, OutboundV1Channel,
- };
- use crate::ln::channel::{
- MAX_FUNDING_SATOSHIS_NO_WUMBO, MIN_THEIR_CHAN_RESERVE_SATOSHIS,
- TOTAL_BITCOIN_SUPPLY_SATOSHIS,
+ MIN_THEIR_CHAN_RESERVE_SATOSHIS,
};
use crate::ln::channel_keys::{RevocationBasepoint, RevocationKey};
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
@@ -16106,15 +16096,6 @@ mod tests {
assert!(ChannelState::ChannelReady(ChannelReadyFlags::new()) < ChannelState::ShutdownComplete);
}
- #[test]
- fn test_max_funding_satoshis_no_wumbo() {
- assert_eq!(TOTAL_BITCOIN_SUPPLY_SATOSHIS, 21_000_000 * 100_000_000);
- assert!(
- MAX_FUNDING_SATOSHIS_NO_WUMBO <= TOTAL_BITCOIN_SUPPLY_SATOSHIS,
- "MAX_FUNDING_SATOSHIS_NO_WUMBO is greater than all satoshis in existence"
- );
- }
-
#[cfg(ldk_test_vectors)]
struct Keys {
signer: crate::sign::InMemorySigner,
diff --git a/lightning/src/ln/channel_open_tests.rs b/lightning/src/ln/channel_open_tests.rs
index 0596393..08cabc0 100644
--- a/lightning/src/ln/channel_open_tests.rs
+++ b/lightning/src/ln/channel_open_tests.rs
@@ -457,8 +457,7 @@ fn test_channel_resumption_fail_post_funding() {
pub fn test_insane_channel_opens() {
// Stand up a network of 2 nodes
use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
- let mut legacy_cfg = test_legacy_channel_config();
- legacy_cfg.channel_handshake_limits.max_funding_satoshis = TOTAL_BITCOIN_SUPPLY_SATOSHIS + 1;
+ let legacy_cfg = test_legacy_channel_config();
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(legacy_cfg.clone())]);
@@ -524,19 +523,6 @@ pub fn test_insane_channel_opens() {
use crate::ln::channelmanager::MAX_LOCAL_BREAKDOWN_TIMEOUT;
- // Test all mutations that would make the channel open message insane
- insane_open_helper(
- format!(
- "Per our config, funding must be at most {}. It was {}",
- TOTAL_BITCOIN_SUPPLY_SATOSHIS + 1,
- TOTAL_BITCOIN_SUPPLY_SATOSHIS + 2
- )
- .as_str(),
- |mut msg| {
- msg.common_fields.funding_satoshis = TOTAL_BITCOIN_SUPPLY_SATOSHIS + 2;
- msg
- },
- );
insane_open_helper(
format!(
"Funding must be smaller than the total bitcoin supply. It was {}",
diff --git a/lightning/src/util/config.rs b/lightning/src/util/config.rs
index 420fad6..dd55d5c 100644
--- a/lightning/src/util/config.rs
+++ b/lightning/src/util/config.rs
@@ -10,7 +10,6 @@
//! Various user-configurable channel limits and settings which ChannelManager
//! applies for you.
-use crate::ln::channel::MAX_FUNDING_SATOSHIS_NO_WUMBO;
use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT};
#[cfg(fuzzing)]
@@ -300,11 +299,6 @@ pub struct ChannelHandshakeLimits {
/// Default value: `1000`
/// (Minimum of [`ChannelHandshakeConfig::their_channel_reserve_proportional_millionths`])
pub min_funding_satoshis: u64,
- /// Maximum allowed satoshis when a channel is funded. This is supplied by the sender and so
- /// only applies to inbound channels.
- ///
- /// Default value: `2^24 - 1`
- pub max_funding_satoshis: u64,
/// The remote node sets a limit on the minimum size of HTLCs we can send to them. This allows
/// you to limit the maximum minimum-size they can require.
///
@@ -374,7 +368,6 @@ impl Default for ChannelHandshakeLimits {
fn default() -> Self {
ChannelHandshakeLimits {
min_funding_satoshis: 1000,
- max_funding_satoshis: MAX_FUNDING_SATOSHIS_NO_WUMBO,
max_htlc_minimum_msat: u64::MAX,
min_max_htlc_value_in_flight_msat: 0,
max_channel_reserve_satoshis: u64::MAX,
@@ -395,7 +388,6 @@ impl Readable for ChannelHandshakeLimits {
fn read<R: crate::io::Read>(reader: &mut R) -> Result<Self, crate::ln::msgs::DecodeError> {
Ok(Self {
min_funding_satoshis: Readable::read(reader)?,
- max_funding_satoshis: Readable::read(reader)?,
max_htlc_minimum_msat: Readable::read(reader)?,
min_max_htlc_value_in_flight_msat: Readable::read(reader)?,
max_channel_reserve_satoshis: Readable::read(reader)?,
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.