Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions lightning/src/ln/chan_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,16 @@ pub(crate) fn commit_tx_fee_sat(feerate_per_kw: u32, num_htlcs: usize, channel_t
/ 1000
}

pub(crate) fn per_outbound_htlc_counterparty_commit_tx_fee_msat(feerate_per_kw: u32, channel_type_features: &ChannelTypeFeatures) -> u64 {
// Note that we need to divide before multiplying to round properly,
// since the lowest denomination of bitcoin on-chain is the satoshi.
let commitment_tx_fee = COMMITMENT_TX_WEIGHT_PER_HTLC * feerate_per_kw as u64 / 1000 * 1000;
if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
commitment_tx_fee + htlc_success_tx_weight(channel_type_features) * feerate_per_kw as u64 / 1000
pub(crate) fn commit_and_htlc_tx_fees_sat(feerate_per_kw: u32, num_accepted_htlcs: usize, num_offered_htlcs: usize, channel_type_features: &ChannelTypeFeatures) -> u64 {
let num_htlcs = num_accepted_htlcs + num_offered_htlcs;
let commit_tx_fees_sat = commit_tx_fee_sat(feerate_per_kw, num_htlcs, channel_type_features);
let htlc_tx_fees_sat = if !channel_type_features.supports_anchors_zero_fee_htlc_tx() {
num_accepted_htlcs as u64 * htlc_success_tx_weight(channel_type_features) * feerate_per_kw as u64 / 1000
+ num_offered_htlcs as u64 * htlc_timeout_tx_weight(channel_type_features) * feerate_per_kw as u64 / 1000
} else {
commitment_tx_fee
}
0
};
commit_tx_fees_sat + htlc_tx_fees_sat
}

// Various functions for key derivation and transaction creation for use within channels. Primarily
Expand Down
73 changes: 27 additions & 46 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use crate::ln::chan_utils::{
HolderCommitmentTransaction, ChannelTransactionParameters,
CounterpartyChannelTransactionParameters, MAX_HTLCS,
get_commitment_transaction_number_obscure_factor,
ClosingTransaction, commit_tx_fee_sat, per_outbound_htlc_counterparty_commit_tx_fee_msat,
ClosingTransaction, commit_tx_fee_sat,
};
use crate::ln::chan_utils;
use crate::ln::onion_utils::HTLCFailReason;
Expand Down Expand Up @@ -834,6 +834,10 @@ struct HTLCStats {
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,
outbound_holding_cell_msat: u64,
on_holder_tx_outbound_holding_cell_htlcs_count: u32, // dust HTLCs *non*-included
Expand Down Expand Up @@ -3705,27 +3709,21 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
.or(self.pending_update_fee.map(|(fee, _)| fee))
.unwrap_or(self.feerate_per_kw)
.checked_sub(dust_exposure_limiting_feerate);
if let Some(excess_feerate) = excess_feerate_opt {
let on_counterparty_tx_nondust_htlcs =
on_counterparty_tx_accepted_nondust_htlcs + on_counterparty_tx_offered_nondust_htlcs;
on_counterparty_tx_dust_exposure_msat +=
commit_tx_fee_sat(excess_feerate, on_counterparty_tx_nondust_htlcs, &self.channel_type) * 1000;
if !self.channel_type.supports_anchors_zero_fee_htlc_tx() {
on_counterparty_tx_dust_exposure_msat +=
on_counterparty_tx_accepted_nondust_htlcs as u64 * htlc_success_tx_weight(&self.channel_type)
* excess_feerate as u64 / 1000;
on_counterparty_tx_dust_exposure_msat +=
on_counterparty_tx_offered_nondust_htlcs as u64 * htlc_timeout_tx_weight(&self.channel_type)
* excess_feerate as u64 / 1000;
}
}
let extra_nondust_htlc_on_counterparty_tx_dust_exposure_msat = excess_feerate_opt.map(|excess_feerate| {
let extra_htlc_dust_exposure = on_counterparty_tx_dust_exposure_msat
+ chan_utils::commit_and_htlc_tx_fees_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + 1, on_counterparty_tx_offered_nondust_htlcs, &self.channel_type) * 1000;
on_counterparty_tx_dust_exposure_msat
+= chan_utils::commit_and_htlc_tx_fees_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs, on_counterparty_tx_offered_nondust_htlcs, &self.channel_type) * 1000;
extra_htlc_dust_exposure
});

HTLCStats {
pending_inbound_htlcs: self.pending_inbound_htlcs.len(),
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,
outbound_holding_cell_msat,
on_holder_tx_outbound_holding_cell_htlcs_count,
Expand Down Expand Up @@ -3930,13 +3928,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
context.holder_dust_limit_satoshis + dust_buffer_feerate * htlc_timeout_tx_weight(context.get_channel_type()) / 1000)
};

