Skip to content

Commit af2ff9b

Browse files
committed
Account for zero fee HTLC transaction within dust limit calculation
With the zero fee HTLC transaction anchors variant, HTLCs can no longer be trimmed due to their amount being too low to have a mempool valid HTLC transaction. Now they can only be trimmed based on the dust limit of each party within the channel.
1 parent cd0d19c commit af2ff9b

File tree

2 files changed

+70
-14
lines changed

2 files changed

+70
-14
lines changed

lightning/src/ln/channel.rs

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,7 +1466,12 @@ impl<Signer: Sign> Channel<Signer> {
14661466
($htlc: expr, $outbound: expr, $source: expr, $state_name: expr) => {
14671467
if $outbound == local { // "offered HTLC output"
14681468
let htlc_in_tx = get_htlc_in_commitment!($htlc, true);
1469-
if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + (feerate_per_kw as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) {
1469+
let htlc_tx_fee = if self.opt_anchors() {
1470+
0
1471+
} else {
1472+
feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000
1473+
};
1474+
if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + htlc_tx_fee {
14701475
log_trace!(logger, " ...including {} {} HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
14711476
included_non_dust_htlcs.push((htlc_in_tx, $source));
14721477
} else {
@@ -1475,7 +1480,12 @@ impl<Signer: Sign> Channel<Signer> {
14751480
}
14761481
} else {
14771482
let htlc_in_tx = get_htlc_in_commitment!($htlc, false);
1478-
if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + (feerate_per_kw as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) {
1483+
let htlc_tx_fee = if self.opt_anchors() {
1484+
0
1485+
} else {
1486+
feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000
1487+
};
1488+
if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + htlc_tx_fee {
14791489
log_trace!(logger, " ...including {} {} HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
14801490
included_non_dust_htlcs.push((htlc_in_tx, $source));
14811491
} else {
@@ -2396,8 +2406,15 @@ impl<Signer: Sign> Channel<Signer> {
23962406
on_holder_tx_holding_cell_htlcs_count: 0,
23972407
};
23982408

2399-
let counterparty_dust_limit_timeout_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
2400-
let holder_dust_limit_success_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
2409+
let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.opt_anchors() {
2410+
(0, 0)
2411+
} else {
2412+
let dust_buffer_feerate = self.get_dust_buffer_feerate(outbound_feerate_update) as u64;
2413+
(dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000,
2414+
dust_buffer_feerate * htlc_success_tx_weight(false) / 1000)
2415+
};
2416+
let counterparty_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.counterparty_dust_limit_satoshis;
2417+
let holder_dust_limit_success_sat = htlc_success_dust_limit + self.holder_dust_limit_satoshis;
24012418
for ref htlc in self.pending_inbound_htlcs.iter() {
24022419
stats.pending_htlcs_value_msat += htlc.amount_msat;
24032420
if htlc.amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
@@ -2421,8 +2438,15 @@ impl<Signer: Sign> Channel<Signer> {
24212438
on_holder_tx_holding_cell_htlcs_count: 0,
24222439
};
24232440

2424-
let counterparty_dust_limit_success_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
2425-
let holder_dust_limit_timeout_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
2441+
let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.opt_anchors() {
2442+
(0, 0)
2443+
} else {
2444+
let dust_buffer_feerate = self.get_dust_buffer_feerate(outbound_feerate_update) as u64;
2445+
(dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000,
2446+
dust_buffer_feerate * htlc_success_tx_weight(false) / 1000)
2447+
};
2448+
let counterparty_dust_limit_success_sat = htlc_success_dust_limit + self.counterparty_dust_limit_satoshis;
2449+
let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
24262450
for ref htlc in self.pending_outbound_htlcs.iter() {
24272451
stats.pending_htlcs_value_msat += htlc.amount_msat;
24282452
if htlc.amount_msat / 1000 < counterparty_dust_limit_success_sat {
@@ -2512,8 +2536,14 @@ impl<Signer: Sign> Channel<Signer> {
25122536
fn next_local_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 {
25132537
assert!(self.is_outbound());
25142538

2515-
let real_dust_limit_success_sat = (self.feerate_per_kw as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
2516-
let real_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
2539+
let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.opt_anchors() {
2540+
(0, 0)
2541+
} else {
2542+
(self.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000,
2543+
self.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000)
2544+
};
2545+
let real_dust_limit_success_sat = htlc_success_dust_limit + self.holder_dust_limit_satoshis;
2546+
let real_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
25172547

25182548
let mut addl_htlcs = 0;
25192549
if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
@@ -2603,8 +2633,14 @@ impl<Signer: Sign> Channel<Signer> {
26032633
fn next_remote_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 {
26042634
assert!(!self.is_outbound());
26052635

2606-
let real_dust_limit_success_sat = (self.feerate_per_kw as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
2607-
let real_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
2636+
let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.opt_anchors() {
2637+
(0, 0)
2638+
} else {
2639+
(self.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000,
2640+
self.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000)
2641+
};
2642+
let real_dust_limit_success_sat = htlc_success_dust_limit + self.counterparty_dust_limit_satoshis;
2643+
let real_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.counterparty_dust_limit_satoshis;
26082644

26092645
let mut addl_htlcs = 0;
26102646
if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
@@ -2727,7 +2763,14 @@ impl<Signer: Sign> Channel<Signer> {
27272763
}
27282764
}
27292765

2730-
let exposure_dust_limit_timeout_sats = (self.get_dust_buffer_feerate(None) as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
2766+
let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.opt_anchors() {
2767+
(0, 0)
2768+
} else {
2769+
let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
2770+
(dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000,
2771+
dust_buffer_feerate * htlc_success_tx_weight(false) / 1000)
2772+
};
2773+
let exposure_dust_limit_timeout_sats = htlc_timeout_dust_limit + self.counterparty_dust_limit_satoshis;
27312774
if msg.amount_msat / 1000 < exposure_dust_limit_timeout_sats {
27322775
let on_counterparty_tx_dust_htlc_exposure_msat = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat + msg.amount_msat;
27332776
if on_counterparty_tx_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -2737,7 +2780,7 @@ impl<Signer: Sign> Channel<Signer> {
27372780
}
27382781
}
27392782

2740-
let exposure_dust_limit_success_sats = (self.get_dust_buffer_feerate(None) as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
2783+
let exposure_dust_limit_success_sats = htlc_success_dust_limit + self.holder_dust_limit_satoshis;
27412784
if msg.amount_msat / 1000 < exposure_dust_limit_success_sats {
27422785
let on_holder_tx_dust_htlc_exposure_msat = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat + msg.amount_msat;
27432786
if on_holder_tx_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -5444,7 +5487,14 @@ impl<Signer: Sign> Channel<Signer> {
54445487
}
54455488
}
54465489

5447-
let exposure_dust_limit_success_sats = (self.get_dust_buffer_feerate(None) as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
5490+
let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.opt_anchors() {
5491+
(0, 0)
5492+
} else {
5493+
let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
5494+
(dust_buffer_feerate * htlc_success_tx_weight(false) / 1000,
5495+
dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000)
5496+
};
5497+
let exposure_dust_limit_success_sats = htlc_success_dust_limit + self.counterparty_dust_limit_satoshis;
54485498
if amount_msat / 1000 < exposure_dust_limit_success_sats {
54495499
let on_counterparty_dust_htlc_exposure_msat = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat + amount_msat;
54505500
if on_counterparty_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -5453,7 +5503,7 @@ impl<Signer: Sign> Channel<Signer> {
54535503
}
54545504
}
54555505

5456-
let exposure_dust_limit_timeout_sats = (self.get_dust_buffer_feerate(None) as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
5506+
let exposure_dust_limit_timeout_sats = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
54575507
if amount_msat / 1000 < exposure_dust_limit_timeout_sats {
54585508
let on_holder_dust_htlc_exposure_msat = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat + amount_msat;
54595509
if on_holder_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {

lightning/src/util/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,12 @@ pub struct ChannelConfig {
325325
/// to such payments may be sustantial if there are many dust HTLCs present when the
326326
/// channel is force-closed.
327327
///
328+
/// The dust threshold for each HTLC is based on the `dust_limit_satoshis` for each party in a
329+
/// channel negotiated throughout the channel open process, along with the fees required to have
330+
/// a broadcastable HTLC spending transaction. When a channel supports anchor outputs
331+
/// (specifically the zero fee HTLC transaction variant), this threshold no longer takes into
332+
/// account the HTLC transaction fee as it is zero.
333+
///
328334
/// This limit is applied for sent, forwarded, and received HTLCs and limits the total
329335
/// exposure across all three types per-channel. Setting this too low may prevent the
330336
/// sending or receipt of low-value HTLCs on high-traffic nodes, and this limit is very

0 commit comments

Comments
 (0)