Skip to content

Commit f399c91

Browse files
committed
Add TxBuilder::commit_tx_fee_sat
When the `TxBuilder` API is made public in the future, this API will let creators of custom commitment transactions tell the lightning state machine the fees required to set a given feerate on a commitment transaction with a given number of nondust HTLCs. In upcoming commits, we plan to expand this call with the full list of HTLCs and let `TxBuilder` decide which HTLCs are dust, and which are nondust.
1 parent aacaf77 commit f399c91

File tree

4 files changed

+59
-33
lines changed

4 files changed

+59
-33
lines changed

lightning/src/ln/chan_utils.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,16 +234,14 @@ pub(crate) fn commit_tx_fee_sat(feerate_per_kw: u32, num_htlcs: usize, channel_t
234234
}
235235

236236
#[rustfmt::skip]
237-
pub(crate) fn commit_and_htlc_tx_fees_sat(feerate_per_kw: u32, num_accepted_htlcs: usize, num_offered_htlcs: usize, channel_type_features: &ChannelTypeFeatures) -> u64 {
238-
let num_htlcs = num_accepted_htlcs + num_offered_htlcs;
239-
let commit_tx_fees_sat = commit_tx_fee_sat(feerate_per_kw, num_htlcs, channel_type_features);
237+
pub(crate) fn htlc_tx_fees_sat(feerate_per_kw: u32, num_accepted_htlcs: usize, num_offered_htlcs: usize, channel_type_features: &ChannelTypeFeatures) -> u64 {
240238
let htlc_tx_fees_sat = if !channel_type_features.supports_anchors_zero_fee_htlc_tx() {
241239
num_accepted_htlcs as u64 * htlc_success_tx_weight(channel_type_features) * feerate_per_kw as u64 / 1000
242240
+ num_offered_htlcs as u64 * htlc_timeout_tx_weight(channel_type_features) * feerate_per_kw as u64 / 1000
243241
} else {
244242
0
245243
};
246-
commit_tx_fees_sat + htlc_tx_fees_sat
244+
htlc_tx_fees_sat
247245
}
248246

249247
// Various functions for key derivation and transaction creation for use within channels. Primarily

lightning/src/ln/channel.rs

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use crate::ln::chan_utils;
4040
#[cfg(splicing)]
4141
use crate::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT;
4242
use crate::ln::chan_utils::{
43-
commit_tx_fee_sat, get_commitment_transaction_number_obscure_factor, htlc_success_tx_weight,
43+
get_commitment_transaction_number_obscure_factor, htlc_success_tx_weight,
4444
htlc_timeout_tx_weight, max_htlcs, ChannelPublicKeys, ChannelTransactionParameters,
4545
ClosingTransaction, CommitmentTransaction, CounterpartyChannelTransactionParameters,
4646
CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HolderCommitmentTransaction,
@@ -69,6 +69,7 @@ use crate::ln::script::{self, ShutdownScript};
6969
use crate::ln::types::ChannelId;
7070
use crate::routing::gossip::NodeId;
7171
use crate::sign::ecdsa::EcdsaChannelSigner;
72+
use crate::sign::tx_builder::{SpecTxBuilder, TxBuilder};
7273
use crate::sign::{ChannelSigner, EntropySource, NodeSigner, Recipient, SignerProvider};
7374
use crate::types::features::{ChannelTypeFeatures, InitFeatures};
7475
use crate::types::payment::{PaymentHash, PaymentPreimage};
@@ -3124,12 +3125,12 @@ where
31243125
0
31253126
};
31263127
let funders_amount_msat = open_channel_fields.funding_satoshis * 1000 - msg_push_msat;
3127-
let commitment_tx_fee = commit_tx_fee_sat(open_channel_fields.commitment_feerate_sat_per_1000_weight, MIN_AFFORDABLE_HTLC_COUNT, &channel_type);
3128-
if (funders_amount_msat / 1000).saturating_sub(anchor_outputs_value) < commitment_tx_fee {
3129-
return Err(ChannelError::close(format!("Funding amount ({} sats) can't even pay fee for initial commitment transaction fee of {} sats.", (funders_amount_msat / 1000).saturating_sub(anchor_outputs_value), commitment_tx_fee)));
3128+
let commit_tx_fee_sat = SpecTxBuilder {}.commit_tx_fee_sat(open_channel_fields.commitment_feerate_sat_per_1000_weight, MIN_AFFORDABLE_HTLC_COUNT, &channel_type);
3129+
if (funders_amount_msat / 1000).saturating_sub(anchor_outputs_value) < commit_tx_fee_sat {
3130+
return Err(ChannelError::close(format!("Funding amount ({} sats) can't even pay fee for initial commitment transaction fee of {} sats.", (funders_amount_msat / 1000).saturating_sub(anchor_outputs_value), commit_tx_fee_sat)));
31303131
}
31313132

3132-
let to_remote_satoshis = funders_amount_msat / 1000 - commitment_tx_fee - anchor_outputs_value;
3133+
let to_remote_satoshis = funders_amount_msat / 1000 - commit_tx_fee_sat - anchor_outputs_value;
31333134
// While it's reasonable for us to not meet the channel reserve initially (if they don't
31343135
// want to push much to us), our counterparty should always have more than our reserve.
31353136
if to_remote_satoshis < holder_selected_channel_reserve_satoshis {
@@ -3402,9 +3403,9 @@ where
34023403
};
34033404

34043405
let value_to_self_msat = channel_value_satoshis * 1000 - push_msat;
3405-
let commitment_tx_fee = commit_tx_fee_sat(commitment_feerate, MIN_AFFORDABLE_HTLC_COUNT, &channel_type) * 1000;
3406-
if value_to_self_msat.saturating_sub(anchor_outputs_value_msat) < commitment_tx_fee {
3407-
return Err(APIError::APIMisuseError{ err: format!("Funding amount ({}) can't even pay fee for initial commitment transaction fee of {}.", value_to_self_msat / 1000, commitment_tx_fee / 1000) });
3406+
let commit_tx_fee_sat = SpecTxBuilder {}.commit_tx_fee_sat(commitment_feerate, MIN_AFFORDABLE_HTLC_COUNT, &channel_type);
3407+
if value_to_self_msat.saturating_sub(anchor_outputs_value_msat) < commit_tx_fee_sat * 1000 {
3408+
return Err(APIError::APIMisuseError{ err: format!("Funding amount ({}) can't even pay fee for initial commitment transaction fee of {}.", value_to_self_msat / 1000, commit_tx_fee_sat) });
34083409
}
34093410

34103411
let mut secp_ctx = Secp256k1::new();
@@ -4504,7 +4505,7 @@ where
45044505
broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, value_to_remote_msat);
45054506
}
45064507

4507-
let commit_tx_fee_sat = commit_tx_fee_sat(feerate_per_kw, non_dust_htlc_count + fee_buffer_nondust_htlcs.unwrap_or(0), &funding.channel_transaction_parameters.channel_type_features);
4508+
let commit_tx_fee_sat = SpecTxBuilder {}.commit_tx_fee_sat(feerate_per_kw, non_dust_htlc_count + fee_buffer_nondust_htlcs.unwrap_or(0), &funding.channel_transaction_parameters.channel_type_features);
45084509
let total_anchors_sat = if funding.channel_transaction_parameters.channel_type_features.supports_anchors_zero_fee_htlc_tx() { ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 };
45094510

45104511
// We MUST use saturating subs here, as the funder's balance is not guaranteed to be greater
@@ -4753,7 +4754,7 @@ where
47534754
{
47544755
let counterparty_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.counterparty_dust_limit_satoshis;
47554756
let holder_dust_limit_success_sat = htlc_success_dust_limit + context.holder_dust_limit_satoshis;
4756-
for ref htlc in context.pending_inbound_htlcs.iter() {
4757+
for htlc in context.pending_inbound_htlcs.iter() {
47574758
pending_inbound_htlcs_value_msat += htlc.amount_msat;
47584759
if htlc.amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
47594760
on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
@@ -4773,7 +4774,7 @@ where
47734774
{
47744775
let counterparty_dust_limit_success_sat = htlc_success_dust_limit + context.counterparty_dust_limit_satoshis;
47754776
let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.holder_dust_limit_satoshis;
4776-
for ref htlc in context.pending_outbound_htlcs.iter() {
4777+
for htlc in context.pending_outbound_htlcs.iter() {
47774778
pending_outbound_htlcs_value_msat += htlc.amount_msat;
47784779
if htlc.amount_msat / 1000 < counterparty_dust_limit_success_sat {
47794780
on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
@@ -4810,10 +4811,14 @@ where
48104811
.unwrap_or(self.feerate_per_kw)
48114812
.checked_sub(dust_exposure_limiting_feerate);
48124813
let extra_nondust_htlc_on_counterparty_tx_dust_exposure_msat = excess_feerate_opt.map(|excess_feerate| {
4813-
let extra_htlc_dust_exposure = on_counterparty_tx_dust_exposure_msat
4814-
+ chan_utils::commit_and_htlc_tx_fees_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + 1, on_counterparty_tx_offered_nondust_htlcs, funding.get_channel_type()) * 1000;
4815-
on_counterparty_tx_dust_exposure_msat
4816-
+= chan_utils::commit_and_htlc_tx_fees_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs, on_counterparty_tx_offered_nondust_htlcs, funding.get_channel_type()) * 1000;
4814+
let extra_htlc_commit_tx_fee_sat = SpecTxBuilder {}.commit_tx_fee_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + 1 + on_counterparty_tx_offered_nondust_htlcs, funding.get_channel_type());
4815+
let extra_htlc_htlc_tx_fees_sat = chan_utils::htlc_tx_fees_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + 1, on_counterparty_tx_offered_nondust_htlcs, funding.get_channel_type());
4816+
4817+
let commit_tx_fee_sat = SpecTxBuilder {}.commit_tx_fee_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + on_counterparty_tx_offered_nondust_htlcs, funding.get_channel_type());
4818+
let htlc_tx_fees_sat = chan_utils::htlc_tx_fees_sat(excess_feerate, on_counterparty_tx_accepted_nondust_htlcs, on_counterparty_tx_offered_nondust_htlcs, funding.get_channel_type());
4819+
4820+
let extra_htlc_dust_exposure = on_counterparty_tx_dust_exposure_msat + (extra_htlc_commit_tx_fee_sat + extra_htlc_htlc_tx_fees_sat) * 1000;
4821+
on_counterparty_tx_dust_exposure_msat += (commit_tx_fee_sat + htlc_tx_fees_sat) * 1000;
48174822
extra_htlc_dust_exposure
48184823
});
48194824

@@ -5158,12 +5163,12 @@ where
51585163
}
51595164

51605165
let num_htlcs = included_htlcs + addl_htlcs;
5161-
let res = commit_tx_fee_sat(context.feerate_per_kw, num_htlcs, funding.get_channel_type()) * 1000;
5166+
let commit_tx_fee_msat = SpecTxBuilder {}.commit_tx_fee_sat(context.feerate_per_kw, num_htlcs, funding.get_channel_type()) * 1000;
51625167
#[cfg(any(test, fuzzing))]
51635168
{
5164-
let mut fee = res;
5169+
let mut fee = commit_tx_fee_msat;
51655170
if fee_spike_buffer_htlc.is_some() {
5166-
fee = commit_tx_fee_sat(context.feerate_per_kw, num_htlcs - 1, funding.get_channel_type()) * 1000;
5171+
fee = SpecTxBuilder {}.commit_tx_fee_sat(context.feerate_per_kw, num_htlcs - 1, funding.get_channel_type()) * 1000;
51675172
}
51685173
let total_pending_htlcs = context.pending_inbound_htlcs.len() + context.pending_outbound_htlcs.len()
51695174
+ context.holding_cell_htlc_updates.len();
@@ -5182,7 +5187,7 @@ where
51825187
};
51835188
*funding.next_local_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info);
51845189
}
5185-
res
5190+
commit_tx_fee_msat
51865191
}
51875192

