Skip to content

Commit 0db41f0

Browse files
author
Antoine Riard
committed
-f Explain worst-case
1 parent 6643df5 commit 0db41f0

File tree

1 file changed

+36
-20
lines changed

1 file changed

+36
-20
lines changed

lightning/src/ln/channel.rs

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,13 +1757,13 @@ impl<Signer: Sign> Channel<Signer> {
17571757
let mut holder_dusted_htlc_msat = 0;
17581758

17591759
let counterparty_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
1760-
let holder_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
1760+
let holder_dust_limit_success_sat = (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
17611761
for ref htlc in self.pending_inbound_htlcs.iter() {
17621762
htlc_inbound_value_msat += htlc.amount_msat;
17631763
if htlc.amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
17641764
counterparty_dusted_htlc_msat += htlc.amount_msat + (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) * 1000;
17651765
}
1766-
if htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat {
1766+
if htlc.amount_msat / 1000 < holder_dust_limit_success_sat {
17671767
holder_dusted_htlc_msat += htlc.amount_msat + (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) * 1000;
17681768
}
17691769
}
@@ -1778,14 +1778,14 @@ impl<Signer: Sign> Channel<Signer> {
17781778
let mut holder_dusted_htlc_msat = 0;
17791779

17801780
let counterparty_dust_limit_success_sat = (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
1781-
let holder_dust_limit_success_sat = (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
1781+
let holder_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
17821782
for ref htlc in self.pending_outbound_htlcs.iter() {
17831783
htlc_outbound_value_msat += htlc.amount_msat;
17841784
if htlc.amount_msat / 1000 < counterparty_dust_limit_success_sat {
17851785
counterparty_dusted_htlc_msat += htlc.amount_msat + (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) * 1000;
17861786
}
1787-
if htlc.amount_msat / 1000 < holder_dust_limit_success_sat {
1788-
holder_dusted_htlc_msat += htlc.amount_msat + (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) * 1000;
1787+
if htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat {
1788+
holder_dusted_htlc_msat += htlc.amount_msat + (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) * 1000;
17891789
}
17901790
}
17911791

@@ -1797,7 +1797,7 @@ impl<Signer: Sign> Channel<Signer> {
17971797
if *amount_msat / 1000 < counterparty_dust_limit_success_sat {
17981798
counterparty_dusted_htlc_msat += amount_msat + (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) * 1000;
17991799
}
1800-
if *amount_msat / 1000 < holder_dust_limit_success_sat {
1800+
if *amount_msat / 1000 < holder_dust_limit_timeout_sat {
18011801
holder_dusted_htlc_msat += amount_msat + (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) * 1000;
18021802
}
18031803
}
@@ -2032,7 +2032,7 @@ impl<Signer: Sign> Channel<Signer> {
20322032
}
20332033

20342034
let (inbound_htlc_count, htlc_inbound_value_msat, on_counterparty_tx_dust_inbound, on_holder_tx_dust_inbound) = self.get_inbound_pending_htlc_stats();
2035-
let (_, _, on_counterparty_tx_dust_outbound, on_holder_tx_dust_outbound) = self.get_outbound_pending_htlc_stats();
2035+
let (outbound_htlc_count, _, on_counterparty_tx_dust_outbound, on_holder_tx_dust_outbound) = self.get_outbound_pending_htlc_stats();
20362036
if inbound_htlc_count + 1 > OUR_MAX_HTLCS as u32 {
20372037
return Err(ChannelError::Close(format!("Remote tried to push more than our max accepted HTLCs ({})", OUR_MAX_HTLCS)));
20382038
}
@@ -2061,20 +2061,35 @@ impl<Signer: Sign> Channel<Signer> {
20612061
}
20622062
}
20632063

2064+
#[cfg(any(test, feature = "fuzztarget"))]
2065+
{
2066+
// Holder dust balance on holder commitment transaction share the same safety
2067+
// risks though it's implictly bounded by the verification of the dust balance on
2068+
// the counterparty commitment transaction, with a delta due to the asymmetry
2069+
// of HTLC claim transaction weights.
2070+
//
2071+
// Assuming worst-case scenario of maximum number of inbound dust HTLCs,
2072+
// this delta is defined by := (N * `htlc_minimum_msat`) + (`feerate_per_kw` * HTLC_SUCCESS_TX_WEIGHT / 1000 * 1000)
2073+
// - (N * `htlc_minimum_msat`) + (`feerate_per_kw` * HTLC_TIMEOUT_TX_WEIGHT / 1000 * 1000)
2074+
//
2075+
// Where N is the number of pending inbound HTLCs. The HTLCs amounts are picked
2076+
// up to be equal to the `htlc_minimum_msat` as this is maxima of "trimmed-to-dust"
2077+
// weight difference between holder and counterparty transactions.
2078+
//
2079+
// With a default `max_balance_dust_htlc_msat`=5_000_000, N is at most 29.
2080+
//
2081+
// Assuming `feerate_per_kw`=253, solving the equation we find 344_000 millisatoshis.
2082+
assert_eq!(on_holder_tx_dust_inbound - inbound_htlc_count as u64 * (self.feerate_per_kw as u64 * (HTLC_SUCCESS_TX_WEIGHT - HTLC_TIMEOUT_TX_WEIGHT) / 1000 * 1000), on_counterparty_tx_dust_inbound);
2083+
assert_eq!(on_holder_tx_dust_outbound + outbound_htlc_count as u64 * (self.feerate_per_kw as u64 * (HTLC_SUCCESS_TX_WEIGHT - HTLC_TIMEOUT_TX_WEIGHT) / 1000 * 1000), on_counterparty_tx_dust_outbound);
2084+
}
2085+
20642086
if msg.amount_msat <= (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) * 1000 + self.counterparty_dust_limit_satoshis {
20652087
if on_counterparty_tx_dust_inbound + on_counterparty_tx_dust_outbound + msg.amount_msat + (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) * 1000 > self.get_max_balance_dust_htlc_msat() {
20662088
log_info!(logger, "Cannot accept value that would put holder dusted balance {} on counterparty commitment over limit {}", on_counterparty_tx_dust_inbound + on_counterparty_tx_dust_outbound, self.get_max_balance_dust_htlc_msat());
20672089
pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x1000|7);
20682090
}
20692091
}
20702092

2071-
if msg.amount_msat <= (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis {
2072-
if on_holder_tx_dust_inbound + on_holder_tx_dust_outbound + msg.amount_msat + (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) > self.get_max_balance_dust_htlc_msat() {
2073-
log_info!(logger, "Cannot accept value that would put holder dusted balance on holder commitment over limit {}", self.get_max_balance_dust_htlc_msat());
2074-
pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x1000|7);
2075-
}
2076-
}
2077-
20782093
let pending_value_to_self_msat =
20792094
self.value_to_self_msat + htlc_inbound_value_msat - removed_outbound_total_msat;
20802095
let pending_remote_value_msat =
@@ -4143,18 +4158,19 @@ impl<Signer: Sign> Channel<Signer> {
41434158
}
41444159
}
41454160

4161+
#[cfg(any(test, feature = "fuzztarget"))]
4162+
{
4163+
// See comment in `update_add_htlc()` for rational.
4164+
assert_eq!(on_holder_tx_dust_inbound - inbound_htlc_count as u64 * (self.feerate_per_kw as u64 * (HTLC_SUCCESS_TX_WEIGHT - HTLC_TIMEOUT_TX_WEIGHT) / 1000 * 1000), on_counterparty_tx_dust_inbound);
4165+
assert_eq!(on_holder_tx_dust_outbound + outbound_htlc_count as u64 * (self.feerate_per_kw as u64 * (HTLC_SUCCESS_TX_WEIGHT - HTLC_TIMEOUT_TX_WEIGHT) / 1000 * 1000), on_counterparty_tx_dust_outbound);
4166+
}
4167+
41464168
if amount_msat <= (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) * 1000 + self.counterparty_dust_limit_satoshis {
41474169
if on_counterparty_tx_dust_inbound + on_counterparty_tx_dust_outbound + amount_msat + (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) * 1000 > self.get_max_balance_dust_htlc_msat() {
41484170
return Err(ChannelError::Ignore(format!("Cannot send value that would put holder dusted balance {} on counterparty commitment over limit {}", on_counterparty_tx_dust_inbound + on_counterparty_tx_dust_outbound, self.get_max_balance_dust_htlc_msat())));
41494171
}
41504172
}
41514173

4152-
if amount_msat <= (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) * 1000 + self.holder_dust_limit_satoshis {
4153-
if on_holder_tx_dust_inbound + on_holder_tx_dust_outbound + amount_msat + (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) * 1000 > self.get_max_balance_dust_htlc_msat() {
4154-
return Err(ChannelError::Ignore(format!("Cannot send value that would put holder dusted balance on holder commitment over limit {}", self.get_max_balance_dust_htlc_msat())));
4155-
}
4156-
}
4157-
41584174
let pending_value_to_self_msat = self.value_to_self_msat - htlc_outbound_value_msat;
41594175
if pending_value_to_self_msat < amount_msat {
41604176
return Err(ChannelError::Ignore(format!("Cannot send value that would overdraw remaining funds. Amount: {}, pending value to self {}", amount_msat, pending_value_to_self_msat)));

0 commit comments

Comments
 (0)