Erase `get_pending_htlc_stats`, `next_*_commit_tx_fee_msat` in `channel`
What changed, and why it matters
This commit removes old, unused helper functions and data structures from the Lightning channel code and updates the tests to use newer replacement functions. There is no indication of a security vulnerability being fixed; it appears to be a code cleanup or refactoring change.
No security action required. Treat as routine refactoring; verify downstream callers do not still depend on the removed private helpers.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit deletes HTLCStats, HTLCCandidate, HTLCInitiator, get_pending_htlc_stats, next_local_commit_tx_fee_msat, and next_remote_commit_tx_fee_msat from lightning/src/ln/channel.rs. It also updates unit tests to call newer get_next_local/remote_commitment_stats APIs instead. The change is purely subtractive/refactoring with no functional security fix evident in the diff.
Changed components
lightning/src/ln/channel.rsInspect captured patch +19 / −319
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index b038d70..f52f3af 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -1133,26 +1133,6 @@ pub enum AnnouncementSigsState {
PeerReceived,
}
-/// An enum indicating whether the local or remote side offered a given HTLC.
-enum HTLCInitiator {
- LocalOffered,
- #[allow(dead_code)]
- RemoteOffered,
-}
-
-/// Current counts of various HTLCs, useful for calculating current balances available exactly.
-struct HTLCStats {
- pending_outbound_htlcs: usize,
- pending_inbound_htlcs_value_msat: u64,
- pending_outbound_htlcs_value_msat: u64,
- on_counterparty_tx_dust_exposure_msat: u64,
- // If the counterparty sets a feerate on the channel in excess of our dust_exposure_limiting_feerate,
- // this will be set to the dust exposure that would result from us adding an additional nondust outbound
- // htlc on the counterparty's commitment transaction.
- extra_nondust_htlc_on_counterparty_tx_dust_exposure_msat: Option<u64>,
- on_holder_tx_dust_exposure_msat: u64,
-}
-
/// A struct gathering data on a commitment, either local or remote.
struct CommitmentData<'a> {
tx: CommitmentTransaction,
@@ -1172,18 +1152,6 @@ pub(crate) struct CommitmentStats {
pub remote_balance_before_fee_msat: u64,
}
-/// Used when calculating whether we or the remote can afford an additional HTLC.
-struct HTLCCandidate {
- amount_msat: u64,
- origin: HTLCInitiator,
-}
-
-impl HTLCCandidate {
- fn new(amount_msat: u64, origin: HTLCInitiator) -> Self {
- Self { amount_msat, origin }
- }
-}
-
/// A return value enum for get_update_fulfill_htlc. See UpdateFulfillCommitFetch variants for
/// description
enum UpdateFulfillFetch {
@@ -5691,111 +5659,6 @@ impl<SP: SignerProvider> ChannelContext<SP> {
self.counterparty_forwarding_info.clone()
}
- /// Returns a HTLCStats about pending htlcs
- #[rustfmt::skip]
- fn get_pending_htlc_stats(
- &self, funding: &FundingScope, outbound_feerate_update: Option<u32>,
- dust_exposure_limiting_feerate: Option<u32>,
- ) -> HTLCStats {
- let context = self;
-
- let dust_buffer_feerate = self.get_dust_buffer_feerate(outbound_feerate_update);
- let (htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat(
- funding.get_channel_type(), dust_buffer_feerate,
- );
-
- let mut on_holder_tx_dust_exposure_msat = 0;
- let mut on_counterparty_tx_dust_exposure_msat = 0;
-
- let mut on_counterparty_tx_offered_nondust_htlcs = 0;
- let mut on_counterparty_tx_accepted_nondust_htlcs = 0;
-
- let mut pending_inbound_htlcs_value_msat = 0;
-
- {
- let counterparty_dust_limit_timeout_sat = htlc_timeout_tx_fee_sat + context.counterparty_dust_limit_satoshis;
- let holder_dust_limit_success_sat = htlc_success_tx_fee_sat + context.holder_dust_limit_satoshis;
- for htlc in context.pending_inbound_htlcs.iter() {
- pending_inbound_htlcs_value_msat += htlc.amount_msat;
- if htlc.amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
- on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
- } else {
- on_counterparty_tx_offered_nondust_htlcs += 1;
- }
- if htlc.amount_msat / 1000 < holder_dust_limit_success_sat {
- on_holder_tx_dust_exposure_msat += htlc.amount_msat;
- }
- }
- }
-
- let mut pending_outbound_htlcs_value_msat = 0;
- let mut pending_outbound_htlcs = self.pending_outbound_htlcs.len();
- {
- let counterparty_dust_limit_success_sat = htlc_success_tx_fee_sat + context.counterparty_dust_limit_satoshis;
- let holder_dust_limit_timeout_sat = htlc_timeout_tx_fee_sat + context.holder_dust_limit_satoshis;
- for htlc in context.pending_outbound_htlcs.iter() {
- pending_outbound_htlcs_value_msat += htlc.amount_msat;
- if htlc.amount_msat / 1000 < counterparty_dust_limit_success_sat {
- on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
- } else {
- on_counterparty_tx_accepted_nondust_htlcs += 1;
- }
- if htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat {
- on_holder_tx_dust_exposure_msat += htlc.amount_msat;
- }
- }
-
- for update in context.holding_cell_htlc_updates.iter() {
- if let &HTLCUpdateAwaitingACK::AddHTLC { ref amount_msat, .. } = update {
- pending_outbound_htlcs += 1;
- pending_outbound_htlcs_value_msat += amount_msat;
- if *amount_msat / 1000 < counterparty_dust_limit_success_sat {
- on_counterparty_tx_dust_exposure_msat += amount_msat;
- } else {
- on_counterparty_tx_accepted_nondust_htlcs += 1;
- }
- if *amount_msat / 1000 < holder_dust_limit_timeout_sat {
- on_holder_tx_dust_exposure_msat += amount_msat;
- }
- }
- }
- }
-
- // Include any mining "excess" fees in the dust calculation
- let excess_feerate_opt = outbound_feerate_update
- .or(self.pending_update_fee.map(|(fee, _)| fee))
- .unwrap_or(self.feerate_per_kw)
- .checked_sub(dust_exposure_limiting_feerate.unwrap_or(0));
-
- // Dust exposure is only decoupled from feerate for zero fee commitment channels.
- let is_zero_fee_comm = funding.get_channel_type().supports_anchor_zero_fee_commitments();
- debug_assert_eq!(is_zero_fee_comm, dust_exposure_limiting_feerate.is_none());
- if is_zero_fee_comm {
- debug_assert_eq!(excess_feerate_opt, Some(0));
- }
-
- let extra_nondust_htlc_on_counterparty_tx_dust_exposure_msat = excess_feerate_opt.map(|excess_feerate| {
- let extra_htlc_commit_tx_fee_sat = chan_utils::commit_tx_fee_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + 1 + on_counterparty_tx_offered_nondust_htlcs, funding.get_channel_type());
- let extra_htlc_htlc_tx_fees_sat = chan_utils::htlc_tx_fees_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + 1, on_counterparty_tx_offered_nondust_htlcs, funding.get_channel_type());
-
- let commit_tx_fee_sat = chan_utils::commit_tx_fee_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + on_counterparty_tx_offered_nondust_htlcs, funding.get_channel_type());
- let htlc_tx_fees_sat = chan_utils::htlc_tx_fees_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs, on_counterparty_tx_offered_nondust_htlcs, funding.get_channel_type());
-
- let extra_htlc_dust_exposure = on_counterparty_tx_dust_exposure_msat + (extra_htlc_commit_tx_fee_sat + extra_htlc_htlc_tx_fees_sat) * 1000;
- on_counterparty_tx_dust_exposure_msat += (commit_tx_fee_sat + htlc_tx_fees_sat) * 1000;
- extra_htlc_dust_exposure
- });
-
- HTLCStats {
- pending_outbound_htlcs,
- pending_inbound_htlcs_value_msat,
- pending_outbound_htlcs_value_msat,
- on_counterparty_tx_dust_exposure_msat,
- extra_nondust_htlc_on_counterparty_tx_dust_exposure_msat,
- on_holder_tx_dust_exposure_msat,
- }
- }
-
/// Returns information on all pending inbound HTLCs.
#[rustfmt::skip]
pub fn get_pending_inbound_htlc_details(&self, funding: &FundingScope) -> Vec<InboundHTLCDetails> {
@@ -5932,169 +5795,6 @@ impl<SP: SignerProvider> ChannelContext<SP> {
)
}
- /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
- /// number of pending HTLCs that are on track to be in our next commitment tx.
- ///
- /// Includes the `HTLCCandidate` given by `htlc` and an additional non-dust HTLC if
- /// `fee_spike_buffer_htlc` is `Some`.
- ///
- /// The first extra HTLC is useful for determining whether we can accept a further HTLC, the
- /// second allows for creating a buffer to ensure a further HTLC can always be accepted/added.
- ///
- /// Dust HTLCs are excluded.
- #[rustfmt::skip]
- fn next_local_commit_tx_fee_msat(
- &self, funding: &FundingScope, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>,
- ) -> u64 {
- let context = self;
- assert!(funding.is_outbound());
-
- if funding.get_channel_type().supports_anchor_zero_fee_commitments() {
- debug_assert_eq!(context.feerate_per_kw, 0);
- debug_assert!(fee_spike_buffer_htlc.is_none());
- return 0;
- }
-
- let (htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat(
- funding.get_channel_type(), context.feerate_per_kw,
- );
- let real_dust_limit_success_sat = htlc_success_tx_fee_sat + context.holder_dust_limit_satoshis;
- let real_dust_limit_timeout_sat = htlc_timeout_tx_fee_sat + context.holder_dust_limit_satoshis;
-
- let mut addl_htlcs = 0;
- if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
- match htlc.origin {
- HTLCInitiator::LocalOffered => {
- if htlc.amount_msat / 1000 >= real_dust_limit_timeout_sat {
- addl_htlcs += 1;
- }
- },
- HTLCInitiator::RemoteOffered => {
- if htlc.amount_msat / 1000 >= real_dust_limit_success_sat {
- addl_htlcs += 1;
- }
- }
- }
-
- let mut included_htlcs = 0;
- for ref htlc in context.pending_inbound_htlcs.iter() {
- if htlc.amount_msat / 1000 < real_dust_limit_success_sat {
- continue
- }
- // We include LocalRemoved HTLCs here because we may still need to broadcast a commitment
- // transaction including this HTLC if it times out before they RAA.
- included_htlcs += 1;
- }
-
- for ref htlc in context.pending_outbound_htlcs.iter() {
- if htlc.amount_msat / 1000 < real_dust_limit_timeout_sat {
- continue
- }
- match htlc.state {
- OutboundHTLCState::LocalAnnounced {..} => included_htlcs += 1,
- OutboundHTLCState::Committed => included_htlcs += 1,
- OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1,
- // We don't include AwaitingRemoteRevokeToRemove HTLCs because our next commitment
- // transaction won't be generated until they send us their next RAA, which will mean
- // dropping any HTLCs in this state.
- _ => {},
- }
- }
-
- for htlc in context.holding_cell_htlc_updates.iter() {
- match htlc {
- &HTLCUpdateAwaitingACK::AddHTLC { amount_msat, .. } => {
- if amount_msat / 1000 < real_dust_limit_timeout_sat {
- continue
- }
- included_htlcs += 1
- },
- _ => {}, // Don't include claims/fails that are awaiting ack, because once we get the
- // ack we're guaranteed to never include them in commitment txs anymore.
- }
- }
-
- let num_htlcs = included_htlcs + addl_htlcs;
- chan_utils::commit_tx_fee_sat(context.feerate_per_kw, num_htlcs, funding.get_channel_type()) * 1000
- }
-
- /// Get the commitment tx fee for the remote's next commitment transaction based on the number of
- /// pending HTLCs that are on track to be in their next commitment tx
- ///
- /// Optionally includes the `HTLCCandidate` given by `htlc` and an additional non-dust HTLC if
- /// `fee_spike_buffer_htlc` is `Some`.
- ///
- /// The first extra HTLC is useful for determining whether we can accept a further HTLC, the
- /// second allows for creating a buffer to ensure a further HTLC can always be accepted/added.
- ///
- /// Dust HTLCs are excluded.
- #[rustfmt::skip]
- fn next_remote_commit_tx_fee_msat(
- &self, funding: &FundingScope, htlc: Option<HTLCCandidate>, fee_spike_buffer_htlc: Option<()>,
- ) -> u64 {
- let context = self;
- assert!(!funding.is_outbound());
-
- if funding.get_channel_type().supports_anchor_zero_fee_commitments() {
- debug_assert_eq!(context.feerate_per_kw, 0);
- debug_assert!(fee_spike_buffer_htlc.is_none());
- return 0
- }
-
- debug_assert!(htlc.is_some() || fee_spike_buffer_htlc.is_some(), "At least one of the options must be set");
-
- let (htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat(
- funding.get_channel_type(), context.feerate_per_kw,
- );
- let real_dust_limit_success_sat = htlc_success_tx_fee_sat + context.counterparty_dust_limit_satoshis;
- let real_dust_limit_timeout_sat = htlc_timeout_tx_fee_sat + context.counterparty_dust_limit_satoshis;
-
- let mut addl_htlcs = 0;
- if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
- if let Some(htlc) = &htlc {
- match htlc.origin {
- HTLCInitiator::LocalOffered => {
- if htlc.amount_msat / 1000 >= real_dust_limit_success_sat {
- addl_htlcs += 1;
- }
- },
- HTLCInitiator::RemoteOffered => {
- if htlc.amount_msat / 1000 >= real_dust_limit_timeout_sat {
- addl_htlcs += 1;
- }
- }
- }
- }
-
- // When calculating the set of HTLCs which will be included in their next commitment_signed, all
- // non-dust inbound HTLCs are included (as all states imply it will be included) and only
- // committed outbound HTLCs, see below.
- let mut included_htlcs = 0;
- for ref htlc in context.pending_inbound_htlcs.iter() {
- if htlc.amount_msat / 1000 < real_dust_limit_timeout_sat {
- continue
- }
- included_htlcs += 1;
- }
-
- for ref htlc in context.pending_outbound_htlcs.iter() {
- if htlc.amount_msat / 1000 < real_dust_limit_success_sat {
- continue
- }
- // We only include outbound HTLCs if it will not be included in their next commitment_signed,
- // i.e. if they've responded to us with an RAA after announcement.
- match htlc.state {
- OutboundHTLCState::Committed => included_htlcs += 1,
- OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1,
- OutboundHTLCState::LocalAnnounced { .. } => included_htlcs += 1,
- _ => {},
- }
- }
-
- let num_htlcs = included_htlcs + addl_htlcs;
- chan_utils::commit_tx_fee_sat(context.feerate_per_kw, num_htlcs, funding.get_channel_type()) * 1000
- }
-
#[rustfmt::skip]
fn if_unbroadcasted_funding<F, O>(&self, f: F) -> Option<O> where F: Fn() -> Option<O> {
match self.channel_state {
@@ -16061,10 +15761,9 @@ mod tests {
use crate::chain::BestBlock;
use crate::ln::chan_utils::{self, commit_tx_fee_sat, ChannelTransactionParameters};
use crate::ln::channel::{
- AwaitingChannelReadyFlags, ChannelState, FundedChannel, HTLCCandidate, HTLCInitiator,
- HTLCUpdateAwaitingACK, InboundHTLCOutput, InboundHTLCState, InboundUpdateAdd,
- InboundV1Channel, OutboundHTLCOutput, OutboundHTLCState, OutboundV1Channel,
- MIN_THEIR_CHAN_RESERVE_SATOSHIS,
+ AwaitingChannelReadyFlags, ChannelState, FundedChannel, HTLCUpdateAwaitingACK,
+ InboundHTLCOutput, InboundHTLCState, InboundUpdateAdd, InboundV1Channel,
+ OutboundHTLCOutput, OutboundHTLCState, OutboundV1Channel, MIN_THEIR_CHAN_RESERVE_SATOSHIS,
};
use crate::ln::channel_keys::{RevocationBasepoint, RevocationKey};
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
@@ -16074,6 +15773,7 @@ mod tests {
use crate::ln::script::ShutdownScript;
use crate::prelude::*;
use crate::routing::router::{Path, RouteHop};
+ use crate::sign::tx_builder::HTLCAmountDirection;
#[cfg(ldk_test_vectors)]
use crate::sign::{ChannelSigner, EntropySource, InMemorySigner, SignerProvider};
use crate::sync::Mutex;
@@ -16266,7 +15966,7 @@ mod tests {
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let mut config = UserConfig::default();
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = false;
- let mut node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, None, &logger).unwrap();
+ let mut node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10_000_000, 100_000_000, 42, &config, 0, 42, None, &logger).unwrap();
// Create Node B's channel by receiving Node A's open_channel message
// Make sure A's dust limit is as we expect.
@@ -16325,8 +16025,8 @@ mod tests {
// Make sure when Node A calculates their local commitment transaction, none of the HTLCs pass
// the dust limit check.
- let htlc_candidate = HTLCCandidate::new(htlc_amount_msat, HTLCInitiator::LocalOffered);
- let local_commit_tx_fee = node_a_chan.context.next_local_commit_tx_fee_msat(&node_a_chan.funding, htlc_candidate, None);
+ let htlc_candidate = HTLCAmountDirection { amount_msat: htlc_amount_msat, outbound: true };
+ let local_commit_tx_fee = node_a_chan.context.get_next_local_commitment_stats(&node_a_chan.funding, Some(htlc_candidate), false, 0, node_a_chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
let local_commit_fee_0_htlcs = commit_tx_fee_sat(node_a_chan.context.feerate_per_kw, 0, node_a_chan.funding.get_channel_type()) * 1000;
assert_eq!(local_commit_tx_fee, local_commit_fee_0_htlcs);
@@ -16334,15 +16034,15 @@ mod tests {
// of the HTLCs are seen to be above the dust limit.
node_a_chan.funding.channel_transaction_parameters.is_outbound_from_holder = false;
let remote_commit_fee_3_htlcs = commit_tx_fee_sat(node_a_chan.context.feerate_per_kw, 3, node_a_chan.funding.get_channel_type()) * 1000;
- let htlc_candidate = HTLCCandidate::new(htlc_amount_msat, HTLCInitiator::LocalOffered);
- let remote_commit_tx_fee = node_a_chan.context.next_remote_commit_tx_fee_msat(&node_a_chan.funding, Some(htlc_candidate), None);
+ let htlc_candidate = HTLCAmountDirection { amount_msat: htlc_amount_msat, outbound: true };
+ let remote_commit_tx_fee = node_a_chan.context.get_next_remote_commitment_stats(&node_a_chan.funding, Some(htlc_candidate), false, 0, node_a_chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
assert_eq!(remote_commit_tx_fee, remote_commit_fee_3_htlcs);
}
#[test]
#[rustfmt::skip]
fn test_timeout_vs_success_htlc_dust_limit() {
- // Make sure that when `next_remote_commit_tx_fee_msat` and `next_local_commit_tx_fee_msat`
+ // Make sure that when `get_next_local/remote_commitment_stats`
// calculate the real dust limits for HTLCs (i.e. the dust limit given by the counterparty
// *plus* the fees paid for the HTLC) they don't swap `HTLC_SUCCESS_TX_WEIGHT` for
// `HTLC_TIMEOUT_TX_WEIGHT`, and vice versa.
@@ -16357,7 +16057,7 @@ mod tests {
let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let mut config = UserConfig::default();
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = false;
- let mut chan = OutboundV1Channel::<&TestKeysInterface>::new(&fee_est, &&keys_provider, &&keys_provider, node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, None, &logger).unwrap();
+ let mut chan = OutboundV1Channel::<&TestKeysInterface>::new(&fee_est, &&keys_provider, &&keys_provider, node_id, &channelmanager::provided_init_features(&config), 10_000_000, 100_000_000, 42, &config, 0, 42, None, &logger).unwrap();
let commitment_tx_fee_0_htlcs = commit_tx_fee_sat(chan.context.feerate_per_kw, 0, chan.funding.get_channel_type()) * 1000;
let commitment_tx_fee_1_htlc = commit_tx_fee_sat(chan.context.feerate_per_kw, 1, chan.funding.get_channel_type()) * 1000;
@@ -16368,28 +16068,28 @@ mod tests {
// If HTLC_SUCCESS_TX_WEIGHT and HTLC_TIMEOUT_TX_WEIGHT were swapped: then this HTLC would be
// counted as dust when it shouldn't be.
let htlc_amt_above_timeout = (htlc_timeout_tx_fee_sat + chan.context.holder_dust_limit_satoshis + 1) * 1000;
- let htlc_candidate = HTLCCandidate::new(htlc_amt_above_timeout, HTLCInitiator::LocalOffered);
- let commitment_tx_fee = chan.context.next_local_commit_tx_fee_msat(&chan.funding, htlc_candidate, None);
+ let htlc_candidate = HTLCAmountDirection { amount_msat: htlc_amt_above_timeout, outbound: true };
+ let commitment_tx_fee = chan.context.get_next_local_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
assert_eq!(commitment_tx_fee, commitment_tx_fee_1_htlc);
// If swapped: this HTLC would be counted as non-dust when it shouldn't be.
let dust_htlc_amt_below_success = (htlc_success_tx_fee_sat + chan.context.holder_dust_limit_satoshis - 1) * 1000;
- let htlc_candidate = HTLCCandidate::new(dust_htlc_amt_below_success, HTLCInitiator::RemoteOffered);
- let commitment_tx_fee = chan.context.next_local_commit_tx_fee_msat(&chan.funding, htlc_candidate, None);
+ let htlc_candidate = HTLCAmountDirection { amount_msat: dust_htlc_amt_below_success, outbound: false };
+ let commitment_tx_fee = chan.context.get_next_local_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
assert_eq!(commitment_tx_fee, commitment_tx_fee_0_htlcs);
chan.funding.channel_transaction_parameters.is_outbound_from_holder = false;
// If swapped: this HTLC would be counted as non-dust when it shouldn't be.
let dust_htlc_amt_above_timeout = (htlc_timeout_tx_fee_sat + chan.context.counterparty_dust_limit_satoshis + 1) * 1000;
- let htlc_candidate = HTLCCandidate::new(dust_htlc_amt_above_timeout, HTLCInitiator::LocalOffered);
- let commitment_tx_fee = chan.context.next_remote_commit_tx_fee_msat(&chan.funding, Some(htlc_candidate), None);
+ let htlc_candidate = HTLCAmountDirection { amount_msat: dust_htlc_amt_above_timeout, outbound: true };
+ let commitment_tx_fee = chan.context.get_next_remote_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
assert_eq!(commitment_tx_fee, commitment_tx_fee_0_htlcs);
// If swapped: this HTLC would be counted as dust when it shouldn't be.
let htlc_amt_below_success = (htlc_success_tx_fee_sat + chan.context.counterparty_dust_limit_satoshis - 1) * 1000;
- let htlc_candidate = HTLCCandidate::new(htlc_amt_below_success, HTLCInitiator::RemoteOffered);
- let commitment_tx_fee = chan.context.next_remote_commit_tx_fee_msat(&chan.funding, Some(htlc_candidate), None);
+ let htlc_candidate = HTLCAmountDirection { amount_msat: htlc_amt_below_success, outbound: false };
+ let commitment_tx_fee = chan.context.get_next_remote_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
assert_eq!(commitment_tx_fee, commitment_tx_fee_1_htlc);
}
Why this scored 12/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.