1
1
//! Defines the `TxBuilder` trait, and the `SpecTxBuilder` type
2
+ #![ allow( dead_code) ]
2
3
4
+ use core:: cmp;
3
5
use core:: ops:: Deref ;
4
6
5
7
use bitcoin:: secp256k1:: { self , PublicKey , Secp256k1 } ;
6
8
7
9
use crate :: ln:: chan_utils:: {
8
- commit_tx_fee_sat, htlc_success_tx_weight, htlc_timeout_tx_weight,
9
- ChannelTransactionParameters , CommitmentTransaction , HTLCOutputInCommitment ,
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 ,
10
13
} ;
11
14
use crate :: ln:: channel:: { CommitmentStats , ANCHOR_OUTPUT_VALUE_SATOSHI } ;
12
15
use crate :: prelude:: * ;
13
16
use crate :: types:: features:: ChannelTypeFeatures ;
14
17
use crate :: util:: logger:: Logger ;
15
18
19
+ pub ( crate ) struct HTLCAmountDirection {
20
+ pub outbound : bool ,
21
+ pub amount_msat : u64 ,
22
+ }
23
+
24
+ impl 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
+
37
+ pub ( crate ) struct NextCommitmentStats {
38
+ pub inbound_htlcs_count : usize ,
39
+ pub inbound_htlcs_value_msat : u64 ,
40
+ pub holder_balance_msat : Option < u64 > ,
41
+ pub counterparty_balance_msat : Option < u64 > ,
42
+ pub nondust_htlc_count : usize ,
43
+ pub commit_tx_fee_sat : u64 ,
44
+ pub dust_exposure_msat : u64 ,
45
+ // If the counterparty sets a feerate on the channel in excess of our dust_exposure_limiting_feerate,
46
+ // this should be set to the dust exposure that would result from us adding an additional nondust outbound
47
+ // htlc on the counterparty's commitment transaction.
48
+ pub extra_nondust_htlc_on_counterparty_tx_dust_exposure_msat : Option < u64 > ,
49
+ }
50
+
51
+ #[ rustfmt:: skip]
52
+ fn excess_fees_on_counterparty_tx_dust_exposure_msat (
53
+ next_commitment_htlcs : & [ HTLCAmountDirection ] , dust_buffer_feerate : u32 ,
54
+ excess_feerate : u32 , counterparty_dust_limit_satoshis : u64 , mut on_counterparty_tx_dust_exposure_msat : u64 ,
55
+ channel_type : & ChannelTypeFeatures ,
56
+ ) -> ( u64 , u64 ) {
57
+
58
+ let on_counterparty_tx_accepted_nondust_htlcs = next_commitment_htlcs. iter ( ) . filter ( |htlc| htlc. outbound && !htlc. is_dust ( false , dust_buffer_feerate, counterparty_dust_limit_satoshis, channel_type) ) . count ( ) ;
59
+ let on_counterparty_tx_offered_nondust_htlcs = next_commitment_htlcs. iter ( ) . filter ( |htlc| !htlc. outbound && !htlc. is_dust ( false , dust_buffer_feerate, counterparty_dust_limit_satoshis, channel_type) ) . count ( ) ;
60
+
61
+ let extra_htlc_commit_tx_fee_sat = commit_tx_fee_sat ( excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + 1 + on_counterparty_tx_offered_nondust_htlcs, channel_type) ;
62
+ let extra_htlc_htlc_tx_fees_sat = htlc_tx_fees_sat ( excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + 1 , on_counterparty_tx_offered_nondust_htlcs, channel_type) ;
63
+
64
+ let commit_tx_fee_sat = commit_tx_fee_sat ( excess_feerate, on_counterparty_tx_accepted_nondust_htlcs + on_counterparty_tx_offered_nondust_htlcs, channel_type) ;
65
+ let htlc_tx_fees_sat = htlc_tx_fees_sat ( excess_feerate, on_counterparty_tx_accepted_nondust_htlcs, on_counterparty_tx_offered_nondust_htlcs, channel_type) ;
66
+
67
+ let extra_htlc_dust_exposure_msat = on_counterparty_tx_dust_exposure_msat + ( extra_htlc_commit_tx_fee_sat + extra_htlc_htlc_tx_fees_sat) * 1000 ;
68
+ on_counterparty_tx_dust_exposure_msat += ( commit_tx_fee_sat + htlc_tx_fees_sat) * 1000 ;
69
+
70
+ (
71
+ on_counterparty_tx_dust_exposure_msat,
72
+ extra_htlc_dust_exposure_msat,
73
+ )
74
+ }
75
+
76
+ fn subtract_addl_outputs (
77
+ is_outbound_from_holder : bool , value_to_self_after_htlcs : u64 ,
78
+ value_to_remote_after_htlcs : u64 , channel_type : & ChannelTypeFeatures ,
79
+ ) -> ( Option < u64 > , Option < u64 > ) {
80
+ let total_anchors_sat = if channel_type. supports_anchors_zero_fee_htlc_tx ( ) {
81
+ ANCHOR_OUTPUT_VALUE_SATOSHI * 2
82
+ } else {
83
+ 0
84
+ } ;
85
+
86
+ // We MUST use checked subs here, as the funder's balance is not guaranteed to be greater
87
+ // than or equal to `total_anchors_sat`.
88
+ //
89
+ // This is because when the remote party sends an `update_fee` message, we build the new
90
+ // commitment transaction *before* checking whether the remote party's balance is enough to
91
+ // cover the total anchor sum.
92
+
93
+ let local_balance_before_fee_msat = if is_outbound_from_holder {
94
+ value_to_self_after_htlcs. checked_sub ( total_anchors_sat * 1000 )
95
+ } else {
96
+ Some ( value_to_self_after_htlcs)
97
+ } ;
98
+
99
+ let remote_balance_before_fee_msat = if !is_outbound_from_holder {
100
+ value_to_remote_after_htlcs. checked_sub ( total_anchors_sat * 1000 )
101
+ } else {
102
+ Some ( value_to_remote_after_htlcs)
103
+ } ;
104
+
105
+ ( local_balance_before_fee_msat, remote_balance_before_fee_msat)
106
+ }
107
+
108
+ fn get_dust_buffer_feerate ( feerate_per_kw : u32 ) -> u32 {
109
+ // When calculating our exposure to dust HTLCs, we assume that the channel feerate
110
+ // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
111
+ // whichever is higher. This ensures that we aren't suddenly exposed to significantly
112
+ // more dust balance if the feerate increases when we have several HTLCs pending
113
+ // which are near the dust limit.
114
+ let feerate_plus_quarter = feerate_per_kw. checked_mul ( 1250 ) . map ( |v| v / 1000 ) ;
115
+ cmp:: max ( feerate_per_kw. saturating_add ( 2530 ) , feerate_plus_quarter. unwrap_or ( u32:: MAX ) )
116
+ }
117
+
16
118
pub ( crate ) trait TxBuilder {
119
+ fn get_next_commitment_stats (
120
+ & self , local : bool , is_outbound_from_holder : bool , channel_value_satoshis : u64 ,
121
+ value_to_holder_msat : u64 , next_commitment_htlcs : & [ HTLCAmountDirection ] ,
122
+ addl_nondust_htlc_count : usize , feerate_per_kw : u32 ,
123
+ dust_exposure_limiting_feerate : Option < u32 > , broadcaster_dust_limit_satoshis : u64 ,
124
+ channel_type : & ChannelTypeFeatures ,
125
+ ) -> NextCommitmentStats ;
17
126
fn commit_tx_fee_sat (
18
127
& self , feerate_per_kw : u32 , nondust_htlc_count : usize , channel_type : & ChannelTypeFeatures ,
19
128
) -> u64 ;
@@ -25,7 +134,7 @@ pub(crate) trait TxBuilder {
25
134
& self , local : bool , commitment_number : u64 , per_commitment_point : & PublicKey ,
26
135
channel_parameters : & ChannelTransactionParameters , secp_ctx : & Secp256k1 < secp256k1:: All > ,
27
136
value_to_self_msat : u64 , htlcs_in_tx : Vec < HTLCOutputInCommitment > , feerate_per_kw : u32 ,
28
- broadcaster_dust_limit_sat : u64 , logger : & L ,
137
+ broadcaster_dust_limit_satoshis : u64 , logger : & L ,
29
138
) -> ( CommitmentTransaction , CommitmentStats )
30
139
where
31
140
L :: Target : Logger ;
@@ -34,6 +143,113 @@ pub(crate) trait TxBuilder {
34
143
pub ( crate ) struct SpecTxBuilder { }
35
144
36
145
impl TxBuilder for SpecTxBuilder {
146
+ fn get_next_commitment_stats (
147
+ & self , local : bool , is_outbound_from_holder : bool , channel_value_satoshis : u64 ,
148
+ value_to_holder_msat : u64 , next_commitment_htlcs : & [ HTLCAmountDirection ] ,
149
+ addl_nondust_htlc_count : usize , feerate_per_kw : u32 ,
150
+ dust_exposure_limiting_feerate : Option < u32 > , broadcaster_dust_limit_satoshis : u64 ,
151
+ channel_type : & ChannelTypeFeatures ,
152
+ ) -> NextCommitmentStats {
153
+ let excess_feerate_opt =
154
+ feerate_per_kw. checked_sub ( dust_exposure_limiting_feerate. unwrap_or ( 0 ) ) ;
155
+ // Dust exposure is only decoupled from feerate for zero fee commitment channels.
156
+ let is_zero_fee_comm = channel_type. supports_anchor_zero_fee_commitments ( ) ;
157
+ debug_assert_eq ! ( is_zero_fee_comm, dust_exposure_limiting_feerate. is_none( ) ) ;
158
+ if is_zero_fee_comm {
159
+ debug_assert_eq ! ( feerate_per_kw, 0 ) ;
160
+ debug_assert_eq ! ( excess_feerate_opt, Some ( 0 ) ) ;
161
+ debug_assert_eq ! ( addl_nondust_htlc_count, 0 ) ;
162
+ }
163
+
164
+ // Calculate inbound htlc count
165
+ let inbound_htlcs_count =
166
+ next_commitment_htlcs. iter ( ) . filter ( |htlc| !htlc. outbound ) . count ( ) ;
167
+
168
+ // Calculate balances after htlcs
169
+ let value_to_counterparty_msat = ( channel_value_satoshis * 1000 )
170
+ . checked_sub ( value_to_holder_msat)
171
+ . expect ( "value_to_holder_msat outgrew the value of the channel!" ) ;
172
+ let outbound_htlcs_value_msat: u64 = next_commitment_htlcs
173
+ . iter ( )
174
+ . filter_map ( |htlc| htlc. outbound . then_some ( htlc. amount_msat ) )
175
+ . sum ( ) ;
176
+ let inbound_htlcs_value_msat: u64 = next_commitment_htlcs
177
+ . iter ( )
178
+ . filter_map ( |htlc| ( !htlc. outbound ) . then_some ( htlc. amount_msat ) )
179
+ . sum ( ) ;
180
+ let value_to_holder_after_htlcs = value_to_holder_msat
181
+ . checked_sub ( outbound_htlcs_value_msat)
182
+ . expect ( "outbound_htlcs_value_msat is greater than value_to_holder_msat" ) ;
183
+ let value_to_counterparty_after_htlcs = value_to_counterparty_msat
184
+ . checked_sub ( inbound_htlcs_value_msat)
185
+ . expect ( "inbound_htlcs_value_msat is greater than value_to_counterparty_msat" ) ;
186
+
187
+ // Subtract the anchors from the channel funder
188
+ let ( holder_balance_msat, counterparty_balance_msat) = subtract_addl_outputs (
189
+ is_outbound_from_holder,
190
+ value_to_holder_after_htlcs,
191
+ value_to_counterparty_after_htlcs,
192
+ channel_type,
193
+ ) ;
194
+
195
+ // Increment the feerate by a buffer to calculate dust exposure
196
+ let dust_buffer_feerate = get_dust_buffer_feerate ( feerate_per_kw) ;
197
+
198
+ // Calculate fees on commitment transaction
199
+ let nondust_htlc_count = next_commitment_htlcs
200
+ . iter ( )
201
+ . filter ( |htlc| {
202
+ !htlc. is_dust ( local, feerate_per_kw, broadcaster_dust_limit_satoshis, channel_type)
203
+ } )
204
+ . count ( ) ;
205
+ let commit_tx_fee_sat = commit_tx_fee_sat (
206
+ feerate_per_kw,
207
+ nondust_htlc_count + addl_nondust_htlc_count,
208
+ channel_type,
209
+ ) ;
210
+
211
+ // Calculate dust exposure on commitment transaction
212
+ let dust_exposure_msat = next_commitment_htlcs
213
+ . iter ( )
214
+ . filter_map ( |htlc| {
215
+ htlc. is_dust (
216
+ local,
217
+ dust_buffer_feerate,
218
+ broadcaster_dust_limit_satoshis,
219
+ channel_type,
220
+ )
221
+ . then_some ( htlc. amount_msat )
222
+ } )
223
+ . sum ( ) ;
224
+
225
+ // Count the excess fees on the counterparty's transaction as dust
226
+ let ( dust_exposure_msat, extra_nondust_htlc_on_counterparty_tx_dust_exposure_msat) =
227
+ if let ( Some ( excess_feerate) , false ) = ( excess_feerate_opt, local) {
228
+ let ( dust_exposure_msat, extra_nondust_htlc_exposure_msat) =
229
+ excess_fees_on_counterparty_tx_dust_exposure_msat (
230
+ & next_commitment_htlcs,
231
+ dust_buffer_feerate,
232
+ excess_feerate,
233
+ broadcaster_dust_limit_satoshis,
234
+ dust_exposure_msat,
235
+ channel_type,
236
+ ) ;
237
+ ( dust_exposure_msat, Some ( extra_nondust_htlc_exposure_msat) )
238
+ } else {
239
+ ( dust_exposure_msat, None )
240
+ } ;
241
+
242
+ NextCommitmentStats {
243
+ inbound_htlcs_count,
244
+ inbound_htlcs_value_msat,
245
+ holder_balance_msat,
246
+ counterparty_balance_msat,
247
+ nondust_htlc_count,
248
+ commit_tx_fee_sat,
249
+ dust_exposure_msat,
250
+ extra_nondust_htlc_on_counterparty_tx_dust_exposure_msat,
251
+ }
252
+ }
37
253
fn commit_tx_fee_sat (
38
254
& self , feerate_per_kw : u32 , nondust_htlc_count : usize , channel_type : & ChannelTypeFeatures ,
39
255
) -> u64 {
@@ -74,7 +290,7 @@ impl TxBuilder for SpecTxBuilder {
74
290
& self , local : bool , commitment_number : u64 , per_commitment_point : & PublicKey ,
75
291
channel_parameters : & ChannelTransactionParameters , secp_ctx : & Secp256k1 < secp256k1:: All > ,
76
292
value_to_self_msat : u64 , mut htlcs_in_tx : Vec < HTLCOutputInCommitment > , feerate_per_kw : u32 ,
77
- broadcaster_dust_limit_sat : u64 , logger : & L ,
293
+ broadcaster_dust_limit_satoshis : u64 , logger : & L ,
78
294
) -> ( CommitmentTransaction , CommitmentStats )
79
295
where
80
296
L :: Target : Logger ,
@@ -95,7 +311,7 @@ impl TxBuilder for SpecTxBuilder {
95
311
// As required by the spec, round down
96
312
feerate_per_kw as u64 * htlc_tx_weight / 1000
97
313
} ;
98
- amount_msat / 1000 < broadcaster_dust_limit_sat + htlc_tx_fee_sat
314
+ amount_msat / 1000 < broadcaster_dust_limit_satoshis + htlc_tx_fee_sat
99
315
} ;
100
316
101
317
// Trim dust htlcs
@@ -107,7 +323,7 @@ impl TxBuilder for SpecTxBuilder {
107
323
remote_htlc_total_msat += htlc. amount_msat ;
108
324
}
109
325
if is_dust ( htlc. offered , htlc. amount_msat ) {
110
- log_trace ! ( logger, " ...trimming {} HTLC with value {}sat, hash {}, due to dust limit {}" , if htlc. offered == local { "outbound" } else { "inbound" } , htlc. amount_msat / 1000 , htlc. payment_hash, broadcaster_dust_limit_sat ) ;
326
+ log_trace ! ( logger, " ...trimming {} HTLC with value {}sat, hash {}, due to dust limit {}" , if htlc. offered == local { "outbound" } else { "inbound" } , htlc. amount_msat / 1000 , htlc. payment_hash, broadcaster_dust_limit_satoshis ) ;
111
327
false
112
328
} else {
113
329
true
@@ -142,13 +358,13 @@ impl TxBuilder for SpecTxBuilder {
142
358
let mut to_broadcaster_value_sat = if local { value_to_self } else { value_to_remote } ;
143
359
let mut to_countersignatory_value_sat = if local { value_to_remote } else { value_to_self } ;
144
360
145
- if to_broadcaster_value_sat >= broadcaster_dust_limit_sat {
361
+ if to_broadcaster_value_sat >= broadcaster_dust_limit_satoshis {
146
362
log_trace ! ( logger, " ...including {} output with value {}" , if local { "to_local" } else { "to_remote" } , to_broadcaster_value_sat) ;
147
363
} else {
148
364
to_broadcaster_value_sat = 0 ;
149
365
}
150
366
151
- if to_countersignatory_value_sat >= broadcaster_dust_limit_sat {
367
+ if to_countersignatory_value_sat >= broadcaster_dust_limit_satoshis {
152
368
log_trace ! ( logger, " ...including {} output with value {}" , if local { "to_remote" } else { "to_local" } , to_countersignatory_value_sat) ;
153
369
} else {
154
370
to_countersignatory_value_sat = 0 ;
0 commit comments