@@ -106,7 +106,7 @@ fn subtract_addl_outputs(
106106 ( local_balance_before_fee_msat, remote_balance_before_fee_msat)
107107}
108108
109- pub ( crate ) fn get_dust_buffer_feerate ( feerate_per_kw : u32 ) -> u32 {
109+ fn get_dust_buffer_feerate ( feerate_per_kw : u32 ) -> u32 {
110110 // When calculating our exposure to dust HTLCs, we assume that the channel feerate
111111 // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
112112 // whichever is higher. This ensures that we aren't suddenly exposed to significantly
@@ -125,6 +125,19 @@ pub(crate) struct ChannelConstraints {
125125}
126126
127127pub ( crate ) trait TxBuilder {
128+ fn get_available_balances (
129+ & self ,
130+ is_outbound_from_holder : bool ,
131+ channel_value_satoshis : u64 ,
132+ value_to_holder_msat : u64 ,
133+ pending_htlcs : & [ HTLCAmountDirection ] ,
134+ feerate_per_kw : u32 ,
135+ dust_exposure_limiting_feerate : Option < u32 > ,
136+ max_dust_htlc_exposure_msat : u64 ,
137+ holder_channel_constraints : ChannelConstraints ,
138+ counterparty_channel_constraints : ChannelConstraints ,
139+ channel_type : & ChannelTypeFeatures ,
140+ ) -> crate :: ln:: channel:: AvailableBalances ;
128141 fn get_next_commitment_stats (
129142 & self , local : bool , is_outbound_from_holder : bool , channel_value_satoshis : u64 ,
130143 value_to_holder_msat : u64 , next_commitment_htlcs : & [ HTLCAmountDirection ] ,
@@ -152,6 +165,141 @@ pub(crate) trait TxBuilder {
152165pub ( crate ) struct SpecTxBuilder { }
153166
154167impl TxBuilder for SpecTxBuilder {
168+ fn get_available_balances (
169+ & self ,
170+ is_outbound_from_holder : bool ,
171+ channel_value_satoshis : u64 ,
172+ value_to_holder_msat : u64 ,
173+ pending_htlcs : & [ HTLCAmountDirection ] ,
174+ feerate_per_kw : u32 ,
175+ dust_exposure_limiting_feerate : Option < u32 > ,
176+ max_dust_htlc_exposure_msat : u64 ,
177+ holder_channel_constraints : ChannelConstraints ,
178+ counterparty_channel_constraints : ChannelConstraints ,
179+ channel_type : & ChannelTypeFeatures ,
180+ ) -> crate :: ln:: channel:: AvailableBalances {
181+ let fee_spike_buffer_htlc = if channel_type. supports_anchor_zero_fee_commitments ( ) {
182+ 0
183+ } else {
184+ 1
185+ } ;
186+
187+ let local_stats_max_fee = SpecTxBuilder { } . get_next_commitment_stats ( true , is_outbound_from_holder, channel_value_satoshis, value_to_holder_msat, pending_htlcs, fee_spike_buffer_htlc + 1 , feerate_per_kw, dust_exposure_limiting_feerate, holder_channel_constraints. dust_limit_satoshis , channel_type) ;
188+ let local_stats_min_fee = SpecTxBuilder { } . get_next_commitment_stats ( true , is_outbound_from_holder, channel_value_satoshis, value_to_holder_msat, pending_htlcs, fee_spike_buffer_htlc, feerate_per_kw, dust_exposure_limiting_feerate, holder_channel_constraints. dust_limit_satoshis , channel_type) ;
189+ let remote_stats = SpecTxBuilder { } . get_next_commitment_stats ( false , is_outbound_from_holder, channel_value_satoshis, value_to_holder_msat, pending_htlcs, 1 , feerate_per_kw, dust_exposure_limiting_feerate, counterparty_channel_constraints. dust_limit_satoshis , channel_type) ;
190+
191+ let outbound_capacity_msat = local_stats_max_fee. holder_balance_before_fee_msat . unwrap_or ( 0 )
192+ . saturating_sub (
193+ holder_channel_constraints. channel_reserve_satoshis * 1000 ) ;
194+
195+ let mut available_capacity_msat = outbound_capacity_msat;
196+ let ( real_htlc_success_tx_fee_sat, real_htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat (
197+ channel_type, feerate_per_kw
198+ ) ;
199+
200+ if is_outbound_from_holder {
201+ // We should mind channel commit tx fee when computing how much of the available capacity
202+ // can be used in the next htlc. Mirrors the logic in send_htlc.
203+ //
204+ // The fee depends on whether the amount we will be sending is above dust or not,
205+ // and the answer will in turn change the amount itself — making it a circular
206+ // dependency.
207+ // This complicates the computation around dust-values, up to the one-htlc-value.
208+
209+ let real_dust_limit_timeout_sat = real_htlc_timeout_tx_fee_sat + holder_channel_constraints. dust_limit_satoshis ;
210+ let mut max_reserved_commit_tx_fee_msat = local_stats_max_fee. commit_tx_fee_sat * 1000 ;
211+ let mut min_reserved_commit_tx_fee_msat = local_stats_min_fee. commit_tx_fee_sat * 1000 ;
212+
213+ if !channel_type. supports_anchors_zero_fee_htlc_tx ( ) {
214+ max_reserved_commit_tx_fee_msat *= crate :: ln:: channel:: FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE ;
215+ min_reserved_commit_tx_fee_msat *= crate :: ln:: channel:: FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE ;
216+ }
217+
218+ // We will first subtract the fee as if we were above-dust. Then, if the resulting
219+ // value ends up being below dust, we have this fee available again. In that case,
220+ // match the value to right-below-dust.
221+ let capacity_minus_max_commitment_fee_msat = available_capacity_msat. saturating_sub ( max_reserved_commit_tx_fee_msat) ;
222+ if capacity_minus_max_commitment_fee_msat < real_dust_limit_timeout_sat * 1000 {
223+ let capacity_minus_min_commitment_fee_msat = available_capacity_msat. saturating_sub ( min_reserved_commit_tx_fee_msat) ;
224+ available_capacity_msat = cmp:: min ( real_dust_limit_timeout_sat * 1000 - 1 , capacity_minus_min_commitment_fee_msat) ;
225+ } else {
226+ available_capacity_msat = capacity_minus_max_commitment_fee_msat;
227+ }
228+ } else {
229+ // If the channel is inbound (i.e. counterparty pays the fee), we need to make sure
230+ // sending a new HTLC won't reduce their balance below our reserve threshold.
231+ let real_dust_limit_success_sat = real_htlc_success_tx_fee_sat + counterparty_channel_constraints. dust_limit_satoshis ;
232+ let max_reserved_commit_tx_fee_msat = remote_stats. commit_tx_fee_sat * 1000 ;
233+
234+ let holder_selected_chan_reserve_msat = counterparty_channel_constraints. channel_reserve_satoshis * 1000 ;
235+ if remote_stats. counterparty_balance_before_fee_msat . unwrap_or ( 0 ) < max_reserved_commit_tx_fee_msat + holder_selected_chan_reserve_msat {
236+ // If another HTLC's fee would reduce the remote's balance below the reserve limit
237+ // we've selected for them, we can only send dust HTLCs.
238+ available_capacity_msat = cmp:: min ( available_capacity_msat, real_dust_limit_success_sat * 1000 - 1 ) ;
239+ }
240+ }
241+
242+ let mut next_outbound_htlc_minimum_msat = counterparty_channel_constraints. htlc_minimum_msat ;
243+
244+ // If we get close to our maximum dust exposure, we end up in a situation where we can send
245+ // between zero and the remaining dust exposure limit remaining OR above the dust limit.
246+ // Because we cannot express this as a simple min/max, we prefer to tell the user they can
247+ // send above the dust limit (as the router can always overpay to meet the dust limit).
248+ let mut remaining_msat_below_dust_exposure_limit = None ;
249+ let mut dust_exposure_dust_limit_msat = 0 ;
250+
251+ let dust_buffer_feerate = get_dust_buffer_feerate ( feerate_per_kw) ;
252+ let ( buffer_htlc_success_tx_fee_sat, buffer_htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat (
253+ channel_type, dust_buffer_feerate,
254+ ) ;
255+ let buffer_dust_limit_success_sat = buffer_htlc_success_tx_fee_sat + counterparty_channel_constraints. dust_limit_satoshis ;
256+ let buffer_dust_limit_timeout_sat = buffer_htlc_timeout_tx_fee_sat + holder_channel_constraints. dust_limit_satoshis ;
257+
258+ if let Some ( extra_htlc_dust_exposure) = remote_stats. extra_nondust_htlc_on_counterparty_tx_dust_exposure_msat {
259+ if extra_htlc_dust_exposure > max_dust_htlc_exposure_msat {
260+ // If adding an extra HTLC would put us over the dust limit in total fees, we cannot
261+ // send any non-dust HTLCs.
262+ available_capacity_msat = cmp:: min ( available_capacity_msat, buffer_dust_limit_success_sat * 1000 ) ;
263+ }
264+ }
265+
266+ if remote_stats. dust_exposure_msat . saturating_add ( buffer_dust_limit_success_sat * 1000 ) > max_dust_htlc_exposure_msat. saturating_add ( 1 ) {
267+ // Note that we don't use the `counterparty_tx_dust_exposure` (with
268+ // `htlc_dust_exposure_msat`) here as it only applies to non-dust HTLCs.
269+ remaining_msat_below_dust_exposure_limit =
270+ Some ( max_dust_htlc_exposure_msat. saturating_sub ( remote_stats. dust_exposure_msat ) ) ;
271+ dust_exposure_dust_limit_msat = cmp:: max ( dust_exposure_dust_limit_msat, buffer_dust_limit_success_sat * 1000 ) ;
272+ }
273+
274+ if local_stats_max_fee. dust_exposure_msat as i64 + buffer_dust_limit_timeout_sat as i64 * 1000 - 1 > max_dust_htlc_exposure_msat. try_into ( ) . unwrap_or ( i64:: max_value ( ) ) {
275+ remaining_msat_below_dust_exposure_limit = Some ( cmp:: min (
276+ remaining_msat_below_dust_exposure_limit. unwrap_or ( u64:: max_value ( ) ) ,
277+ max_dust_htlc_exposure_msat. saturating_sub ( local_stats_max_fee. dust_exposure_msat ) ) ) ;
278+ dust_exposure_dust_limit_msat = cmp:: max ( dust_exposure_dust_limit_msat, buffer_dust_limit_timeout_sat * 1000 ) ;
279+ }
280+
281+ if let Some ( remaining_limit_msat) = remaining_msat_below_dust_exposure_limit {
282+ if available_capacity_msat < dust_exposure_dust_limit_msat {
283+ available_capacity_msat = cmp:: min ( available_capacity_msat, remaining_limit_msat) ;
284+ } else {
285+ next_outbound_htlc_minimum_msat = cmp:: max ( next_outbound_htlc_minimum_msat, dust_exposure_dust_limit_msat) ;
286+ }
287+ }
288+
289+ available_capacity_msat = cmp:: min ( available_capacity_msat,
290+ counterparty_channel_constraints. max_htlc_value_in_flight_msat - pending_htlcs. iter ( ) . filter ( |htlc| htlc. outbound ) . map ( |htlc| htlc. amount_msat ) . sum :: < u64 > ( ) ) ;
291+
292+ if pending_htlcs. iter ( ) . filter ( |htlc| htlc. outbound ) . count ( ) + 1 > counterparty_channel_constraints. max_accepted_htlcs as usize {
293+ available_capacity_msat = 0 ;
294+ }
295+
296+ crate :: ln:: channel:: AvailableBalances {
297+ inbound_capacity_msat : remote_stats. counterparty_balance_before_fee_msat . unwrap_or ( 0 ) . saturating_sub ( counterparty_channel_constraints. channel_reserve_satoshis * 1000 ) ,
298+ outbound_capacity_msat,
299+ next_outbound_htlc_limit_msat : available_capacity_msat,
300+ next_outbound_htlc_minimum_msat,
301+ }
302+ }
155303 fn get_next_commitment_stats (
156304 & self , local : bool , is_outbound_from_holder : bool , channel_value_satoshis : u64 ,
157305 value_to_holder_msat : u64 , next_commitment_htlcs : & [ HTLCAmountDirection ] ,
0 commit comments