1#![allow(dead_code)]
3
4use core::cmp;
5use core::ops::Deref;
6
7use bitcoin::secp256k1::{self, PublicKey, Secp256k1};
8
9use crate::ln::chan_utils::{
10 commit_tx_fee_sat, htlc_success_tx_weight, htlc_timeout_tx_weight, htlc_tx_fees_sat,
11 second_stage_tx_fees_sat, ChannelTransactionParameters, CommitmentTransaction,
12 HTLCOutputInCommitment,
13};
14use crate::ln::channel::{CommitmentStats, ANCHOR_OUTPUT_VALUE_SATOSHI};
15use crate::prelude::*;
16use crate::types::features::ChannelTypeFeatures;
17use crate::util::logger::Logger;
18
19pub(crate) struct HTLCAmountDirection {
20 pub outbound: bool,
21 pub amount_msat: u64,
22}
23
24impl HTLCAmountDirection {
25 fn is_dust(
26 &self, local: bool, feerate_per_kw: u32, broadcaster_dust_limit_satoshis: u64,
27 channel_type: &ChannelTypeFeatures,
28 ) -> bool {
29 let (success_tx_fee_sat, timeout_tx_fee_sat) =
30 second_stage_tx_fees_sat(channel_type, feerate_per_kw);
31 let htlc_tx_fee_sat =
32 if self.outbound == local { timeout_tx_fee_sat } else { success_tx_fee_sat };
33 self.amount_msat / 1000 < broadcaster_dust_limit_satoshis + htlc_tx_fee_sat
34 }
35}
36
37pub(crate) struct NextCommitmentStats {
38 pub is_outbound_from_holder: bool,
39 pub inbound_htlcs_count: usize,
40 pub inbound_htlcs_value_msat: u64,
41 pub holder_balance_before_fee_msat: u64,
42 pub counterparty_balance_before_fee_msat: u64,
43 pub nondust_htlc_count: usize,
44 pub commit_tx_fee_sat: u64,
45 pub dust_exposure_msat: u64,
46 pub extra_accepted_htlc_dust_exposure_msat: u64,
47}
48
49impl NextCommitmentStats {
50 pub(crate) fn get_holder_counterparty_balances_incl_fee_msat(&self) -> Result<(u64, u64), ()> {
51 if self.is_outbound_from_holder {
52 Ok((
53 self.holder_balance_before_fee_msat
54 .checked_sub(self.commit_tx_fee_sat * 1000)
55 .ok_or(())?,
56 self.counterparty_balance_before_fee_msat,
57 ))
58 } else {
59 Ok((
60 self.holder_balance_before_fee_msat,
61 self.counterparty_balance_before_fee_msat
62 .checked_sub(self.commit_tx_fee_sat * 1000)
63 .ok_or(())?,
64 ))
65 }
66 }
67}
68
69fn commit_plus_htlc_tx_fees_msat(
70 local: bool, next_commitment_htlcs: &[HTLCAmountDirection], dust_buffer_feerate: u32,
71 feerate: u32, broadcaster_dust_limit_satoshis: u64, channel_type: &ChannelTypeFeatures,
72) -> (u64, u64) {
73 let accepted_nondust_htlcs = next_commitment_htlcs
74 .iter()
75 .filter(|htlc| {
76 htlc.outbound != local
77 && !htlc.is_dust(
78 local,
79 dust_buffer_feerate,
80 broadcaster_dust_limit_satoshis,
81 channel_type,
82 )
83 })
84 .count();
85 let offered_nondust_htlcs = next_commitment_htlcs
86 .iter()
87 .filter(|htlc| {
88 htlc.outbound == local
89 && !htlc.is_dust(
90 local,
91 dust_buffer_feerate,
92 broadcaster_dust_limit_satoshis,
93 channel_type,
94 )
95 })
96 .count();
97
98 let commitment_fee_sat =
99 commit_tx_fee_sat(feerate, accepted_nondust_htlcs + offered_nondust_htlcs, channel_type);
100 let second_stage_fees_sat =
101 htlc_tx_fees_sat(feerate, accepted_nondust_htlcs, offered_nondust_htlcs, channel_type);
102 let total_fees_msat = (commitment_fee_sat + second_stage_fees_sat) * 1000;
103
104 let extra_accepted_htlc_commitment_fee_sat = commit_tx_fee_sat(
105 feerate,
106 accepted_nondust_htlcs + 1 + offered_nondust_htlcs,
107 channel_type,
108 );
109 let extra_accepted_htlc_second_stage_fees_sat =
110 htlc_tx_fees_sat(feerate, accepted_nondust_htlcs + 1, offered_nondust_htlcs, channel_type);
111 let extra_accepted_htlc_total_fees_msat =
112 (extra_accepted_htlc_commitment_fee_sat + extra_accepted_htlc_second_stage_fees_sat) * 1000;
113
114 (total_fees_msat, extra_accepted_htlc_total_fees_msat)
115}
116
117fn subtract_addl_outputs(
118 is_outbound_from_holder: bool, value_to_self_after_htlcs_msat: u64,
119 value_to_remote_after_htlcs_msat: u64, channel_type: &ChannelTypeFeatures,
120) -> Result<(u64, u64), ()> {
121 let total_anchors_sat = if channel_type.supports_anchors_zero_fee_htlc_tx() {
122 ANCHOR_OUTPUT_VALUE_SATOSHI * 2
123 } else {
124 0
125 };
126
127 if is_outbound_from_holder {
135 Ok((
136 value_to_self_after_htlcs_msat.checked_sub(total_anchors_sat * 1000).ok_or(())?,
137 value_to_remote_after_htlcs_msat,
138 ))
139 } else {
140 Ok((
141 value_to_self_after_htlcs_msat,
142 value_to_remote_after_htlcs_msat.checked_sub(total_anchors_sat * 1000).ok_or(())?,
143 ))
144 }
145}
146
147fn get_dust_buffer_feerate(feerate_per_kw: u32) -> u32 {
148 let feerate_plus_quarter = feerate_per_kw.checked_mul(1250).map(|v| v / 1000);
154 cmp::max(feerate_per_kw.saturating_add(2530), feerate_plus_quarter.unwrap_or(u32::MAX))
155}
156
157pub(crate) trait TxBuilder {
158 fn get_next_commitment_stats(
159 &self, local: bool, is_outbound_from_holder: bool, channel_value_satoshis: u64,
160 value_to_holder_msat: u64, next_commitment_htlcs: &[HTLCAmountDirection],
161 addl_nondust_htlc_count: usize, feerate_per_kw: u32,
162 dust_exposure_limiting_feerate: Option<u32>, broadcaster_dust_limit_satoshis: u64,
163 channel_type: &ChannelTypeFeatures,
164 ) -> Result<NextCommitmentStats, ()>;
165 fn commit_tx_fee_sat(
166 &self, feerate_per_kw: u32, nondust_htlc_count: usize, channel_type: &ChannelTypeFeatures,
167 ) -> u64;
168 fn subtract_non_htlc_outputs(
169 &self, is_outbound_from_holder: bool, value_to_self_after_htlcs: u64,
170 value_to_remote_after_htlcs: u64, channel_type: &ChannelTypeFeatures,
171 ) -> (u64, u64);
172 fn build_commitment_transaction<L: Deref>(
173 &self, local: bool, commitment_number: u64, per_commitment_point: &PublicKey,
174 channel_parameters: &ChannelTransactionParameters, secp_ctx: &Secp256k1<secp256k1::All>,
175 value_to_self_msat: u64, htlcs_in_tx: Vec<HTLCOutputInCommitment>, feerate_per_kw: u32,
176 broadcaster_dust_limit_satoshis: u64, logger: &L,
177 ) -> (CommitmentTransaction, CommitmentStats)
178 where
179 L::Target: Logger;
180}
181
182pub(crate) struct SpecTxBuilder {}
183
184impl TxBuilder for SpecTxBuilder {
185 fn get_next_commitment_stats(
186 &self, local: bool, is_outbound_from_holder: bool, channel_value_satoshis: u64,
187 value_to_holder_msat: u64, next_commitment_htlcs: &[HTLCAmountDirection],
188 addl_nondust_htlc_count: usize, feerate_per_kw: u32,
189 dust_exposure_limiting_feerate: Option<u32>, broadcaster_dust_limit_satoshis: u64,
190 channel_type: &ChannelTypeFeatures,
191 ) -> Result<NextCommitmentStats, ()> {
192 let excess_feerate =
193 feerate_per_kw.saturating_sub(dust_exposure_limiting_feerate.unwrap_or(feerate_per_kw));
194 if channel_type.supports_anchor_zero_fee_commitments() {
195 debug_assert_eq!(feerate_per_kw, 0);
196 debug_assert_eq!(excess_feerate, 0);
197 debug_assert_eq!(addl_nondust_htlc_count, 0);
198 }
199
200 let inbound_htlcs_count =
202 next_commitment_htlcs.iter().filter(|htlc| !htlc.outbound).count();
203
204 let value_to_counterparty_msat =
206 (channel_value_satoshis * 1000).checked_sub(value_to_holder_msat).ok_or(())?;
207 let outbound_htlcs_value_msat: u64 = next_commitment_htlcs
208 .iter()
209 .filter_map(|htlc| htlc.outbound.then_some(htlc.amount_msat))
210 .sum();
211 let inbound_htlcs_value_msat: u64 = next_commitment_htlcs
212 .iter()
213 .filter_map(|htlc| (!htlc.outbound).then_some(htlc.amount_msat))
214 .sum();
215 let value_to_holder_after_htlcs_msat =
216 value_to_holder_msat.checked_sub(outbound_htlcs_value_msat).ok_or(())?;
217 let value_to_counterparty_after_htlcs_msat =
218 value_to_counterparty_msat.checked_sub(inbound_htlcs_value_msat).ok_or(())?;
219
220 let (holder_balance_before_fee_msat, counterparty_balance_before_fee_msat) =
222 subtract_addl_outputs(
223 is_outbound_from_holder,
224 value_to_holder_after_htlcs_msat,
225 value_to_counterparty_after_htlcs_msat,
226 channel_type,
227 )?;
228
229 let dust_buffer_feerate = get_dust_buffer_feerate(feerate_per_kw);
231
232 let nondust_htlc_count = next_commitment_htlcs
234 .iter()
235 .filter(|htlc| {
236 !htlc.is_dust(local, feerate_per_kw, broadcaster_dust_limit_satoshis, channel_type)
237 })
238 .count();
239 let commit_tx_fee_sat = commit_tx_fee_sat(
240 feerate_per_kw,
241 nondust_htlc_count + addl_nondust_htlc_count,
242 channel_type,
243 );
244
245 let dust_exposure_msat = next_commitment_htlcs
247 .iter()
248 .filter_map(|htlc| {
249 htlc.is_dust(
250 local,
251 dust_buffer_feerate,
252 broadcaster_dust_limit_satoshis,
253 channel_type,
254 )
255 .then_some(htlc.amount_msat)
256 })
257 .sum();
258
259 let (dust_exposure_msat, extra_accepted_htlc_dust_exposure_msat) = if local {
261 (dust_exposure_msat, dust_exposure_msat)
262 } else {
263 let (excess_fees_msat, extra_accepted_htlc_excess_fees_msat) =
264 commit_plus_htlc_tx_fees_msat(
265 local,
266 &next_commitment_htlcs,
267 dust_buffer_feerate,
268 excess_feerate,
269 broadcaster_dust_limit_satoshis,
270 channel_type,
271 );
272 (
273 dust_exposure_msat + excess_fees_msat,
274 dust_exposure_msat + extra_accepted_htlc_excess_fees_msat,
275 )
276 };
277
278 Ok(NextCommitmentStats {
279 is_outbound_from_holder,
280 inbound_htlcs_count,
281 inbound_htlcs_value_msat,
282 holder_balance_before_fee_msat,
283 counterparty_balance_before_fee_msat,
284 nondust_htlc_count: nondust_htlc_count + addl_nondust_htlc_count,
285 commit_tx_fee_sat,
286 dust_exposure_msat,
287 extra_accepted_htlc_dust_exposure_msat,
288 })
289 }
290 fn commit_tx_fee_sat(
291 &self, feerate_per_kw: u32, nondust_htlc_count: usize, channel_type: &ChannelTypeFeatures,
292 ) -> u64 {
293 commit_tx_fee_sat(feerate_per_kw, nondust_htlc_count, channel_type)
294 }
295 fn subtract_non_htlc_outputs(
296 &self, is_outbound_from_holder: bool, value_to_self_after_htlcs: u64,
297 value_to_remote_after_htlcs: u64, channel_type: &ChannelTypeFeatures,
298 ) -> (u64, u64) {
299 let total_anchors_sat = if channel_type.supports_anchors_zero_fee_htlc_tx() {
300 ANCHOR_OUTPUT_VALUE_SATOSHI * 2
301 } else {
302 0
303 };
304
305 let mut local_balance_before_fee_msat = value_to_self_after_htlcs;
306 let mut remote_balance_before_fee_msat = value_to_remote_after_htlcs;
307
308 if is_outbound_from_holder {
316 local_balance_before_fee_msat =
317 local_balance_before_fee_msat.saturating_sub(total_anchors_sat * 1000);
318 } else {
319 remote_balance_before_fee_msat =
320 remote_balance_before_fee_msat.saturating_sub(total_anchors_sat * 1000);
321 }
322
323 (local_balance_before_fee_msat, remote_balance_before_fee_msat)
324 }
325 fn build_commitment_transaction<L: Deref>(
326 &self, local: bool, commitment_number: u64, per_commitment_point: &PublicKey,
327 channel_parameters: &ChannelTransactionParameters, secp_ctx: &Secp256k1<secp256k1::All>,
328 value_to_self_msat: u64, mut htlcs_in_tx: Vec<HTLCOutputInCommitment>, feerate_per_kw: u32,
329 broadcaster_dust_limit_satoshis: u64, logger: &L,
330 ) -> (CommitmentTransaction, CommitmentStats)
331 where
332 L::Target: Logger,
333 {
334 let mut local_htlc_total_msat = 0;
335 let mut remote_htlc_total_msat = 0;
336 let channel_type = &channel_parameters.channel_type_features;
337
338 let is_dust = |offered: bool, amount_msat: u64| -> bool {
339 let htlc_tx_fee_sat = if channel_type.supports_anchors_zero_fee_htlc_tx() {
340 0
341 } else {
342 let htlc_tx_weight = if offered {
343 htlc_timeout_tx_weight(channel_type)
344 } else {
345 htlc_success_tx_weight(channel_type)
346 };
347 feerate_per_kw as u64 * htlc_tx_weight / 1000
349 };
350 amount_msat / 1000 < broadcaster_dust_limit_satoshis + htlc_tx_fee_sat
351 };
352
353 htlcs_in_tx.retain(|htlc| {
355 if htlc.offered == local {
356 local_htlc_total_msat += htlc.amount_msat;
358 } else {
359 remote_htlc_total_msat += htlc.amount_msat;
360 }
361 if is_dust(htlc.offered, htlc.amount_msat) {
362 log_trace!(
363 logger,
364 " ...trimming {} HTLC with value {}sat, hash {}, due to dust limit {}",
365 if htlc.offered == local { "outbound" } else { "inbound" },
366 htlc.amount_msat / 1000,
367 htlc.payment_hash,
368 broadcaster_dust_limit_satoshis
369 );
370 false
371 } else {
372 true
373 }
374 });
375
376 let commit_tx_fee_sat = self.commit_tx_fee_sat(
382 feerate_per_kw,
383 htlcs_in_tx.len(),
384 &channel_parameters.channel_type_features,
385 );
386 let value_to_self_after_htlcs_msat =
387 value_to_self_msat.checked_sub(local_htlc_total_msat).unwrap();
388 let value_to_remote_after_htlcs_msat = (channel_parameters.channel_value_satoshis * 1000)
389 .checked_sub(value_to_self_msat)
390 .unwrap()
391 .checked_sub(remote_htlc_total_msat)
392 .unwrap();
393 let (local_balance_before_fee_msat, remote_balance_before_fee_msat) = self
394 .subtract_non_htlc_outputs(
395 channel_parameters.is_outbound_from_holder,
396 value_to_self_after_htlcs_msat,
397 value_to_remote_after_htlcs_msat,
398 &channel_parameters.channel_type_features,
399 );
400
401 let (value_to_self, value_to_remote) = if channel_parameters.is_outbound_from_holder {
409 (
410 (local_balance_before_fee_msat / 1000).saturating_sub(commit_tx_fee_sat),
411 remote_balance_before_fee_msat / 1000,
412 )
413 } else {
414 (
415 local_balance_before_fee_msat / 1000,
416 (remote_balance_before_fee_msat / 1000).saturating_sub(commit_tx_fee_sat),
417 )
418 };
419
420 let mut to_broadcaster_value_sat = if local { value_to_self } else { value_to_remote };
421 let mut to_countersignatory_value_sat = if local { value_to_remote } else { value_to_self };
422
423 if to_broadcaster_value_sat >= broadcaster_dust_limit_satoshis {
424 log_trace!(
425 logger,
426 " ...including {} output with value {}",
427 if local { "to_local" } else { "to_remote" },
428 to_broadcaster_value_sat
429 );
430 } else {
431 to_broadcaster_value_sat = 0;
432 }
433
434 if to_countersignatory_value_sat >= broadcaster_dust_limit_satoshis {
435 log_trace!(
436 logger,
437 " ...including {} output with value {}",
438 if local { "to_remote" } else { "to_local" },
439 to_countersignatory_value_sat
440 );
441 } else {
442 to_countersignatory_value_sat = 0;
443 }
444
445 let directed_parameters = if local {
446 channel_parameters.as_holder_broadcastable()
447 } else {
448 channel_parameters.as_counterparty_broadcastable()
449 };
450 let tx = CommitmentTransaction::new(
451 commitment_number,
452 per_commitment_point,
453 to_broadcaster_value_sat,
454 to_countersignatory_value_sat,
455 feerate_per_kw,
456 htlcs_in_tx,
457 &directed_parameters,
458 secp_ctx,
459 );
460
461 (
462 tx,
463 CommitmentStats {
464 commit_tx_fee_sat,
465 local_balance_before_fee_msat,
466 remote_balance_before_fee_msat,
467 },
468 )
469 }
470}