From b856d803323394c6467451410173578a7e1a4297 Mon Sep 17 00:00:00 2001 From: Andrew Barnes Date: Sun, 8 Mar 2026 12:35:13 -0400 Subject: [PATCH 1/2] Expose current dust exposure in ChannelDetails Add current_dust_exposure_msat field to ChannelDetails that surfaces the total dust exposure including both dust HTLC values and the commitment transaction fee component. This is the maximum of the holder and counterparty commitment transaction dust exposures. Users can compare this value against max_dust_htlc_exposure in ChannelConfig to understand how close a channel is to its dust exposure limit without needing internal channel access. Fixes #2264 --- fuzz/src/router.rs | 1 + lightning/src/ln/channel.rs | 8 ++++++++ lightning/src/ln/channel_state.rs | 19 +++++++++++++++++++ lightning/src/ln/channelmanager.rs | 1 + lightning/src/routing/router.rs | 2 ++ lightning/src/sign/tx_builder.rs | 3 +++ 6 files changed, 34 insertions(+) diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs index 2e5b15fc7f4..7c62b3ac5a0 100644 --- a/fuzz/src/router.rs +++ b/fuzz/src/router.rs @@ -255,6 +255,7 @@ pub fn do_test(data: &[u8], out: Out) { channel_shutdown_state: Some(ChannelShutdownState::NotShuttingDown), pending_inbound_htlcs: Vec::new(), pending_outbound_htlcs: Vec::new(), + current_dust_exposure_msat: None, }); } Some(&$first_hops_vec[..]) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index ab3627225d3..931c33bc607 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -121,6 +121,13 @@ pub struct AvailableBalances { pub next_outbound_htlc_limit_msat: u64, /// The minimum value we can assign to the next outbound HTLC pub next_outbound_htlc_minimum_msat: u64, + /// The current total dust exposure on this channel, in millisatoshis. + /// + /// This is the maximum of the dust exposure on the holder and counterparty commitment + /// transactions, and includes both the value of all pending HTLCs that are below the dust + /// threshold as well as any excess commitment transaction fees that contribute to dust + /// exposure. + pub dust_exposure_msat: u64, } #[derive(Debug, Clone, Copy, PartialEq)] @@ -12577,6 +12584,7 @@ where next_outbound_htlc_minimum_msat: acc .next_outbound_htlc_minimum_msat .max(e.next_outbound_htlc_minimum_msat), + dust_exposure_msat: acc.dust_exposure_msat.max(e.dust_exposure_msat), }) }) } diff --git a/lightning/src/ln/channel_state.rs b/lightning/src/ln/channel_state.rs index 5547bee8f4c..e7becd72f01 100644 --- a/lightning/src/ln/channel_state.rs +++ b/lightning/src/ln/channel_state.rs @@ -479,6 +479,21 @@ pub struct ChannelDetails { /// /// This field will be `None` for objects serialized with LDK versions prior to 0.2.0. pub funding_redeem_script: Option, + /// The current total dust exposure on this channel, in millisatoshis. + /// + /// This is the maximum of the dust exposure on the holder and counterparty commitment + /// transactions, and includes both the value of all pending HTLCs that are below the dust + /// threshold as well as the portion of commitment transaction fees that contribute to dust + /// exposure. + /// + /// The dust exposure is compared against + /// [`ChannelConfig::max_dust_htlc_exposure`] to determine whether new HTLCs can be + /// accepted or offered on this channel. + /// + /// This field will be `None` for objects serialized with LDK versions prior to 0.2.1. + /// + /// [`ChannelConfig::max_dust_htlc_exposure`]: crate::util::config::ChannelConfig::max_dust_htlc_exposure + pub current_dust_exposure_msat: Option, } impl ChannelDetails { @@ -533,6 +548,7 @@ impl ChannelDetails { outbound_capacity_msat: 0, next_outbound_htlc_limit_msat: 0, next_outbound_htlc_minimum_msat: u64::MAX, + dust_exposure_msat: 0, } }); let (to_remote_reserve_satoshis, to_self_reserve_satoshis) = @@ -596,6 +612,7 @@ impl ChannelDetails { channel_shutdown_state: Some(context.shutdown_state()), pending_inbound_htlcs: context.get_pending_inbound_htlc_details(funding), pending_outbound_htlcs: context.get_pending_outbound_htlc_details(funding), + current_dust_exposure_msat: Some(balance.dust_exposure_msat), } } } @@ -636,6 +653,7 @@ impl_writeable_tlv_based!(ChannelDetails, { (43, pending_inbound_htlcs, optional_vec), (45, pending_outbound_htlcs, optional_vec), (47, funding_redeem_script, option), + (49, current_dust_exposure_msat, option), (_unused, user_channel_id, (static_value, _user_channel_id_low.unwrap_or(0) as u128 | ((_user_channel_id_high.unwrap_or(0) as u128) << 64) )), @@ -756,6 +774,7 @@ mod tests { skimmed_fee_msat: Some(42), is_dust: false, }], + current_dust_exposure_msat: Some(150_000), }; let mut buffer = Vec::new(); channel_details.write(&mut buffer).unwrap(); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 5951c6cdbe6..82f9f6a1a29 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -7929,6 +7929,7 @@ impl< outbound_capacity_msat: 0, next_outbound_htlc_limit_msat: 0, next_outbound_htlc_minimum_msat: u64::MAX, + dust_exposure_msat: 0, } }); let is_in_range = (balances.next_outbound_htlc_minimum_msat diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 0c0d14b43fd..5de18695720 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -4164,6 +4164,7 @@ mod tests { channel_shutdown_state: Some(ChannelShutdownState::NotShuttingDown), pending_inbound_htlcs: Vec::new(), pending_outbound_htlcs: Vec::new(), + current_dust_exposure_msat: None, } } @@ -9665,6 +9666,7 @@ pub(crate) mod bench_utils { channel_shutdown_state: Some(ChannelShutdownState::NotShuttingDown), pending_inbound_htlcs: Vec::new(), pending_outbound_htlcs: Vec::new(), + current_dust_exposure_msat: None, } } diff --git a/lightning/src/sign/tx_builder.rs b/lightning/src/sign/tx_builder.rs index 4273b62c7b7..ebde25c4a61 100644 --- a/lightning/src/sign/tx_builder.rs +++ b/lightning/src/sign/tx_builder.rs @@ -512,6 +512,8 @@ fn get_available_balances( available_capacity_msat = 0; } + let dust_exposure_msat = cmp::max(local_dust_exposure_msat, remote_dust_exposure_msat); + #[allow(deprecated)] // TODO: Remove once balance_msat is removed crate::ln::channel::AvailableBalances { inbound_capacity_msat: remote_balance_before_fee_msat @@ -519,6 +521,7 @@ fn get_available_balances( outbound_capacity_msat, next_outbound_htlc_limit_msat: available_capacity_msat, next_outbound_htlc_minimum_msat, + dust_exposure_msat, } } From 6ea327e419fcc1b865ade8e32eb20584a5108851 Mon Sep 17 00:00:00 2001 From: Bortlesboat Date: Thu, 19 Mar 2026 21:51:17 -0400 Subject: [PATCH 2/2] Add config knob reference to dust_exposure_msat docs --- lightning/src/ln/channel.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 931c33bc607..db6968eab59 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -127,6 +127,8 @@ pub struct AvailableBalances { /// transactions, and includes both the value of all pending HTLCs that are below the dust /// threshold as well as any excess commitment transaction fees that contribute to dust /// exposure. + /// + /// See [`ChannelConfig::max_dust_htlc_exposure`] for the config knob that limits this. pub dust_exposure_msat: u64, }