51885193
/// Get the commitment tx fee for the remote's next commitment transaction based on the number of
@@ -5256,12 +5261,12 @@ where
52565261
}
52575262

52585263
let num_htlcs = included_htlcs + addl_htlcs;
5259-
let res = commit_tx_fee_sat(context.feerate_per_kw, num_htlcs, funding.get_channel_type()) * 1000;
5264+
let commit_tx_fee_msat = SpecTxBuilder {}.commit_tx_fee_sat(context.feerate_per_kw, num_htlcs, funding.get_channel_type()) * 1000;
52605265
#[cfg(any(test, fuzzing))]
52615266
if let Some(htlc) = &htlc {
5262-
let mut fee = res;
5267+
let mut fee = commit_tx_fee_msat;
52635268
if fee_spike_buffer_htlc.is_some() {
5264-
fee = commit_tx_fee_sat(context.feerate_per_kw, num_htlcs - 1, funding.get_channel_type()) * 1000;
5269+
fee = SpecTxBuilder {}.commit_tx_fee_sat(context.feerate_per_kw, num_htlcs - 1, funding.get_channel_type()) * 1000;
52655270
}
52665271
let total_pending_htlcs = context.pending_inbound_htlcs.len() + context.pending_outbound_htlcs.len();
52675272
let commitment_tx_info = CommitmentTxInfoCached {
@@ -5279,7 +5284,7 @@ where
52795284
};
52805285
*funding.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info);
52815286
}
5282-
res
5287+
commit_tx_fee_msat
52835288
}
52845289

