@@ -144,7 +144,7 @@ fn subtract_addl_outputs(
144144 }
145145}
146146
147- pub ( crate ) fn get_dust_buffer_feerate ( feerate_per_kw : u32 ) -> u32 {
147+ fn get_dust_buffer_feerate ( feerate_per_kw : u32 ) -> u32 {
148148 // When calculating our exposure to dust HTLCs, we assume that the channel feerate
149149 // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
150150 // whichever is higher. This ensures that we aren't suddenly exposed to significantly
@@ -163,6 +163,19 @@ pub(crate) struct ChannelConstraints {
163163}
164164
165165pub ( crate ) trait TxBuilder {
166+ fn get_available_balances (
167+ & self ,
168+ is_outbound_from_holder : bool ,
169+ channel_value_satoshis : u64 ,
170+ value_to_holder_msat : u64 ,
171+ pending_htlcs : & [ HTLCAmountDirection ] ,
172+ feerate_per_kw : u32 ,
173+ dust_exposure_limiting_feerate : Option < u32 > ,
174+ max_dust_htlc_exposure_msat : u64 ,
175+ holder_channel_constraints : ChannelConstraints ,
176+ counterparty_channel_constraints : ChannelConstraints ,
177+ channel_type : & ChannelTypeFeatures ,
178+ ) -> crate :: ln:: channel:: AvailableBalances ;
166179 fn get_next_commitment_stats (
167180 & self , local : bool , is_outbound_from_holder : bool , channel_value_satoshis : u64 ,
168181 value_to_holder_msat : u64 , next_commitment_htlcs : & [ HTLCAmountDirection ] ,
@@ -190,6 +203,137 @@ pub(crate) trait TxBuilder {
190203pub ( crate ) struct SpecTxBuilder { }
191204
192205impl TxBuilder for SpecTxBuilder {
206+ fn get_available_balances (
207+ & self ,
208+ is_outbound_from_holder : bool ,
209+ channel_value_satoshis : u64 ,
210+ value_to_holder_msat : u64 ,
211+ pending_htlcs : & [ HTLCAmountDirection ] ,
212+ feerate_per_kw : u32 ,
213+ dust_exposure_limiting_feerate : Option < u32 > ,
214+ max_dust_htlc_exposure_msat : u64 ,
215+ holder_channel_constraints : ChannelConstraints ,
216+ counterparty_channel_constraints : ChannelConstraints ,
217+ channel_type : & ChannelTypeFeatures ,
218+ ) -> crate :: ln:: channel:: AvailableBalances {
219+ let fee_spike_buffer_htlc = if channel_type. supports_anchor_zero_fee_commitments ( ) {
220+ 0
221+ } else {
222+ 1
223+ } ;
224+
225+ 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) . unwrap ( ) ;
226+ 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) . unwrap ( ) ;
227+ 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) . unwrap ( ) ;
228+
229+ let outbound_capacity_msat = local_stats_max_fee. holder_balance_before_fee_msat . saturating_sub ( holder_channel_constraints. channel_reserve_satoshis * 1000 ) ;
230+
231+ let mut available_capacity_msat = outbound_capacity_msat;
232+ let ( real_htlc_success_tx_fee_sat, real_htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat (
233+ channel_type, feerate_per_kw
234+ ) ;
235+
236+ if is_outbound_from_holder {
237+ // We should mind channel commit tx fee when computing how much of the available capacity
238+ // can be used in the next htlc. Mirrors the logic in send_htlc.
239+ //
240+ // The fee depends on whether the amount we will be sending is above dust or not,
241+ // and the answer will in turn change the amount itself — making it a circular
242+ // dependency.
243+ // This complicates the computation around dust-values, up to the one-htlc-value.
244+
245+ let real_dust_limit_timeout_sat = real_htlc_timeout_tx_fee_sat + holder_channel_constraints. dust_limit_satoshis ;
246+ let mut max_reserved_commit_tx_fee_msat = local_stats_max_fee. commit_tx_fee_sat * 1000 ;
247+ let mut min_reserved_commit_tx_fee_msat = local_stats_min_fee. commit_tx_fee_sat * 1000 ;
248+
249+ if !channel_type. supports_anchors_zero_fee_htlc_tx ( ) {
250+ max_reserved_commit_tx_fee_msat *= crate :: ln:: channel:: FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE ;
251+ min_reserved_commit_tx_fee_msat *= crate :: ln:: channel:: FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE ;
252+ }
253+
254+ // We will first subtract the fee as if we were above-dust. Then, if the resulting
255+ // value ends up being below dust, we have this fee available again. In that case,
256+ // match the value to right-below-dust.
257+ let capacity_minus_max_commitment_fee_msat = available_capacity_msat. saturating_sub ( max_reserved_commit_tx_fee_msat) ;
258+ if capacity_minus_max_commitment_fee_msat < real_dust_limit_timeout_sat * 1000 {
259+ let capacity_minus_min_commitment_fee_msat = available_capacity_msat. saturating_sub ( min_reserved_commit_tx_fee_msat) ;
260+ available_capacity_msat = cmp:: min ( real_dust_limit_timeout_sat * 1000 - 1 , capacity_minus_min_commitment_fee_msat) ;
261+ } else {
262+ available_capacity_msat = capacity_minus_max_commitment_fee_msat;
263+ }
264+ } else {
265+ // If the channel is inbound (i.e. counterparty pays the fee), we need to make sure
266+ // sending a new HTLC won't reduce their balance below our reserve threshold.
267+ let real_dust_limit_success_sat = real_htlc_success_tx_fee_sat + counterparty_channel_constraints. dust_limit_satoshis ;
268+ let max_reserved_commit_tx_fee_msat = remote_stats. commit_tx_fee_sat * 1000 ;
269+
270+ let holder_selected_chan_reserve_msat = counterparty_channel_constraints. channel_reserve_satoshis * 1000 ;
271+ if remote_stats. counterparty_balance_before_fee_msat < max_reserved_commit_tx_fee_msat + holder_selected_chan_reserve_msat {
272+ // If another HTLC's fee would reduce the remote's balance below the reserve limit
273+ // we've selected for them, we can only send dust HTLCs.
274+ available_capacity_msat = cmp:: min ( available_capacity_msat, real_dust_limit_success_sat * 1000 - 1 ) ;
275+ }
276+ }
277+
278+ let mut next_outbound_htlc_minimum_msat = counterparty_channel_constraints. htlc_minimum_msat ;
279+
280+ // If we get close to our maximum dust exposure, we end up in a situation where we can send
281+ // between zero and the remaining dust exposure limit remaining OR above the dust limit.
282+ // Because we cannot express this as a simple min/max, we prefer to tell the user they can
283+ // send above the dust limit (as the router can always overpay to meet the dust limit).
284+ let mut remaining_msat_below_dust_exposure_limit = None ;
285+ let mut dust_exposure_dust_limit_msat = 0 ;
286+
287+ let dust_buffer_feerate = get_dust_buffer_feerate ( feerate_per_kw) ;
288+ let ( buffer_htlc_success_tx_fee_sat, buffer_htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat (
289+ channel_type, dust_buffer_feerate,
290+ ) ;
291+ let buffer_dust_limit_success_sat = buffer_htlc_success_tx_fee_sat + counterparty_channel_constraints. dust_limit_satoshis ;
292+ let buffer_dust_limit_timeout_sat = buffer_htlc_timeout_tx_fee_sat + holder_channel_constraints. dust_limit_satoshis ;
293+
294+ if remote_stats. extra_accepted_htlc_dust_exposure_msat > max_dust_htlc_exposure_msat {
295+ // If adding an extra HTLC would put us over the dust limit in total fees, we cannot
296+ // send any non-dust HTLCs.
297+ available_capacity_msat = cmp:: min ( available_capacity_msat, buffer_dust_limit_success_sat * 1000 ) ;
298+ }
299+
300+ if remote_stats. dust_exposure_msat . saturating_add ( buffer_dust_limit_success_sat * 1000 ) > max_dust_htlc_exposure_msat. saturating_add ( 1 ) {
301+ // Note that we don't use the `counterparty_tx_dust_exposure` (with
302+ // `htlc_dust_exposure_msat`) here as it only applies to non-dust HTLCs.
303+ remaining_msat_below_dust_exposure_limit =
304+ Some ( max_dust_htlc_exposure_msat. saturating_sub ( remote_stats. dust_exposure_msat ) ) ;
305+ dust_exposure_dust_limit_msat = cmp:: max ( dust_exposure_dust_limit_msat, buffer_dust_limit_success_sat * 1000 ) ;
306+ }
307+
308+ 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 ( ) ) {
309+ remaining_msat_below_dust_exposure_limit = Some ( cmp:: min (
310+ remaining_msat_below_dust_exposure_limit. unwrap_or ( u64:: max_value ( ) ) ,
311+ max_dust_htlc_exposure_msat. saturating_sub ( local_stats_max_fee. dust_exposure_msat ) ) ) ;
312+ dust_exposure_dust_limit_msat = cmp:: max ( dust_exposure_dust_limit_msat, buffer_dust_limit_timeout_sat * 1000 ) ;
313+ }
314+
315+ if let Some ( remaining_limit_msat) = remaining_msat_below_dust_exposure_limit {
316+ if available_capacity_msat < dust_exposure_dust_limit_msat {
317+ available_capacity_msat = cmp:: min ( available_capacity_msat, remaining_limit_msat) ;
318+ } else {
319+ next_outbound_htlc_minimum_msat = cmp:: max ( next_outbound_htlc_minimum_msat, dust_exposure_dust_limit_msat) ;
320+ }
321+ }
322+
323+ available_capacity_msat = cmp:: min ( available_capacity_msat,
324+ counterparty_channel_constraints. max_htlc_value_in_flight_msat - pending_htlcs. iter ( ) . filter ( |htlc| htlc. outbound ) . map ( |htlc| htlc. amount_msat ) . sum :: < u64 > ( ) ) ;
325+
326+ if pending_htlcs. iter ( ) . filter ( |htlc| htlc. outbound ) . count ( ) + 1 > counterparty_channel_constraints. max_accepted_htlcs as usize {
327+ available_capacity_msat = 0 ;
328+ }
329+
330+ crate :: ln:: channel:: AvailableBalances {
331+ inbound_capacity_msat : remote_stats. counterparty_balance_before_fee_msat . saturating_sub ( counterparty_channel_constraints. channel_reserve_satoshis * 1000 ) ,
332+ outbound_capacity_msat,
333+ next_outbound_htlc_limit_msat : available_capacity_msat,
334+ next_outbound_htlc_minimum_msat,
335+ }
336+ }
193337 fn get_next_commitment_stats (
194338 & self , local : bool , is_outbound_from_holder : bool , channel_value_satoshis : u64 ,
195339 value_to_holder_msat : u64 , next_commitment_htlcs : & [ HTLCAmountDirection ] ,
0 commit comments