From 3644da07cb1505d87b7c23557c02da32da74bb62 Mon Sep 17 00:00:00 2001 From: Leo Nash Date: Wed, 1 Apr 2026 01:01:58 +0000 Subject: [PATCH] Add `unannounced_channel_max_inbound_htlc_value_in_flight_percentage` Users can now configure two different max percentages for the channel value that can be allocated to inbound HTLCs, one for announced channels, and another for unannounced channels. --- fuzz/src/full_stack.rs | 6 +- lightning/src/ln/async_payments_tests.rs | 3 + lightning/src/ln/channel.rs | 104 +++++++++++++++----- lightning/src/ln/channel_open_tests.rs | 6 +- lightning/src/ln/functional_tests.rs | 14 +-- lightning/src/ln/htlc_reserve_unit_tests.rs | 21 ++-- lightning/src/ln/payment_tests.rs | 34 ++++--- lightning/src/ln/splicing_tests.rs | 24 +++-- lightning/src/ln/update_fee_tests.rs | 7 +- lightning/src/util/config.rs | 64 +++++++++--- 10 files changed, 205 insertions(+), 78 deletions(-) diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index c1d7982e5e4..0adedfd3293 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -1196,7 +1196,7 @@ fn two_peer_forwarding_seed() -> Vec { // our network key ext_from_hex("0100000000000000000000000000000000000000000000000000000000000000", &mut test); // config - ext_from_hex("000000000090000000000000000064000100000000000100ffff00000000000000ffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test); + ext_from_hex("00000000009000000000000000006464000100000000000100ffff00000000000000ffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test); // new outbound connection with id 0 ext_from_hex("00", &mut test); @@ -1650,7 +1650,7 @@ fn gossip_exchange_seed() -> Vec { // our network key ext_from_hex("0100000000000000000000000000000000000000000000000000000000000000", &mut test); // config - ext_from_hex("000000000090000000000000000064000100000000000100ffff00000000000000ffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test); + ext_from_hex("00000000009000000000000000006464000100000000000100ffff00000000000000ffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test); // new outbound connection with id 0 ext_from_hex("00", &mut test); @@ -1732,7 +1732,7 @@ fn splice_seed() -> Vec { // our network key ext_from_hex("0100000000000000000000000000000000000000000000000000000000000000", &mut test); // config - ext_from_hex("000000000090000000000000000064000100000000000100ffff00000000000000ffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test); + ext_from_hex("00000000009000000000000000006464000100000000000100ffff00000000000000ffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000000000", &mut test); // new outbound connection with id 0 ext_from_hex("00", &mut test); diff --git a/lightning/src/ln/async_payments_tests.rs b/lightning/src/ln/async_payments_tests.rs index 25522346d9c..531d3b76d28 100644 --- a/lightning/src/ln/async_payments_tests.rs +++ b/lightning/src/ln/async_payments_tests.rs @@ -504,6 +504,9 @@ fn often_offline_node_cfg() -> UserConfig { cfg.channel_handshake_config.announce_for_forwarding = false; cfg.channel_handshake_limits.force_announced_channel_preference = true; cfg.hold_outbound_htlcs_at_next_hop = true; + // Use the setting that matches the default at the time these tests were written + cfg.channel_handshake_config.unannounced_channel_max_inbound_htlc_value_in_flight_percentage = + 10; cfg } diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 03f78dc82b4..5579c119e05 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -4102,6 +4102,7 @@ impl ChannelContext { ), holder_max_htlc_value_in_flight_msat: get_holder_max_htlc_value_in_flight_msat( channel_value_satoshis, + announce_for_forwarding, &config.channel_handshake_config, ), counterparty_htlc_minimum_msat: open_channel_fields.htlc_minimum_msat, @@ -4405,6 +4406,7 @@ impl ChannelContext { // receive `accept_channel2`. holder_max_htlc_value_in_flight_msat: get_holder_max_htlc_value_in_flight_msat( channel_value_satoshis, + config.channel_handshake_config.announce_for_forwarding, &config.channel_handshake_config, ), counterparty_htlc_minimum_msat: 0, @@ -6678,24 +6680,41 @@ impl ChannelContext { /// Returns the value to use for `holder_max_htlc_value_in_flight_msat` as a percentage of the /// `channel_value_satoshis` in msat, set through -/// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`] +/// [`ChannelHandshakeConfig::announced_channel_max_inbound_htlc_value_in_flight_percentage`] +/// or [`ChannelHandshakeConfig::unannounced_channel_max_inbound_htlc_value_in_flight_percentage`] +/// depending on the value of [`ChannelHandshakeConfig::announce_for_forwarding`]. /// /// The effective percentage is lower bounded by 1% and upper bounded by 100%. /// -/// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`]: crate::util::config::ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel +/// [`ChannelHandshakeConfig::announced_channel_max_inbound_htlc_value_in_flight_percentage`]: crate::util::config::ChannelHandshakeConfig::announced_channel_max_inbound_htlc_value_in_flight_percentage +/// [`ChannelHandshakeConfig::unannounced_channel_max_inbound_htlc_value_in_flight_percentage`]: crate::util::config::ChannelHandshakeConfig::unannounced_channel_max_inbound_htlc_value_in_flight_percentage +/// [`ChannelHandshakeConfig::announce_for_forwarding`]: crate::util::config::ChannelHandshakeConfig::announce_for_forwarding fn get_holder_max_htlc_value_in_flight_msat( - channel_value_satoshis: u64, config: &ChannelHandshakeConfig, + channel_value_satoshis: u64, is_announced_channel: bool, config: &ChannelHandshakeConfig, ) -> u64 { - let configured_percent = if config.max_inbound_htlc_value_in_flight_percent_of_channel < 1 { + let config_setting = if is_announced_channel { + config.announced_channel_max_inbound_htlc_value_in_flight_percentage + } else { + config.unannounced_channel_max_inbound_htlc_value_in_flight_percentage + }; + let configured_percent = if config_setting < 1 { 1 - } else if config.max_inbound_htlc_value_in_flight_percent_of_channel > 100 { + } else if config_setting > 100 { 100 } else { - config.max_inbound_htlc_value_in_flight_percent_of_channel as u64 + config_setting as u64 }; channel_value_satoshis * 10 * configured_percent } +/// This is for legacy reasons, present for forward-compatibility. +/// LDK versions older than 0.0.104 don't know how read/handle values other than the legacy +/// percentage from storage. Hence, we use this function to not persist legacy values of +/// `holder_max_htlc_value_in_flight_msat` for channels into storage. +fn get_legacy_default_holder_max_htlc_value_in_flight_msat(channel_value_satoshis: u64) -> u64 { + channel_value_satoshis * 10 * MAX_IN_FLIGHT_PERCENT_LEGACY as u64 +} + /// Returns a minimum channel reserve value the remote needs to maintain, /// required by us according to the configured or default /// [`ChannelHandshakeConfig::their_channel_reserve_proportional_millionths`] @@ -15691,15 +15710,11 @@ impl Writeable for FundedChannel { None }; - let mut old_max_in_flight_percent_config = UserConfig::default().channel_handshake_config; - old_max_in_flight_percent_config.max_inbound_htlc_value_in_flight_percent_of_channel = - MAX_IN_FLIGHT_PERCENT_LEGACY; - let max_in_flight_msat = get_holder_max_htlc_value_in_flight_msat( + let legacy_max_in_flight_msat = get_legacy_default_holder_max_htlc_value_in_flight_msat( self.funding.get_value_satoshis(), - &old_max_in_flight_percent_config, ); let serialized_holder_htlc_max_in_flight = - if self.context.holder_max_htlc_value_in_flight_msat != max_in_flight_msat { + if self.context.holder_max_htlc_value_in_flight_msat != legacy_max_in_flight_msat { Some(self.context.holder_max_htlc_value_in_flight_msat) } else { None @@ -16131,11 +16146,9 @@ impl<'a, 'b, 'c, ES: EntropySource, SP: SignerProvider> let mut holder_selected_channel_reserve_satoshis = Some( get_legacy_default_holder_selected_channel_reserve_satoshis(channel_value_satoshis), ); + let mut holder_max_htlc_value_in_flight_msat = - Some(get_holder_max_htlc_value_in_flight_msat( - channel_value_satoshis, - &UserConfig::default().channel_handshake_config, - )); + Some(get_legacy_default_holder_max_htlc_value_in_flight_msat(channel_value_satoshis)); // Prior to supporting channel type negotiation, all of our channels were static_remotekey // only, so we default to that if none was written. let mut channel_type = Some(ChannelTypeFeatures::only_static_remote_key()); @@ -17141,8 +17154,13 @@ mod tests { } #[test] - #[rustfmt::skip] fn test_configured_holder_max_htlc_value_in_flight() { + do_test_configured_holder_max_htlc_value_in_flight(true); + do_test_configured_holder_max_htlc_value_in_flight(false); + } + + #[rustfmt::skip] + fn do_test_configured_holder_max_htlc_value_in_flight(announce_channel: bool) { let test_est = TestFeeEstimator::new(15000); let feeest = LowerBoundedFeeEstimator::new(&test_est); let logger = TestLogger::new(); @@ -17154,13 +17172,49 @@ mod tests { let inbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); let mut config_2_percent = UserConfig::default(); - config_2_percent.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 2; + config_2_percent.channel_handshake_config.announce_for_forwarding = announce_channel; + if announce_channel { + config_2_percent + .channel_handshake_config + .announced_channel_max_inbound_htlc_value_in_flight_percentage = 2; + } else { + config_2_percent + .channel_handshake_config + .unannounced_channel_max_inbound_htlc_value_in_flight_percentage = 2; + } let mut config_99_percent = UserConfig::default(); - config_99_percent.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 99; + config_99_percent.channel_handshake_config.announce_for_forwarding = announce_channel; + if announce_channel { + config_99_percent + .channel_handshake_config + .announced_channel_max_inbound_htlc_value_in_flight_percentage = 99; + } else { + config_99_percent + .channel_handshake_config + .unannounced_channel_max_inbound_htlc_value_in_flight_percentage = 99; + } let mut config_0_percent = UserConfig::default(); - config_0_percent.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 0; + config_0_percent.channel_handshake_config.announce_for_forwarding = announce_channel; + if announce_channel { + config_0_percent + .channel_handshake_config + .announced_channel_max_inbound_htlc_value_in_flight_percentage = 0; + } else { + config_0_percent + .channel_handshake_config + .unannounced_channel_max_inbound_htlc_value_in_flight_percentage = 0; + } let mut config_101_percent = UserConfig::default(); - config_101_percent.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 101; + config_101_percent.channel_handshake_config.announce_for_forwarding = announce_channel; + if announce_channel { + config_101_percent + .channel_handshake_config + .announced_channel_max_inbound_htlc_value_in_flight_percentage = 101; + } else { + config_101_percent + .channel_handshake_config + .unannounced_channel_max_inbound_htlc_value_in_flight_percentage = 101; + } // Test that `OutboundV1Channel::new` creates a channel with the correct value for // `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value, @@ -17189,26 +17243,26 @@ mod tests { assert_eq!(chan_4.context.holder_max_htlc_value_in_flight_msat, (chan_4_value_msat as f64 * 0.99) as u64); // Test that `OutboundV1Channel::new` uses the lower bound of the configurable percentage values (1%) - // if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a value less than 1. + // if `(un)announced_channel_max_inbound_htlc_value_in_flight_percentage` is set to a value less than 1. let chan_5 = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_0_percent), 10000000, 100000, 42, &config_0_percent, 0, 42, None, &logger, None).unwrap(); let chan_5_value_msat = chan_5.funding.get_value_satoshis() * 1000; assert_eq!(chan_5.context.holder_max_htlc_value_in_flight_msat, (chan_5_value_msat as f64 * 0.01) as u64); // Test that `OutboundV1Channel::new` uses the upper bound of the configurable percentage values - // (100%) if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a larger value + // (100%) if `(un)announced_channel_max_inbound_htlc_value_in_flight_percentage` is set to a larger value // than 100. let chan_6 = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_101_percent), 10000000, 100000, 42, &config_101_percent, 0, 42, None, &logger, None).unwrap(); let chan_6_value_msat = chan_6.funding.get_value_satoshis() * 1000; assert_eq!(chan_6.context.holder_max_htlc_value_in_flight_msat, chan_6_value_msat); // Test that `InboundV1Channel::new` uses the lower bound of the configurable percentage values (1%) - // if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a value less than 1. + // if `(un)announced_channel_max_inbound_htlc_value_in_flight_percentage` is set to a value less than 1. let chan_7 = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_0_percent), &channelmanager::provided_init_features(&config_0_percent), &chan_1_open_channel_msg, 7, &config_0_percent, 0, &&logger, None).unwrap(); let chan_7_value_msat = chan_7.funding.get_value_satoshis() * 1000; assert_eq!(chan_7.context.holder_max_htlc_value_in_flight_msat, (chan_7_value_msat as f64 * 0.01) as u64); // Test that `InboundV1Channel::new` uses the upper bound of the configurable percentage values - // (100%) if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a larger value + // (100%) if `(un)announced_channel_max_inbound_htlc_value_in_flight_percentage` is set to a larger value // than 100. let chan_8 = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_101_percent), &channelmanager::provided_init_features(&config_101_percent), &chan_1_open_channel_msg, 7, &config_101_percent, 0, &&logger, None).unwrap(); let chan_8_value_msat = chan_8.funding.get_value_satoshis() * 1000; diff --git a/lightning/src/ln/channel_open_tests.rs b/lightning/src/ln/channel_open_tests.rs index d28d157488d..ac4a1b67994 100644 --- a/lightning/src/ln/channel_open_tests.rs +++ b/lightning/src/ln/channel_open_tests.rs @@ -182,7 +182,8 @@ fn test_inbound_anchors_manual_acceptance() { fn test_inbound_anchors_config_overridden() { let overrides = ChannelConfigOverrides { handshake_overrides: Some(ChannelHandshakeConfigUpdate { - max_inbound_htlc_value_in_flight_percent_of_channel: Some(5), + announced_channel_max_inbound_htlc_value_in_flight_percentage: Some(5), + unannounced_channel_max_inbound_htlc_value_in_flight_percentage: None, htlc_minimum_msat: Some(1000), minimum_depth: Some(2), to_self_delay: Some(200), @@ -1070,7 +1071,8 @@ pub fn test_accept_inbound_channel_config_override() { let config_overrides = ChannelConfigOverrides { handshake_overrides: Some(ChannelHandshakeConfigUpdate { - max_inbound_htlc_value_in_flight_percent_of_channel: None, + announced_channel_max_inbound_htlc_value_in_flight_percentage: None, + unannounced_channel_max_inbound_htlc_value_in_flight_percentage: None, htlc_minimum_msat: None, minimum_depth: None, to_self_delay: None, diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 7ed46922d8a..98b249ab506 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -6903,22 +6903,22 @@ pub fn test_channel_update_has_correct_htlc_maximum_msat() { config_30_percent.channel_handshake_config.announce_for_forwarding = true; config_30_percent .channel_handshake_config - .max_inbound_htlc_value_in_flight_percent_of_channel = 30; + .announced_channel_max_inbound_htlc_value_in_flight_percentage = 30; let mut config_50_percent = UserConfig::default(); config_50_percent.channel_handshake_config.announce_for_forwarding = true; config_50_percent .channel_handshake_config - .max_inbound_htlc_value_in_flight_percent_of_channel = 50; + .announced_channel_max_inbound_htlc_value_in_flight_percentage = 50; let mut config_95_percent = UserConfig::default(); config_95_percent.channel_handshake_config.announce_for_forwarding = true; config_95_percent .channel_handshake_config - .max_inbound_htlc_value_in_flight_percent_of_channel = 95; + .announced_channel_max_inbound_htlc_value_in_flight_percentage = 95; let mut config_100_percent = UserConfig::default(); config_100_percent.channel_handshake_config.announce_for_forwarding = true; config_100_percent .channel_handshake_config - .max_inbound_htlc_value_in_flight_percent_of_channel = 100; + .announced_channel_max_inbound_htlc_value_in_flight_percentage = 100; let chanmon_cfgs = create_chanmon_cfgs(4); let node_cfgs = create_node_cfgs(4, &chanmon_cfgs); @@ -9067,7 +9067,8 @@ pub fn test_nondust_htlc_excess_fees_are_dust() { config.channel_handshake_limits.min_max_accepted_htlcs = chan_utils::max_htlcs(&chan_ty); config.channel_handshake_config.our_max_accepted_htlcs = chan_utils::max_htlcs(&chan_ty); config.channel_handshake_config.our_htlc_minimum_msat = 1; - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let node_chanmgrs = create_node_chanmgrs( 3, @@ -10039,7 +10040,8 @@ pub fn test_dust_exposure_holding_cell_assertion() { // Use a fixed dust exposure limit to make the test simpler const DUST_HTLC_VALUE_MSAT: u64 = 500_000; config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FixedLimitMsat(5_000_000); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let configs = [Some(config.clone()), Some(config.clone()), Some(config.clone())]; let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &configs); diff --git a/lightning/src/ln/htlc_reserve_unit_tests.rs b/lightning/src/ln/htlc_reserve_unit_tests.rs index 608ac143c8d..aaf81b87be7 100644 --- a/lightning/src/ln/htlc_reserve_unit_tests.rs +++ b/lightning/src/ln/htlc_reserve_unit_tests.rs @@ -2376,7 +2376,8 @@ fn test_create_channel_to_trusted_peer_0reserve() { fn do_test_create_channel_to_trusted_peer_0reserve(mut config: UserConfig) -> ChannelTypeFeatures { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); @@ -2465,7 +2466,8 @@ fn do_test_accept_inbound_channel_from_trusted_peer_0reserve( ) -> ChannelTypeFeatures { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); @@ -2679,7 +2681,8 @@ fn do_test_0reserve_no_outputs_legacy(no_outputs_case: LegacyChannelsNoOutputs) let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let channel_type = ChannelTypeFeatures::only_static_remote_key(); @@ -2979,7 +2982,8 @@ fn do_test_0reserve_no_outputs_keyed_anchors(payment_success: bool) { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let channel_type = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(); @@ -3112,7 +3116,8 @@ fn do_test_0reserve_no_outputs_p2a_anchor() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let channel_type = ChannelTypeFeatures::anchors_zero_fee_commitments(); @@ -3170,7 +3175,8 @@ fn do_test_0reserve_force_close_with_single_p2a_output(high_feerate: bool) { *feerate_lock = 2500; } let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let channel_type = ChannelTypeFeatures::anchors_zero_fee_commitments(); @@ -3276,7 +3282,8 @@ fn test_0reserve_zero_conf_combined() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index be52459a872..810fe3fbd97 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -212,7 +212,9 @@ fn mpp_retry_overpay() { let node_cfgs = create_node_cfgs(4, &chanmon_cfgs); let mut user_config = test_legacy_channel_config(); - user_config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + user_config + .channel_handshake_config + .announced_channel_max_inbound_htlc_value_in_flight_percentage = 100; let mut limited_1 = user_config.clone(); limited_1.channel_handshake_config.our_htlc_minimum_msat = 35_000_000; let mut limited_2 = user_config.clone(); @@ -1702,7 +1704,8 @@ fn preflight_probes_yield_event_skip_private_hop() { // We alleviate the HTLC max-in-flight limit, as otherwise we'd always be limited through that. let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let config = Some(config); let configs = [config.clone(), config.clone(), config.clone(), config.clone(), config]; @@ -1749,7 +1752,8 @@ fn preflight_probes_yield_event() { // We alleviate the HTLC max-in-flight limit, as otherwise we'd always be limited through that. let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let config = Some(config); let configs = [config.clone(), config.clone(), config.clone(), config]; @@ -1800,7 +1804,8 @@ fn preflight_probes_yield_event_and_skip() { // We alleviate the HTLC max-in-flight limit, as otherwise we'd always be limited through that. let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let config = Some(config); let configs = @@ -2429,11 +2434,12 @@ fn do_accept_underpaying_htlcs_config(num_mpp_parts: usize) { HTLCInterceptionFlags::ToInterceptSCIDs as u8; intercept_forwards_config .channel_handshake_config - .max_inbound_htlc_value_in_flight_percent_of_channel = max_in_flight_percent; + .announced_channel_max_inbound_htlc_value_in_flight_percentage = max_in_flight_percent; let mut underpay_config = test_default_channel_config(); underpay_config.channel_config.accept_underpaying_htlcs = true; - underpay_config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = - max_in_flight_percent; + underpay_config + .channel_handshake_config + .unannounced_channel_max_inbound_htlc_value_in_flight_percentage = max_in_flight_percent; let configs = [None, Some(intercept_forwards_config), Some(underpay_config)]; let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &configs); @@ -4788,7 +4794,8 @@ fn do_test_payment_metadata_consistency(do_reload: bool, do_modify: bool) { let chain_mon; let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 50; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 50; let configs = [None, Some(config.clone()), Some(config.clone()), Some(config.clone())]; let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &configs); let node_d_reload; @@ -5147,7 +5154,8 @@ fn test_non_strict_forwarding() { let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); let mut config = test_legacy_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let configs = [Some(config.clone()), Some(config.clone()), Some(config)]; let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &configs); @@ -5386,11 +5394,15 @@ fn max_out_mpp_path() { let mut user_cfg = test_default_channel_config(); user_cfg.channel_config.forwarding_fee_base_msat = 0; - user_cfg.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + user_cfg + .channel_handshake_config + .unannounced_channel_max_inbound_htlc_value_in_flight_percentage = 100; let mut lsp_cfg = test_default_channel_config(); lsp_cfg.channel_config.forwarding_fee_base_msat = 0; lsp_cfg.channel_config.forwarding_fee_proportional_millionths = 3000; - lsp_cfg.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + lsp_cfg + .channel_handshake_config + .unannounced_channel_max_inbound_htlc_value_in_flight_percentage = 100; let chanmon_cfgs = create_chanmon_cfgs(3); let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); diff --git a/lightning/src/ln/splicing_tests.rs b/lightning/src/ln/splicing_tests.rs index 5279f2dfcc0..e4c4c10dbbd 100644 --- a/lightning/src/ln/splicing_tests.rs +++ b/lightning/src/ln/splicing_tests.rs @@ -1122,7 +1122,8 @@ fn test_splice_in() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(config)]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); @@ -1172,7 +1173,8 @@ fn test_splice_out() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(config)]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); @@ -1215,7 +1217,8 @@ fn test_splice_in_and_out() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(config)]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); @@ -3546,7 +3549,8 @@ fn test_splice_balance_falls_below_reserve() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); @@ -4066,19 +4070,22 @@ fn test_funding_contributed_unfunded_channel() { #[test] fn test_splice_pending_htlcs() { let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = false; config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false; do_test_splice_pending_htlcs(config); let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false; do_test_splice_pending_htlcs(config); let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = false; config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = true; do_test_splice_pending_htlcs(config); @@ -6298,7 +6305,8 @@ fn test_splice_revalidation_at_quiescence() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let mut config = test_default_channel_config(); - config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + config.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); diff --git a/lightning/src/ln/update_fee_tests.rs b/lightning/src/ln/update_fee_tests.rs index fc80059bbd3..b1f8257088e 100644 --- a/lightning/src/ln/update_fee_tests.rs +++ b/lightning/src/ln/update_fee_tests.rs @@ -1031,7 +1031,8 @@ pub fn do_cannot_afford_on_holding_cell_release( let chanmon_cfgs = create_chanmon_cfgs(2); let mut cfg = test_legacy_channel_config(); - cfg.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + cfg.channel_handshake_config.announced_channel_max_inbound_htlc_value_in_flight_percentage = + 100; if channel_type_features.supports_anchors_zero_fee_htlc_tx() { cfg.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; } @@ -1225,7 +1226,9 @@ pub fn do_can_afford_given_trimmed_htlcs(inequality_regions: core::cmp::Ordering let chanmon_cfgs = create_chanmon_cfgs(2); let mut legacy_cfg = test_legacy_channel_config(); - legacy_cfg.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100; + legacy_cfg + .channel_handshake_config + .announced_channel_max_inbound_htlc_value_in_flight_percentage = 100; let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let node_chanmgrs = diff --git a/lightning/src/util/config.rs b/lightning/src/util/config.rs index 14c507184ac..772830c8a65 100644 --- a/lightning/src/util/config.rs +++ b/lightning/src/util/config.rs @@ -62,17 +62,13 @@ pub struct ChannelHandshakeConfig { /// Default value: `1` (If the value is less than `1`, it is ignored and set to `1`, as is /// required by the protocol. pub our_htlc_minimum_msat: u64, - /// Sets the percentage of the channel value we will cap the total value of outstanding inbound - /// HTLCs to. + /// Sets the maximum percentage of the total channel value that can be allocated to inbound + /// HTLCs in announced channels. /// /// This can be set to a value between 1-100, where the value corresponds to the percent of the /// channel value in whole percentages. /// /// Note that: - /// * If configured to another value than the default value `10`, any new channels created with - /// the non default value will cause versions of LDK prior to 0.0.104 to refuse to read the - /// `ChannelManager`. - /// /// * This caps the total value for inbound HTLCs in-flight only, and there's currently /// no way to configure the cap for the total value of outbound HTLCs in-flight. /// @@ -87,7 +83,29 @@ pub struct ChannelHandshakeConfig { /// Minimum value: `1` (Any values less will be treated as `1` instead.) /// /// Maximum value: `100` (Any values larger will be treated as `100` instead.) - pub max_inbound_htlc_value_in_flight_percent_of_channel: u8, + pub announced_channel_max_inbound_htlc_value_in_flight_percentage: u8, + /// Sets the maximum percentage of the total channel value that can be allocated to inbound + /// HTLCs in unannounced channels. + /// + /// This can be set to a value between 1-100, where the value corresponds to the percent of the + /// channel value in whole percentages. + /// + /// Note that: + /// * This caps the total value for inbound HTLCs in-flight only, and there's currently + /// no way to configure the cap for the total value of outbound HTLCs in-flight. + /// + /// * The requirements for your node being online to ensure the safety of HTLC-encumbered funds + /// are different from the non-HTLC-encumbered funds. This makes this an important knob to + /// restrict exposure to loss due to being offline for too long. + /// See [`ChannelHandshakeConfig::our_to_self_delay`] and [`ChannelConfig::cltv_expiry_delta`] + /// for more information. + /// + /// Default value: `25` + /// + /// Minimum value: `1` (Any values less will be treated as `1` instead.) + /// + /// Maximum value: `100` (Any values larger will be treated as `100` instead.) + pub unannounced_channel_max_inbound_htlc_value_in_flight_percentage: u8, /// If set, we attempt to negotiate the `scid_privacy` (referred to as `scid_alias` in the /// BOLTs) option for outbound private channels. This provides better privacy by not including /// our real on-chain channel UTXO in each invoice and requiring that our counterparty only @@ -246,7 +264,8 @@ impl Default for ChannelHandshakeConfig { minimum_depth: 6, our_to_self_delay: BREAKDOWN_TIMEOUT, our_htlc_minimum_msat: 1, - max_inbound_htlc_value_in_flight_percent_of_channel: 10, + announced_channel_max_inbound_htlc_value_in_flight_percentage: 10, + unannounced_channel_max_inbound_htlc_value_in_flight_percentage: 25, negotiate_scid_privacy: false, announce_for_forwarding: false, commit_upfront_shutdown_pubkey: true, @@ -268,7 +287,10 @@ impl Readable for ChannelHandshakeConfig { minimum_depth: Readable::read(reader)?, our_to_self_delay: Readable::read(reader)?, our_htlc_minimum_msat: Readable::read(reader)?, - max_inbound_htlc_value_in_flight_percent_of_channel: Readable::read(reader)?, + announced_channel_max_inbound_htlc_value_in_flight_percentage: Readable::read(reader)?, + unannounced_channel_max_inbound_htlc_value_in_flight_percentage: Readable::read( + reader, + )?, negotiate_scid_privacy: Readable::read(reader)?, announce_for_forwarding: Readable::read(reader)?, commit_upfront_shutdown_pubkey: Readable::read(reader)?, @@ -1171,9 +1193,15 @@ impl UserConfig { /// Config structure for overriding channel handshake parameters. #[derive(Default)] pub struct ChannelHandshakeConfigUpdate { - /// Overrides the percentage of the channel value we will cap the total value of outstanding inbound HTLCs to. See - /// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`]. - pub max_inbound_htlc_value_in_flight_percent_of_channel: Option, + /// Overrides the maximum percentage of the total channel value that can be allocated to inbound + /// HTLCs in announced channels. See + /// [`ChannelHandshakeConfig::announced_channel_max_inbound_htlc_value_in_flight_percentage`]. + pub announced_channel_max_inbound_htlc_value_in_flight_percentage: Option, + + /// Overrides the maximum percentage of the total channel value that can be allocated to inbound + /// HTLCs in unannounced channels. See + /// [`ChannelHandshakeConfig::unannounced_channel_max_inbound_htlc_value_in_flight_percentage`]. + pub unannounced_channel_max_inbound_htlc_value_in_flight_percentage: Option, /// Overrides the smallest value HTLC we will accept to process. See [`ChannelHandshakeConfig::our_htlc_minimum_msat`]. pub htlc_minimum_msat: Option, @@ -1199,9 +1227,17 @@ impl ChannelHandshakeConfig { /// Applies the provided handshake config update. pub fn apply(&mut self, config: &ChannelHandshakeConfigUpdate) { if let Some(max_in_flight_percent) = - config.max_inbound_htlc_value_in_flight_percent_of_channel + config.announced_channel_max_inbound_htlc_value_in_flight_percentage + { + self.announced_channel_max_inbound_htlc_value_in_flight_percentage = + max_in_flight_percent; + } + + if let Some(max_in_flight_percent) = + config.unannounced_channel_max_inbound_htlc_value_in_flight_percentage { - self.max_inbound_htlc_value_in_flight_percent_of_channel = max_in_flight_percent; + self.unannounced_channel_max_inbound_htlc_value_in_flight_percentage = + max_in_flight_percent; } if let Some(htlc_minimum_msat) = config.htlc_minimum_msat {