52855290
#[rustfmt::skip]
@@ -10574,8 +10579,7 @@ where
1057410579
&& info.next_holder_htlc_id == self.context.next_holder_htlc_id
1057510580
&& info.next_counterparty_htlc_id == self.context.next_counterparty_htlc_id
1057610581
&& info.feerate == self.context.feerate_per_kw {
10577-
let actual_fee = commit_tx_fee_sat(self.context.feerate_per_kw, counterparty_commitment_tx.nondust_htlcs().len(), self.funding.get_channel_type()) * 1000;
10578-
assert_eq!(actual_fee, info.fee);
10582+
assert_eq!(commitment_data.stats.commit_tx_fee_sat, info.fee);
1057910583
}
1058010584
}
1058110585
}
@@ -13194,11 +13198,13 @@ mod tests {
1319413198
use crate::chain::chaininterface::LowerBoundedFeeEstimator;
1319513199
use crate::chain::transaction::OutPoint;
1319613200
use crate::chain::BestBlock;
13197-
use crate::ln::chan_utils::{self, htlc_success_tx_weight, htlc_timeout_tx_weight};
13201+
use crate::ln::chan_utils::{
13202+
self, commit_tx_fee_sat, htlc_success_tx_weight, htlc_timeout_tx_weight,
13203+
};
1319813204
use crate::ln::channel::{
13199-
commit_tx_fee_sat, AwaitingChannelReadyFlags, ChannelState, FundedChannel, HTLCCandidate,
13200-
HTLCInitiator, HTLCUpdateAwaitingACK, InboundHTLCOutput, InboundHTLCState,
13201-
InboundV1Channel, OutboundHTLCOutput, OutboundHTLCState, OutboundV1Channel,
13205+
AwaitingChannelReadyFlags, ChannelState, FundedChannel, HTLCCandidate, HTLCInitiator,
13206+
HTLCUpdateAwaitingACK, InboundHTLCOutput, InboundHTLCState, InboundV1Channel,
13207+
OutboundHTLCOutput, OutboundHTLCState, OutboundV1Channel,
1320213208
};
1320313209
use crate::ln::channel::{
1320413210
MAX_FUNDING_SATOSHIS_NO_WUMBO, MIN_THEIR_CHAN_RESERVE_SATOSHIS,

lightning/src/sign/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub(crate) mod type_resolver;
7878
pub mod ecdsa;
7979
#[cfg(taproot)]
8080
pub mod taproot;
81+
pub mod tx_builder;
8182

8283
/// Information about a spendable output to a P2WSH script.
8384
///

lightning/src/sign/tx_builder.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//! Defines the `TxBuilder` trait, and the `SpecTxBuilder` type
2+
3+
use types::features::ChannelTypeFeatures;
4+
5+
use crate::ln::chan_utils::commit_tx_fee_sat;
6+
7+
pub(crate) trait TxBuilder {
8+
fn commit_tx_fee_sat(
9+
&self, feerate_per_kw: u32, nondust_htlc_count: usize, channel_type: &ChannelTypeFeatures,
10+
) -> u64;
11+
}
12+
13+
pub(crate) struct SpecTxBuilder {}
14+
15+
impl TxBuilder for SpecTxBuilder {
16+
fn commit_tx_fee_sat(
17+
&self, feerate_per_kw: u32, nondust_htlc_count: usize, channel_type: &ChannelTypeFeatures,
18+
) -> u64 {
19+
commit_tx_fee_sat(feerate_per_kw, nondust_htlc_count, channel_type)
20+
}
21+
}

0 commit comments

Comments
 (0)