Expose current dust exposure in ChannelDetails
What changed, and why it matters
This commit only exposes an existing internal calculation as a new public field in channel information structures. It does not change any security logic, limits, or behavior; it merely makes the current 'dust exposure' value readable by users of the library. There is no indication of a vulnerability being fixed.
No security action required; review as a normal API addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch adds dust_exposure_msat to AvailableBalances and current_dust_exposure_msat to ChannelDetails, plumbing an already-computed cmp::max(local_dust_exposure_msat, remote_dust_exposure_msat) value through serialization and test/bench/fuzz constructors. The underlying dust-exposure calculation and its comparison against ChannelConfig::max_dust_htlc_exposure remain unchanged. This is a pure API/observability addition.
Changed components
lightning/src/ln/channel.rslightning/src/ln/channel_state.rslightning/src/ln/channelmanager.rslightning/src/sign/tx_builder.rslightning/src/routing/router.rsfuzz/src/router.rsInspect captured patch +35 / −0
diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs
index 2e5b15f..7c62b3a 100644
--- a/fuzz/src/router.rs
+++ b/fuzz/src/router.rs
@@ -255,6 +255,7 @@ pub fn do_test<Out: test_logger::Output>(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 55d4a84..52c6fed 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -122,6 +122,15 @@ 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.
+ ///
+ /// See [`ChannelConfig::max_dust_htlc_exposure`] for more information on the dust calculation and to configure a limit.
+ pub dust_exposure_msat: u64,
}
#[derive(Debug, Clone, Copy, PartialEq)]
@@ -13603,6 +13612,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 5547bee..d59e30f 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<bitcoin::ScriptBuf>,
+ /// 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.3.
+ ///
+ /// [`ChannelConfig::max_dust_htlc_exposure`]: crate::util::config::ChannelConfig::max_dust_htlc_exposure
+ pub current_dust_exposure_msat: Option<u64>,
}
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 7f6d653..1f63d78 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -8025,6 +8025,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 0c0d14b..5de1869 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 a54f8f7..0af30da 100644
--- a/lightning/src/sign/tx_builder.rs
+++ b/lightning/src/sign/tx_builder.rs
@@ -575,6 +575,7 @@ fn get_available_balances(
next_outbound_htlc_minimum_msat,
available_capacity_msat,
);
+ let dust_exposure_msat = cmp::max(local_dust_exposure_msat, remote_dust_exposure_msat);
crate::ln::channel::AvailableBalances {
inbound_capacity_msat: remote_balance_before_fee_msat
@@ -582,6 +583,7 @@ fn get_available_balances(
outbound_capacity_msat,
next_outbound_htlc_limit_msat: available_capacity_msat,
next_outbound_htlc_minimum_msat,
+ dust_exposure_msat,
}
}
Why this scored 14/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.