Create `ChannelConstraints` to hold parameters for balance calculations
What changed, and why it matters
This commit is a pure internal code reorganization. It creates a new ChannelConstraints container and copies existing channel parameters into it so a future change can pass them more easily. The values used in balance calculations are identical to before; nothing about user funds, network behavior, or security rules changes.
No security action needed. Treat as normal refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces a ChannelConstraints struct in lightning/src/sign/tx_builder.rs and a helper get_channel_constraints in ChannelContext. get_available_balances_for_scope now reads reserve, dust limit, HTLC minimum, max in-flight, and max accepted HTLC values from ChannelConstraints instead of directly from self/funding. The commit message explicitly states there are no functional changes, and the diff confirms every value is taken from the same source with the same arithmetic.
Changed components
lightning/src/ln/channel.rslightning/src/sign/tx_builder.rsInspect captured patch +40 / −12
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 104e61a..fac0c38 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -72,8 +72,8 @@ use crate::offers::static_invoice::StaticInvoice;
use crate::routing::gossip::NodeId;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::tx_builder::{
- saturating_sub_anchor_outputs, ChannelStats, HTLCAmountDirection, SpecTxBuilder,
- TxBuilder,
+ saturating_sub_anchor_outputs, ChannelConstraints, ChannelStats, HTLCAmountDirection,
+ SpecTxBuilder, TxBuilder,
};
use crate::sign::{ChannelSigner, EntropySource, NodeSigner, Recipient, SignerProvider};
use crate::types::features::{ChannelTypeFeatures, InitFeatures};
@@ -5904,11 +5904,28 @@ impl<SP: SignerProvider> ChannelContext<SP> {
outbound_details
}
+ fn get_channel_constraints(&self, funding: &FundingScope) -> ChannelConstraints {
+ ChannelConstraints {
+ holder_dust_limit_satoshis: self.holder_dust_limit_satoshis,
+ counterparty_selected_channel_reserve_satoshis: funding
+ .counterparty_selected_channel_reserve_satoshis
+ .unwrap_or(0),
+ counterparty_dust_limit_satoshis: self.counterparty_dust_limit_satoshis,
+ holder_selected_channel_reserve_satoshis: funding
+ .holder_selected_channel_reserve_satoshis,
+ counterparty_htlc_minimum_msat: self.counterparty_htlc_minimum_msat,
+ counterparty_max_accepted_htlcs: self.counterparty_max_accepted_htlcs as u64,
+ counterparty_max_htlc_value_in_flight_msat: self
+ .counterparty_max_htlc_value_in_flight_msat,
+ }
+ }
+
#[rustfmt::skip]
fn get_available_balances_for_scope<F: FeeEstimator>(
&self, funding: &FundingScope, fee_estimator: &LowerBoundedFeeEstimator<F>,
) -> AvailableBalances {
let context = &self;
+ let channel_constraints = self.get_channel_constraints(funding);
// Note that we have to handle overflow due to the case mentioned in the docs in general
// here.
@@ -5927,7 +5944,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
let outbound_capacity_msat = local_balance_before_fee_msat
.saturating_sub(
- funding.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) * 1000);
+ channel_constraints.counterparty_selected_channel_reserve_satoshis * 1000);
let mut available_capacity_msat = outbound_capacity_msat;
let (real_htlc_success_tx_fee_sat, real_htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat(
@@ -5948,7 +5965,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
Some(())
};
- let real_dust_limit_timeout_sat = real_htlc_timeout_tx_fee_sat + context.holder_dust_limit_satoshis;
+ let real_dust_limit_timeout_sat = real_htlc_timeout_tx_fee_sat + channel_constraints.holder_dust_limit_satoshis;
let htlc_above_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000, HTLCInitiator::LocalOffered);
let mut max_reserved_commit_tx_fee_msat = context.next_local_commit_tx_fee_msat(&funding, htlc_above_dust, fee_spike_buffer_htlc);
let htlc_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000 - 1, HTLCInitiator::LocalOffered);
@@ -5972,11 +5989,11 @@ impl<SP: SignerProvider> ChannelContext<SP> {
} else {
// If the channel is inbound (i.e. counterparty pays the fee), we need to make sure
// sending a new HTLC won't reduce their balance below our reserve threshold.
- let real_dust_limit_success_sat = real_htlc_success_tx_fee_sat + context.counterparty_dust_limit_satoshis;
+ let real_dust_limit_success_sat = real_htlc_success_tx_fee_sat + channel_constraints.counterparty_dust_limit_satoshis;
let htlc_above_dust = HTLCCandidate::new(real_dust_limit_success_sat * 1000, HTLCInitiator::LocalOffered);
let max_reserved_commit_tx_fee_msat = context.next_remote_commit_tx_fee_msat(funding, Some(htlc_above_dust), None);
- let holder_selected_chan_reserve_msat = funding.holder_selected_channel_reserve_satoshis * 1000;
+ let holder_selected_chan_reserve_msat = channel_constraints.holder_selected_channel_reserve_satoshis * 1000;
if remote_balance_before_fee_msat < max_reserved_commit_tx_fee_msat + holder_selected_chan_reserve_msat {
// If another HTLC's fee would reduce the remote's balance below the reserve limit
// we've selected for them, we can only send dust HTLCs.
@@ -5984,7 +6001,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
}
}
- let mut next_outbound_htlc_minimum_msat = context.counterparty_htlc_minimum_msat;
+ let mut next_outbound_htlc_minimum_msat = channel_constraints.counterparty_htlc_minimum_msat;
// If we get close to our maximum dust exposure, we end up in a situation where we can send
// between zero and the remaining dust exposure limit remaining OR above the dust limit.
@@ -5998,8 +6015,8 @@ impl<SP: SignerProvider> ChannelContext<SP> {
let (buffer_htlc_success_tx_fee_sat, buffer_htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat(
funding.get_channel_type(), dust_buffer_feerate,
);
- let buffer_dust_limit_success_sat = buffer_htlc_success_tx_fee_sat + context.counterparty_dust_limit_satoshis;
- let buffer_dust_limit_timeout_sat = buffer_htlc_timeout_tx_fee_sat + context.holder_dust_limit_satoshis;
+ let buffer_dust_limit_success_sat = buffer_htlc_success_tx_fee_sat + channel_constraints.counterparty_dust_limit_satoshis;
+ let buffer_dust_limit_timeout_sat = buffer_htlc_timeout_tx_fee_sat + channel_constraints.holder_dust_limit_satoshis;
if let Some(extra_htlc_dust_exposure) = htlc_stats.extra_nondust_htlc_on_counterparty_tx_dust_exposure_msat {
if extra_htlc_dust_exposure > max_dust_htlc_exposure_msat {
@@ -6033,15 +6050,15 @@ impl<SP: SignerProvider> ChannelContext<SP> {
}
available_capacity_msat = cmp::min(available_capacity_msat,
- context.counterparty_max_htlc_value_in_flight_msat - htlc_stats.pending_outbound_htlcs_value_msat);
+ channel_constraints.counterparty_max_htlc_value_in_flight_msat - htlc_stats.pending_outbound_htlcs_value_msat);
- if htlc_stats.pending_outbound_htlcs + 1 > context.counterparty_max_accepted_htlcs as usize {
+ if htlc_stats.pending_outbound_htlcs + 1 > channel_constraints.counterparty_max_accepted_htlcs as usize {
available_capacity_msat = 0;
}
#[allow(deprecated)] // TODO: Remove once balance_msat is removed.
AvailableBalances {
- inbound_capacity_msat: remote_balance_before_fee_msat.saturating_sub(funding.holder_selected_channel_reserve_satoshis * 1000),
+ inbound_capacity_msat: remote_balance_before_fee_msat.saturating_sub(channel_constraints.holder_selected_channel_reserve_satoshis * 1000),
outbound_capacity_msat,
next_outbound_htlc_limit_msat: available_capacity_msat,
next_outbound_htlc_minimum_msat,
diff --git a/lightning/src/sign/tx_builder.rs b/lightning/src/sign/tx_builder.rs
index 9c2942f..6dae14b 100644
--- a/lightning/src/sign/tx_builder.rs
+++ b/lightning/src/sign/tx_builder.rs
@@ -173,6 +173,17 @@ fn get_dust_buffer_feerate(feerate_per_kw: u32) -> u32 {
cmp::max(feerate_per_kw.saturating_add(2530), feerate_plus_quarter.unwrap_or(u32::MAX))
}
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct ChannelConstraints {
+ pub holder_dust_limit_satoshis: u64,
+ pub counterparty_selected_channel_reserve_satoshis: u64,
+ pub counterparty_dust_limit_satoshis: u64,
+ pub holder_selected_channel_reserve_satoshis: u64,
+ pub counterparty_htlc_minimum_msat: u64,
+ pub counterparty_max_htlc_value_in_flight_msat: u64,
+ pub counterparty_max_accepted_htlcs: u64,
+}
+
fn get_next_commitment_stats(
local: bool, is_outbound_from_holder: bool, channel_value_satoshis: u64,
value_to_holder_msat: u64, next_commitment_htlcs: &[HTLCAmountDirection],
Why this scored 15/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.