Skip to content

Commit e90d524

Browse files
committed
Move the calculation of the spiked feerate to tx_builder
In the next commit, we will make changes to how the fee spike buffer is calculated which require the real feerate to always be passed to `tx_builder::get_next_commitment_stats`, even in the case where we include a fee spike multiple.
1 parent df624db commit e90d524

2 files changed

Lines changed: 50 additions & 41 deletions

File tree

lightning/src/ln/channel.rs

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4198,6 +4198,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
41984198
include_counterparty_unknown_htlcs,
41994199
addl_nondust_htlc_count,
42004200
channel_context.feerate_per_kw,
4201+
false,
42014202
dust_exposure_limiting_feerate,
42024203
)
42034204
.map_err(|()| {
@@ -4494,6 +4495,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
44944495
include_counterparty_unknown_htlcs,
44954496
addl_nondust_htlc_count,
44964497
channel_context.feerate_per_kw,
4498+
false,
44974499
dust_exposure_limiting_feerate,
44984500
)
44994501
.map_err(|()| APIError::APIMisuseError {
@@ -5284,7 +5286,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
52845286
fn get_next_local_commitment_stats(
52855287
&self, funding: &FundingScope, htlc_candidate: Option<HTLCAmountDirection>,
52865288
include_counterparty_unknown_htlcs: bool, addl_nondust_htlc_count: usize,
5287-
feerate_per_kw: u32, dust_exposure_limiting_feerate: Option<u32>,
5289+
feerate_per_kw: u32, assume_fee_spike: bool, dust_exposure_limiting_feerate: Option<u32>,
52885290
) -> Result<(ChannelStats, Vec<HTLCAmountDirection>), ()> {
52895291
let next_commitment_htlcs = self.get_next_commitment_htlcs(
52905292
true,
@@ -5306,6 +5308,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
53065308
&next_commitment_htlcs,
53075309
addl_nondust_htlc_count,
53085310
feerate_per_kw,
5311+
assume_fee_spike,
53095312
dust_exposure_limiting_feerate,
53105313
max_dust_htlc_exposure_msat,
53115314
channel_constraints,
@@ -5330,6 +5333,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
53305333
&next_commitment_htlcs,
53315334
0,
53325335
feerate_per_kw,
5336+
false,
53335337
dust_exposure_limiting_feerate,
53345338
max_dust_htlc_exposure_msat,
53355339
channel_constraints,
@@ -5351,7 +5355,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
53515355
fn get_next_remote_commitment_stats(
53525356
&self, funding: &FundingScope, htlc_candidate: Option<HTLCAmountDirection>,
53535357
include_counterparty_unknown_htlcs: bool, addl_nondust_htlc_count: usize,
5354-
feerate_per_kw: u32, dust_exposure_limiting_feerate: Option<u32>,
5358+
feerate_per_kw: u32, assume_fee_spike: bool, dust_exposure_limiting_feerate: Option<u32>,
53555359
) -> Result<(ChannelStats, Vec<HTLCAmountDirection>), ()> {
53565360
let next_commitment_htlcs = self.get_next_commitment_htlcs(
53575361
false,
@@ -5373,6 +5377,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
53735377
&next_commitment_htlcs,
53745378
addl_nondust_htlc_count,
53755379
feerate_per_kw,
5380+
assume_fee_spike,
53765381
dust_exposure_limiting_feerate,
53775382
max_dust_htlc_exposure_msat,
53785383
channel_constraints,
@@ -5397,6 +5402,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
53975402
&next_commitment_htlcs,
53985403
0,
53995404
feerate_per_kw,
5405+
false,
54005406
dust_exposure_limiting_feerate,
54015407
max_dust_htlc_exposure_msat,
54025408
channel_constraints,
@@ -5439,6 +5445,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
54395445
include_counterparty_unknown_htlcs,
54405446
fee_spike_buffer_htlc,
54415447
self.feerate_per_kw,
5448+
false,
54425449
dust_exposure_limiting_feerate,
54435450
)
54445451
.map_err(|()| {
@@ -5497,6 +5504,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
54975504
include_counterparty_unknown_htlcs,
54985505
fee_spike_buffer_htlc,
54995506
self.feerate_per_kw,
5507+
false,
55005508
dust_exposure_limiting_feerate,
55015509
)
55025510
.map_err(|()| {
@@ -5523,6 +5531,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
55235531
include_counterparty_unknown_htlcs,
55245532
0,
55255533
new_feerate_per_kw,
5534+
false,
55265535
dust_exposure_limiting_feerate,
55275536
)
55285537
.map_err(|()| {
@@ -5544,6 +5553,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
55445553
include_counterparty_unknown_htlcs,
55455554
0,
55465555
new_feerate_per_kw,
5556+
false,
55475557
dust_exposure_limiting_feerate,
55485558
)
55495559
.map_err(|()| {
@@ -5724,6 +5734,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
57245734
include_counterparty_unknown_htlcs,
57255735
CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize,
57265736
feerate_per_kw,
5737+
false,
57275738
dust_exposure_limiting_feerate,
57285739
) {
57295740
stats
@@ -5763,6 +5774,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
57635774
include_counterparty_unknown_htlcs,
57645775
CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize,
57655776
feerate_per_kw,
5777+
false,
57665778
dust_exposure_limiting_feerate,
57675779
) {
57685780
stats
@@ -5810,6 +5822,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
58105822
include_counterparty_unknown_htlcs,
58115823
fee_spike_buffer_htlc,
58125824
feerate,
5825+
false,
58135826
dust_exposure_limiting_feerate,
58145827
)
58155828
.map_err(|()| {
@@ -5826,6 +5839,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
58265839
include_counterparty_unknown_htlcs,
58275840
fee_spike_buffer_htlc,
58285841
feerate,
5842+
false,
58295843
dust_exposure_limiting_feerate,
58305844
)
58315845
.map_err(|()| {
@@ -5862,21 +5876,14 @@ impl<SP: SignerProvider> ChannelContext<SP> {
58625876
if !funding.is_outbound() {
58635877
// Note that with anchor outputs we are no longer as sensitive to fee spikes, so we don't need
58645878
// to account for them.
5865-
let fee_spike_multiple =
5866-
if !funding.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
5867-
FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32
5868-
} else {
5869-
1
5870-
};
5871-
// Note that the feerate is 0 in zero-fee commitment channels, so this statement is a noop
5872-
let spiked_feerate = feerate.saturating_mul(fee_spike_multiple);
58735879
let (remote_stats, _remote_htlcs) = self
58745880
.get_next_remote_commitment_stats(
58755881
funding,
58765882
None,
58775883
include_counterparty_unknown_htlcs,
58785884
fee_spike_buffer_htlc,
5879-
spiked_feerate,
5885+
feerate,
5886+
true,
58805887
dust_exposure_limiting_feerate,
58815888
)
58825889
.map_err(|()| {
@@ -6231,6 +6238,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
62316238
include_counterparty_unknown_htlcs,
62326239
addl_nondust_htlc_count,
62336240
self.feerate_per_kw,
6241+
false,
62346242
dust_exposure_limiting_feerate,
62356243
)
62366244
.map(|(remote_stats, _)| remote_stats.available_balances)?;
@@ -6252,6 +6260,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
62526260
include_counterparty_unknown_htlcs,
62536261
addl_nondust_htlc_count,
62546262
self.feerate_per_kw,
6263+
false,
62556264
dust_exposure_limiting_feerate,
62566265
)
62576266
.unwrap();
@@ -13372,16 +13381,6 @@ where
1337213381
// We are not interested in dust exposure
1337313382
let dust_exposure_limiting_feerate = None;
1337413383

13375-
// Note that the feerate is 0 in zero-fee commitment channels, so this statement is a noop
13376-
let feerate_per_kw = if !funding.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
13377-
// Similar to HTLC additions, require the funder to have enough funds reserved for
13378-
// fees such that the feerate can jump without rendering the channel useless.
13379-
let spike_mul = FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
13380-
self.context.feerate_per_kw.saturating_mul(spike_mul)
13381-
} else {
13382-
self.context.feerate_per_kw
13383-
};
13384-
1338513384
// Different dust limits on the local and remote commitments cause the commitment
1338613385
// transaction fee to be different depending on the commitment, so we grab the floor
1338713386
// of both balances across both commitments here.
@@ -13399,7 +13398,8 @@ where
1339913398
None, // htlc_candidate
1340013399
include_counterparty_unknown_htlcs,
1340113400
addl_nondust_htlc_count,
13402-
feerate_per_kw,
13401+
self.context.feerate_per_kw,
13402+
true,
1340313403
dust_exposure_limiting_feerate,
1340413404
)
1340513405
.map_err(|()| "Balance exhausted on local commitment")?;
@@ -13411,7 +13411,8 @@ where
1341113411
None, // htlc_candidate
1341213412
include_counterparty_unknown_htlcs,
1341313413
addl_nondust_htlc_count,
13414-
feerate_per_kw,
13414+
self.context.feerate_per_kw,
13415+
true,
1341513416
dust_exposure_limiting_feerate,
1341613417
)
1341713418
.map_err(|()| "Balance exhausted on remote commitment")?;
@@ -13451,6 +13452,7 @@ where
1345113452
include_counterparty_unknown_htlcs,
1345213453
0,
1345313454
self.context.feerate_per_kw,
13455+
false,
1345413456
dust_exposure_limiting_feerate,
1345513457
)
1345613458
.map_err(|()| "Balance exhausted on remote commitment")?;
@@ -17186,7 +17188,7 @@ mod tests {
1718617188
// Make sure when Node A calculates their local commitment transaction, none of the HTLCs pass
1718717189
// the dust limit check.
1718817190
let htlc_candidate = HTLCAmountDirection { amount_msat: htlc_amount_msat, outbound: true };
17189-
let local_commit_tx_fee = node_a_chan.context.get_next_local_commitment_stats(&node_a_chan.funding, Some(htlc_candidate), false, 0, node_a_chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
17191+
let local_commit_tx_fee = node_a_chan.context.get_next_local_commitment_stats(&node_a_chan.funding, Some(htlc_candidate), false, 0, node_a_chan.context.feerate_per_kw, false, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
1719017192
let local_commit_fee_0_htlcs = commit_tx_fee_sat(node_a_chan.context.feerate_per_kw, 0, node_a_chan.funding.get_channel_type()) * 1000;
1719117193
assert_eq!(local_commit_tx_fee, local_commit_fee_0_htlcs);
1719217194

@@ -17195,7 +17197,7 @@ mod tests {
1719517197
node_a_chan.funding.channel_transaction_parameters.is_outbound_from_holder = false;
1719617198
let remote_commit_fee_3_htlcs = commit_tx_fee_sat(node_a_chan.context.feerate_per_kw, 3, node_a_chan.funding.get_channel_type()) * 1000;
1719717199
let htlc_candidate = HTLCAmountDirection { amount_msat: htlc_amount_msat, outbound: true };
17198-
let remote_commit_tx_fee = node_a_chan.context.get_next_remote_commitment_stats(&node_a_chan.funding, Some(htlc_candidate), false, 0, node_a_chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
17200+
let remote_commit_tx_fee = node_a_chan.context.get_next_remote_commitment_stats(&node_a_chan.funding, Some(htlc_candidate), false, 0, node_a_chan.context.feerate_per_kw, false, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
1719917201
assert_eq!(remote_commit_tx_fee, remote_commit_fee_3_htlcs);
1720017202
}
1720117203

@@ -17230,27 +17232,27 @@ mod tests {
1723017232
// counted as dust when it shouldn't be.
1723117233
let htlc_amt_above_timeout = (htlc_timeout_tx_fee_sat + chan.context.holder_dust_limit_satoshis + 1) * 1000;
1723217234
let htlc_candidate = HTLCAmountDirection { amount_msat: htlc_amt_above_timeout, outbound: true };
17233-
let commitment_tx_fee = chan.context.get_next_local_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
17235+
let commitment_tx_fee = chan.context.get_next_local_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, false, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
1723417236
assert_eq!(commitment_tx_fee, commitment_tx_fee_1_htlc);
1723517237

1723617238
// If swapped: this HTLC would be counted as non-dust when it shouldn't be.
1723717239
let dust_htlc_amt_below_success = (htlc_success_tx_fee_sat + chan.context.holder_dust_limit_satoshis - 1) * 1000;
1723817240
let htlc_candidate = HTLCAmountDirection { amount_msat: dust_htlc_amt_below_success, outbound: false };
17239-
let commitment_tx_fee = chan.context.get_next_local_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
17241+
let commitment_tx_fee = chan.context.get_next_local_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, false, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
1724017242
assert_eq!(commitment_tx_fee, commitment_tx_fee_0_htlcs);
1724117243

1724217244
chan.funding.channel_transaction_parameters.is_outbound_from_holder = false;
1724317245

1724417246
// If swapped: this HTLC would be counted as non-dust when it shouldn't be.
1724517247
let dust_htlc_amt_above_timeout = (htlc_timeout_tx_fee_sat + chan.context.counterparty_dust_limit_satoshis + 1) * 1000;
1724617248
let htlc_candidate = HTLCAmountDirection { amount_msat: dust_htlc_amt_above_timeout, outbound: true };
17247-
let commitment_tx_fee = chan.context.get_next_remote_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
17249+
let commitment_tx_fee = chan.context.get_next_remote_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, false, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
1724817250
assert_eq!(commitment_tx_fee, commitment_tx_fee_0_htlcs);
1724917251

1725017252
// If swapped: this HTLC would be counted as dust when it shouldn't be.
1725117253
let htlc_amt_below_success = (htlc_success_tx_fee_sat + chan.context.counterparty_dust_limit_satoshis - 1) * 1000;
1725217254
let htlc_candidate = HTLCAmountDirection { amount_msat: htlc_amt_below_success, outbound: false };
17253-
let commitment_tx_fee = chan.context.get_next_remote_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
17255+
let commitment_tx_fee = chan.context.get_next_remote_commitment_stats(&chan.funding, Some(htlc_candidate), false, 0, chan.context.feerate_per_kw, false, None).unwrap().0.commitment_stats.commit_tx_fee_sat * 1000;
1725417256
assert_eq!(commitment_tx_fee, commitment_tx_fee_1_htlc);
1725517257
}
1725617258

lightning/src/sign/tx_builder.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::ln::chan_utils::{
1111
};
1212
use crate::ln::channel::{
1313
get_v2_channel_reserve_satoshis, CommitmentStats, ANCHOR_OUTPUT_VALUE_SATOSHI,
14-
MIN_CHANNEL_VALUE_SATOSHIS,
14+
FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_CHANNEL_VALUE_SATOSHIS,
1515
};
1616
use crate::prelude::*;
1717
use crate::types::features::ChannelTypeFeatures;
@@ -219,7 +219,7 @@ fn has_output(
219219
fn get_next_commitment_stats(
220220
local: bool, is_outbound_from_holder: bool, channel_value_satoshis: u64,
221221
value_to_holder_msat: u64, next_commitment_htlcs: &[HTLCAmountDirection],
222-
addl_nondust_htlc_count: usize, feerate_per_kw: u32,
222+
addl_nondust_htlc_count: usize, feerate_per_kw: u32, assume_fee_spike: bool,
223223
dust_exposure_limiting_feerate: Option<u32>, broadcaster_dust_limit_satoshis: u64,
224224
channel_type: &ChannelTypeFeatures,
225225
) -> Result<NextCommitmentStats, ()> {
@@ -270,11 +270,16 @@ fn get_next_commitment_stats(
270270
channel_type,
271271
);
272272

273-
// Calculate fees on commitment transaction
274-
let nondust_htlc_count = next_commitment_htlcs
273+
let spiked_feerate = if assume_fee_spike && !channel_type.supports_anchors_zero_fee_htlc_tx() {
274+
feerate_per_kw.saturating_mul(FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32)
275+
} else {
276+
feerate_per_kw
277+
};
278+
279+
let spiked_nondust_htlc_count = next_commitment_htlcs
275280
.iter()
276281
.filter(|htlc| {
277-
!htlc.is_dust(local, feerate_per_kw, broadcaster_dust_limit_satoshis, channel_type)
282+
!htlc.is_dust(local, spiked_feerate, broadcaster_dust_limit_satoshis, channel_type)
278283
})
279284
.count();
280285

@@ -284,8 +289,8 @@ fn get_next_commitment_stats(
284289
is_outbound_from_holder,
285290
holder_balance_before_fee_msat,
286291
counterparty_balance_before_fee_msat,
287-
feerate_per_kw,
288-
nondust_htlc_count,
292+
spiked_feerate,
293+
spiked_nondust_htlc_count,
289294
broadcaster_dust_limit_satoshis,
290295
channel_type,
291296
) {
@@ -296,8 +301,8 @@ fn get_next_commitment_stats(
296301
// this bigger transaction fee ? The funder can dip below their dust limit to cover this case, as the
297302
// commitment will have at least one output: the non-dust fee spike buffer HTLC offered by the counterparty.
298303
let commit_tx_fee_sat = commit_tx_fee_sat(
299-
feerate_per_kw,
300-
nondust_htlc_count + addl_nondust_htlc_count,
304+
spiked_feerate,
305+
spiked_nondust_htlc_count + addl_nondust_htlc_count,
301306
channel_type,
302307
);
303308
let (holder_balance_msat, counterparty_balance_msat) = checked_sub_from_funder(
@@ -312,7 +317,7 @@ fn get_next_commitment_stats(
312317
counterparty_balance_msat,
313318
dust_exposure_msat,
314319
#[cfg(any(test, fuzzing))]
315-
nondust_htlc_count: nondust_htlc_count + addl_nondust_htlc_count,
320+
nondust_htlc_count: spiked_nondust_htlc_count + addl_nondust_htlc_count,
316321
#[cfg(any(test, fuzzing))]
317322
commit_tx_fee_sat,
318323
})
@@ -802,7 +807,7 @@ pub(crate) trait TxBuilder {
802807
fn get_channel_stats(
803808
&self, local: bool, is_outbound_from_holder: bool, channel_value_satoshis: u64,
804809
value_to_holder_msat: u64, pending_htlcs: &[HTLCAmountDirection],
805-
addl_nondust_htlc_count: usize, feerate_per_kw: u32,
810+
addl_nondust_htlc_count: usize, feerate_per_kw: u32, assume_fee_spike: bool,
806811
dust_exposure_limiting_feerate: Option<u32>, max_dust_htlc_exposure_msat: u64,
807812
channel_constraints: ChannelConstraints, channel_type: &ChannelTypeFeatures,
808813
) -> Result<ChannelStats, ()>;
@@ -820,7 +825,7 @@ impl TxBuilder for SpecTxBuilder {
820825
fn get_channel_stats(
821826
&self, local: bool, is_outbound_from_holder: bool, channel_value_satoshis: u64,
822827
value_to_holder_msat: u64, pending_htlcs: &[HTLCAmountDirection],
823-
addl_nondust_htlc_count: usize, feerate_per_kw: u32,
828+
addl_nondust_htlc_count: usize, feerate_per_kw: u32, assume_fee_spike: bool,
824829
dust_exposure_limiting_feerate: Option<u32>, max_dust_htlc_exposure_msat: u64,
825830
channel_constraints: ChannelConstraints, channel_type: &ChannelTypeFeatures,
826831
) -> Result<ChannelStats, ()> {
@@ -833,6 +838,7 @@ impl TxBuilder for SpecTxBuilder {
833838
pending_htlcs,
834839
addl_nondust_htlc_count,
835840
feerate_per_kw,
841+
assume_fee_spike,
836842
dust_exposure_limiting_feerate,
837843
channel_constraints.holder_dust_limit_satoshis,
838844
channel_type,
@@ -846,6 +852,7 @@ impl TxBuilder for SpecTxBuilder {
846852
pending_htlcs,
847853
addl_nondust_htlc_count,
848854
feerate_per_kw,
855+
assume_fee_spike,
849856
dust_exposure_limiting_feerate,
850857
channel_constraints.counterparty_dust_limit_satoshis,
851858
channel_type,

0 commit comments

Comments
 (0)