Skip to content

Commit f742006

Browse files
author
Antoine Riard
committed
Check we won't overflow max_dust_htlc_exposure_msat at outbound feerate update
1 parent 33c47e1 commit f742006

File tree

1 file changed

+39
-24
lines changed

1 file changed

+39
-24
lines changed

lightning/src/ln/channel.rs

+39-24
Original file line numberDiff line numberDiff line change
@@ -1913,16 +1913,16 @@ impl<Signer: Sign> Channel<Signer> {
19131913
}
19141914

19151915
/// Returns a HTLCStats about inbound pending htlcs
1916-
fn get_inbound_pending_htlc_stats(&self) -> HTLCStats {
1916+
fn get_inbound_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>) -> HTLCStats {
19171917
let mut stats = HTLCStats {
19181918
pending_htlcs: self.pending_inbound_htlcs.len() as u32,
19191919
pending_htlcs_value_msat: 0,
19201920
on_counterparty_tx_dust_exposure_msat: 0,
19211921
on_holder_tx_dust_exposure_msat: 0,
19221922
};
19231923

1924-
let counterparty_dust_limit_timeout_sat = (self.get_dust_buffer_feerate() as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
1925-
let holder_dust_limit_success_sat = (self.get_dust_buffer_feerate() as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
1924+
let counterparty_dust_limit_timeout_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
1925+
let holder_dust_limit_success_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
19261926
for ref htlc in self.pending_inbound_htlcs.iter() {
19271927
stats.pending_htlcs_value_msat += htlc.amount_msat;
19281928
if htlc.amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
@@ -1936,16 +1936,16 @@ impl<Signer: Sign> Channel<Signer> {
19361936
}
19371937

19381938
/// Returns a HTLCStats about pending outbound htlcs, *including* pending adds in our holding cell.
1939-
fn get_outbound_pending_htlc_stats(&self) -> HTLCStats {
1939+
fn get_outbound_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>) -> HTLCStats {
19401940
let mut stats = HTLCStats {
19411941
pending_htlcs: self.pending_outbound_htlcs.len() as u32,
19421942
pending_htlcs_value_msat: 0,
19431943
on_counterparty_tx_dust_exposure_msat: 0,
19441944
on_holder_tx_dust_exposure_msat: 0,
19451945
};
19461946

1947-
let counterparty_dust_limit_success_sat = (self.get_dust_buffer_feerate() as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
1948-
let holder_dust_limit_timeout_sat = (self.get_dust_buffer_feerate() as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
1947+
let counterparty_dust_limit_success_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
1948+
let holder_dust_limit_timeout_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
19491949
for ref htlc in self.pending_outbound_htlcs.iter() {
19501950
stats.pending_htlcs_value_msat += htlc.amount_msat;
19511951
if htlc.amount_msat / 1000 < counterparty_dust_limit_success_sat {
@@ -1980,11 +1980,11 @@ impl<Signer: Sign> Channel<Signer> {
19801980
(
19811981
cmp::max(self.channel_value_satoshis as i64 * 1000
19821982
- self.value_to_self_msat as i64
1983-
- self.get_inbound_pending_htlc_stats().pending_htlcs_value_msat as i64
1983+
- self.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
19841984
- Self::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis) as i64 * 1000,
19851985
0) as u64,
19861986
cmp::max(self.value_to_self_msat as i64
1987-
- self.get_outbound_pending_htlc_stats().pending_htlcs_value_msat as i64
1987+
- self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
19881988
- self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
19891989
0) as u64
19901990
)
@@ -2203,8 +2203,8 @@ impl<Signer: Sign> Channel<Signer> {
22032203
return Err(ChannelError::Close(format!("Remote side tried to send less than our minimum HTLC value. Lower limit: ({}). Actual: ({})", self.holder_htlc_minimum_msat, msg.amount_msat)));
22042204
}
22052205

2206-
let inbound_stats = self.get_inbound_pending_htlc_stats();
2207-
let outbound_stats = self.get_outbound_pending_htlc_stats();
2206+
let inbound_stats = self.get_inbound_pending_htlc_stats(None);
2207+
let outbound_stats = self.get_outbound_pending_htlc_stats(None);
22082208
if inbound_stats.pending_htlcs + 1 > OUR_MAX_HTLCS as u32 {
22092209
return Err(ChannelError::Close(format!("Remote tried to push more than our max accepted HTLCs ({})", OUR_MAX_HTLCS)));
22102210
}
@@ -2233,7 +2233,7 @@ impl<Signer: Sign> Channel<Signer> {
22332233
}
22342234
}
22352235

2236-
let exposure_dust_limit_timeout_sats = (self.get_dust_buffer_feerate() as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
2236+
let exposure_dust_limit_timeout_sats = (self.get_dust_buffer_feerate(None) as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
22372237
if msg.amount_msat / 1000 < exposure_dust_limit_timeout_sats {
22382238
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;
22392239
if on_counterparty_tx_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -2243,7 +2243,7 @@ impl<Signer: Sign> Channel<Signer> {
22432243
}
22442244
}
22452245

2246-
let exposure_dust_limit_success_sats = (self.get_dust_buffer_feerate() as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
2246+
let exposure_dust_limit_success_sats = (self.get_dust_buffer_feerate(None) as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
22472247
if msg.amount_msat / 1000 < exposure_dust_limit_success_sats {
22482248
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;
22492249
if on_holder_tx_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -2977,8 +2977,8 @@ impl<Signer: Sign> Channel<Signer> {
29772977
}
29782978

29792979
// Before proposing a feerate update, check that we can actually afford the new fee.
2980-
let inbound_stats = self.get_inbound_pending_htlc_stats();
2981-
let outbound_stats = self.get_outbound_pending_htlc_stats();
2980+
let inbound_stats = self.get_inbound_pending_htlc_stats(Some(feerate_per_kw));
2981+
let outbound_stats = self.get_outbound_pending_htlc_stats(Some(feerate_per_kw));
29822982
// In case of a concurrent update_add_htlc proposed by our counterparty, we might
29832983
// not have enough balance value remaining to cover the onchain cost of this new
29842984
// HTLC weight. If this happens, our counterparty fails the reception of our
@@ -2996,6 +2996,18 @@ impl<Signer: Sign> Channel<Signer> {
29962996
return None;
29972997
}
29982998

2999+
if feerate_per_kw > self.get_dust_buffer_feerate(None) {
3000+
// Note, we evaluate pending htlc "preemptive" trimmed-to-dust threshold at the proposed `feerate_per_kw`.
3001+
let holder_tx_dust_exposure = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat;
3002+
let counterparty_tx_dust_exposure = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat;
3003+
if holder_tx_dust_exposure > self.get_max_dust_htlc_exposure_msat() {
3004+
return None;
3005+
}
3006+
if counterparty_tx_dust_exposure > self.get_max_dust_htlc_exposure_msat() {
3007+
return None;
3008+
}
3009+
}
3010+
29993011
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::MonitorUpdateFailed as u32)) != 0 {
30003012
self.holding_cell_update_fee = Some(feerate_per_kw);
30013013
return None;
@@ -3163,16 +3175,16 @@ impl<Signer: Sign> Channel<Signer> {
31633175
return Err(ChannelError::Close("Peer sent update_fee when we needed a channel_reestablish".to_owned()));
31643176
}
31653177
Channel::<Signer>::check_remote_fee(fee_estimator, msg.feerate_per_kw)?;
3166-
let feerate_over_dust_buffer = msg.feerate_per_kw > self.get_dust_buffer_feerate();
3178+
let feerate_over_dust_buffer = msg.feerate_per_kw > self.get_dust_buffer_feerate(None);
31673179

31683180
self.pending_update_fee = Some((msg.feerate_per_kw, FeeUpdateState::RemoteAnnounced));
31693181
self.update_time_counter += 1;
31703182
// If the feerate has increased over the previous dust buffer (note that
31713183
// `get_dust_buffer_feerate` considers the `pending_update_fee` status), check that we
31723184
// won't be pushed over our dust exposure limit by the feerate increase.
31733185
if feerate_over_dust_buffer {
3174-
let inbound_stats = self.get_inbound_pending_htlc_stats();
3175-
let outbound_stats = self.get_outbound_pending_htlc_stats();
3186+
let inbound_stats = self.get_inbound_pending_htlc_stats(None);
3187+
let outbound_stats = self.get_outbound_pending_htlc_stats(None);
31763188
let holder_tx_dust_exposure = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat;
31773189
let counterparty_tx_dust_exposure = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat;
31783190
if holder_tx_dust_exposure > self.get_max_dust_htlc_exposure_msat() {
@@ -3873,7 +3885,7 @@ impl<Signer: Sign> Channel<Signer> {
38733885
self.feerate_per_kw
38743886
}
38753887

3876-
pub fn get_dust_buffer_feerate(&self) -> u32 {
3888+
pub fn get_dust_buffer_feerate(&self, outbound_feerate_update: Option<u32>) -> u32 {
38773889
// When calculating our exposure to dust HTLCs, we assume that the channel feerate
38783890
// may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
38793891
// whichever is higher. This ensures that we aren't suddenly exposed to significantly
@@ -3885,6 +3897,9 @@ impl<Signer: Sign> Channel<Signer> {
38853897
if let Some((feerate, _)) = self.pending_update_fee {
38863898
feerate_per_kw = cmp::max(feerate_per_kw, feerate);
38873899
}
3900+
if let Some(feerate) = outbound_feerate_update {
3901+
feerate_per_kw = cmp::max(feerate_per_kw, feerate);
3902+
}
38883903
cmp::max(2530, feerate_per_kw * 1250 / 1000)
38893904
}
38903905

@@ -4542,8 +4557,8 @@ impl<Signer: Sign> Channel<Signer> {
45424557
return Err(ChannelError::Ignore("Cannot send an HTLC while disconnected from channel counterparty".to_owned()));
45434558
}
45444559

4545-
let inbound_stats = self.get_inbound_pending_htlc_stats();
4546-
let outbound_stats = self.get_outbound_pending_htlc_stats();
4560+
let inbound_stats = self.get_inbound_pending_htlc_stats(None);
4561+
let outbound_stats = self.get_outbound_pending_htlc_stats(None);
45474562
if outbound_stats.pending_htlcs + 1 > self.counterparty_max_accepted_htlcs as u32 {
45484563
return Err(ChannelError::Ignore(format!("Cannot push more than their max accepted HTLCs ({})", self.counterparty_max_accepted_htlcs)));
45494564
}
@@ -4563,7 +4578,7 @@ impl<Signer: Sign> Channel<Signer> {
45634578
}
45644579
}
45654580

4566-
let exposure_dust_limit_success_sats = (self.get_dust_buffer_feerate() as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
4581+
let exposure_dust_limit_success_sats = (self.get_dust_buffer_feerate(None) as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
45674582
if amount_msat / 1000 < exposure_dust_limit_success_sats {
45684583
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;
45694584
if on_counterparty_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -4572,7 +4587,7 @@ impl<Signer: Sign> Channel<Signer> {
45724587
}
45734588
}
45744589

4575-
let exposure_dust_limit_timeout_sats = (self.get_dust_buffer_feerate() as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
4590+
let exposure_dust_limit_timeout_sats = (self.get_dust_buffer_feerate(None) as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
45764591
if amount_msat / 1000 < exposure_dust_limit_timeout_sats {
45774592
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;
45784593
if on_holder_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -4754,13 +4769,13 @@ impl<Signer: Sign> Channel<Signer> {
47544769
}
47554770
}
47564771
}
4757-
4772+
47584773
if self.is_outbound() {
47594774
// Log if we can't afford next remote commitment tx fee at pending outbound feerate update.
47604775
if let Some(pending_feerate) = self.pending_update_fee {
47614776
assert_eq!(pending_feerate.1, FeeUpdateState::Outbound);
47624777
let next_total_fee = Channel::<Signer>::commit_tx_fee_sat(pending_feerate.0, self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len());
4763-
let outbound_stats = self.get_outbound_pending_htlc_stats();
4778+
let outbound_stats = self.get_outbound_pending_htlc_stats(Some(pending_feerate.0));
47644779
let holder_balance_after_fee_sub_sats = (self.value_to_self_msat / 1000).checked_sub(next_total_fee).map(|a| a.checked_sub(outbound_stats.pending_htlcs_value_msat / 1000)).unwrap_or(None).unwrap_or(u64::max_value());
47654780
if holder_balance_after_fee_sub_sats < self.counterparty_selected_channel_reserve_satoshis.unwrap() {
47664781
log_trace!(logger, "Outbound update_fee HTLC buffer overflow - counterparty should force-close this channel");

0 commit comments

Comments
 (0)