let excess_feerate_opt = self.feerate_per_kw.checked_sub(dust_exposure_limiting_feerate);
if let Some(excess_feerate) = excess_feerate_opt {
let htlc_dust_exposure_msat =
per_outbound_htlc_counterparty_commit_tx_fee_msat(excess_feerate, &context.channel_type);
let nondust_htlc_counterparty_tx_dust_exposure =
htlc_stats.on_counterparty_tx_dust_exposure_msat.saturating_add(htlc_dust_exposure_msat);
if nondust_htlc_counterparty_tx_dust_exposure > max_dust_htlc_exposure_msat {
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 {
// If adding an extra HTLC would put us over the dust limit in total fees, we cannot
// send any non-dust HTLCs.
available_capacity_msat = cmp::min(available_capacity_msat, htlc_success_dust_limit * 1000);
Expand Down Expand Up @@ -7336,6 +7329,8 @@ impl<SP: Deref> FundedChannel<SP> where
})
}

/// When this function is called, the HTLC is already irrevocably committed to the channel;
/// this function determines whether to fail the HTLC, or forward / claim it.
pub fn can_accept_incoming_htlc<F: Deref, L: Deref>(
&self, msg: &msgs::UpdateAddHTLC, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: L
) -> Result<(), (&'static str, u16)>
Expand All @@ -7350,33 +7345,19 @@ impl<SP: Deref> FundedChannel<SP> where
let dust_exposure_limiting_feerate = self.context.get_dust_exposure_limiting_feerate(&fee_estimator);
let htlc_stats = self.context.get_pending_htlc_stats(None, dust_exposure_limiting_feerate);
let max_dust_htlc_exposure_msat = self.context.get_max_dust_htlc_exposure_msat(dust_exposure_limiting_feerate);
let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
(0, 0)
let on_counterparty_tx_dust_htlc_exposure_msat = htlc_stats.on_counterparty_tx_dust_exposure_msat;
if on_counterparty_tx_dust_htlc_exposure_msat > max_dust_htlc_exposure_msat {
// Note that the total dust exposure includes both the dust HTLCs and the excess mining fees of the counterparty commitment transaction
log_info!(logger, "Cannot accept value that would put our total dust exposure at {} over the limit {} on counterparty commitment tx",
on_counterparty_tx_dust_htlc_exposure_msat, max_dust_htlc_exposure_msat);
return Err(("Exceeded our total dust exposure limit on counterparty commitment tx", 0x1000|7))
}
let htlc_success_dust_limit = if self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
0
} else {
let dust_buffer_feerate = self.context.get_dust_buffer_feerate(None) as u64;
(dust_buffer_feerate * htlc_timeout_tx_weight(self.context.get_channel_type()) / 1000,
dust_buffer_feerate * htlc_success_tx_weight(self.context.get_channel_type()) / 1000)
dust_buffer_feerate * htlc_success_tx_weight(self.context.get_channel_type()) / 1000
};
let exposure_dust_limit_timeout_sats = htlc_timeout_dust_limit + self.context.counterparty_dust_limit_satoshis;
if msg.amount_msat / 1000 < exposure_dust_limit_timeout_sats {
let on_counterparty_tx_dust_htlc_exposure_msat = htlc_stats.on_counterparty_tx_dust_exposure_msat;
if on_counterparty_tx_dust_htlc_exposure_msat > max_dust_htlc_exposure_msat {
log_info!(logger, "Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx",
on_counterparty_tx_dust_htlc_exposure_msat, max_dust_htlc_exposure_msat);
return Err(("Exceeded our dust exposure limit on counterparty commitment tx", 0x1000|7))
}
} else {
let htlc_dust_exposure_msat =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be worth adding a comment at the top of the method here noting that, when we're called, the HTLC is actually already added on the channel, thus we're trying to decide whether to fail or forward/claim.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly will do

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 9bea4ce

per_outbound_htlc_counterparty_commit_tx_fee_msat(self.context.feerate_per_kw, &self.context.channel_type);
let counterparty_tx_dust_exposure =
htlc_stats.on_counterparty_tx_dust_exposure_msat.saturating_add(htlc_dust_exposure_msat);
if counterparty_tx_dust_exposure > max_dust_htlc_exposure_msat {
log_info!(logger, "Cannot accept value that would put our exposure to tx fee dust at {} over the limit {} on counterparty commitment tx",
counterparty_tx_dust_exposure, max_dust_htlc_exposure_msat);
return Err(("Exceeded our tx fee dust exposure limit on counterparty commitment tx", 0x1000|7))
}
}

let exposure_dust_limit_success_sats = htlc_success_dust_limit + self.context.holder_dust_limit_satoshis;
if msg.amount_msat / 1000 < exposure_dust_limit_success_sats {
let on_holder_tx_dust_htlc_exposure_msat = htlc_stats.on_holder_tx_dust_exposure_msat;
Expand Down
Loading
Loading