Skip to content

Commit 1cf2b53

Browse files
author
Antoine Riard
committed
Enforce max_balance_dust_htlc_msat at HTLC reception/forward
At `update_add_htlc()`/`send_htlc()`, we verify that the inbound/ outbound dust or the sum of both, on either sides of the link isn't above new config setting `max_balance_dust_htlc_msat`. A dust HTLC is hence defined as a trimmed-to-dust one, i.e including the fee cost to publish its claiming transaction.
1 parent 29e755b commit 1cf2b53

File tree

3 files changed

+42
-0
lines changed

3 files changed

+42
-0
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ fn check_api_err(api_err: APIError) {
244244
_ if err.starts_with("Cannot send value that would put counterparty balance under holder-announced channel reserve value") => {},
245245
_ if err.starts_with("Cannot send value that would overdraw remaining funds.") => {},
246246
_ if err.starts_with("Cannot send value that would not leave enough to pay for fees.") => {},
247+
_ if err.starts_with("Cannot send value that would put our exposure to dust HTLCs at") => {},
247248
_ => panic!("{}", err),
248249
}
249250
},

lightning/src/ln/channel.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,6 +2138,26 @@ impl<Signer: Sign> Channel<Signer> {
21382138
}
21392139
}
21402140

2141+
let exposure_dust_limit_timeout_sats = (self.get_dust_buffer_feerate() as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
2142+
if msg.amount_msat / 1000 < exposure_dust_limit_timeout_sats {
2143+
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;
2144+
if on_counterparty_tx_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
2145+
log_info!(logger, "Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx",
2146+
on_counterparty_tx_dust_htlc_exposure_msat, self.get_max_dust_htlc_exposure_msat());
2147+
pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x1000|7);
2148+
}
2149+
}
2150+
2151+
let exposure_dust_limit_success_sats = (self.get_dust_buffer_feerate() as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
2152+
if msg.amount_msat / 1000 < exposure_dust_limit_success_sats {
2153+
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;
2154+
if on_holder_tx_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
2155+
log_info!(logger, "Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx",
2156+
on_holder_tx_dust_htlc_exposure_msat, self.get_max_dust_htlc_exposure_msat());
2157+
pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x1000|7);
2158+
}
2159+
}
2160+
21412161
let pending_value_to_self_msat =
21422162
self.value_to_self_msat + inbound_stats.pending_htlcs_value_msat - removed_outbound_total_msat;
21432163
let pending_remote_value_msat =
@@ -4219,6 +4239,24 @@ impl<Signer: Sign> Channel<Signer> {
42194239
}
42204240
}
42214241

4242+
let exposure_dust_limit_success_sats = (self.get_dust_buffer_feerate() as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
4243+
if amount_msat / 1000 < exposure_dust_limit_success_sats {
4244+
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;
4245+
if on_counterparty_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
4246+
return Err(ChannelError::Ignore(format!("Cannot send value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx",
4247+
on_counterparty_dust_htlc_exposure_msat, self.get_max_dust_htlc_exposure_msat())));
4248+
}
4249+
}
4250+
4251+
let exposure_dust_limit_timeout_sats = (self.get_dust_buffer_feerate() as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
4252+
if amount_msat / 1000 < exposure_dust_limit_timeout_sats {
4253+
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;
4254+
if on_holder_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
4255+
return Err(ChannelError::Ignore(format!("Cannot send value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx",
4256+
on_holder_dust_htlc_exposure_msat, self.get_max_dust_htlc_exposure_msat())));
4257+
}
4258+
}
4259+
42224260
let pending_value_to_self_msat = self.value_to_self_msat - outbound_stats.pending_htlcs_value_msat;
42234261
if pending_value_to_self_msat < amount_msat {
42244262
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)));

lightning/src/ln/functional_test_utils.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,9 @@ pub fn test_default_channel_config() -> UserConfig {
13841384
// When most of our tests were written, the default HTLC minimum was fixed at 1000.
13851385
// It now defaults to 1, so we simply set it to the expected value here.
13861386
default_config.own_channel_config.our_htlc_minimum_msat = 1000;
1387+
// When most of our tests were written, we didn't have the notion of a `max_dust_htlc_exposure_msat`,
1388+
// It now defaults to 5_000_000 msat; to avoid interfering with tests we bump it to 50_000_000 msat.
1389+
default_config.channel_options.max_dust_htlc_exposure_msat = 50_000_000;
13871390
default_config
13881391
}
13891392

0 commit comments

Comments
 (0)