@@ -25,6 +25,7 @@ use crate::chain::chainmonitor::ChainMonitor;
2525use crate :: chain:: chainmonitor:: Persist ;
2626use crate :: chain:: Filter ;
2727use crate :: events:: bump_transaction:: Utxo ;
28+ use crate :: ln:: chan_utils:: MAX_HTLCS ;
2829use crate :: ln:: channelmanager:: AChannelManager ;
2930use crate :: prelude:: new_hash_set;
3031use crate :: sign:: ecdsa:: EcdsaChannelSigner ;
@@ -33,6 +34,7 @@ use bitcoin::constants::WITNESS_SCALE_FACTOR;
3334use bitcoin:: Amount ;
3435use bitcoin:: FeeRate ;
3536use bitcoin:: Weight ;
37+ use core:: cmp:: min;
3638use core:: ops:: Deref ;
3739
3840// Transaction weights based on:
@@ -150,6 +152,15 @@ pub struct AnchorChannelReserveContext {
150152 pub taproot_wallet : bool ,
151153}
152154
155+ /// Errors around anchor channel reserve calculations.
156+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
157+ pub enum AnchorChannelReserveError {
158+ /// An overflow occurred in a fee calculation, caused by an excessive fee rate or weight.
159+ FeeOverflow ,
160+ /// An overflow occurred calculating a total amount of reserves.
161+ AmountOverflow ,
162+ }
163+
153164/// A default for the [AnchorChannelReserveContext] parameters is provided as follows:
154165/// - The upper bound fee rate is set to the 99th percentile of the median block fee rate since 2019:
155166/// ~50 sats/vbyte.
@@ -171,28 +182,31 @@ impl Default for AnchorChannelReserveContext {
171182///
172183/// This reserve currently needs to be allocated as a disjoint set of UTXOs per channel,
173184/// as claims are not yet aggregated across channels.
174- pub fn get_reserve_per_channel ( context : & AnchorChannelReserveContext ) -> Amount {
185+ pub fn get_reserve_per_channel (
186+ context : & AnchorChannelReserveContext ,
187+ ) -> Result < Amount , AnchorChannelReserveError > {
188+ let num_htlcs = min ( context. expected_accepted_htlcs , MAX_HTLCS ) as u64 ;
175189 let weight = Weight :: from_wu (
176190 COMMITMENT_TRANSACTION_BASE_WEIGHT +
177191 // Reserves are calculated in terms of accepted HTLCs, as their timeout defines the urgency of
178192 // on-chain resolution. Each accepted HTLC is assumed to be forwarded to calculate an upper
179193 // bound for the reserve, resulting in `expected_accepted_htlcs` inbound HTLCs and
180194 // `expected_accepted_htlcs` outbound HTLCs per channel in aggregate.
181- 2 * ( context . expected_accepted_htlcs as u64 ) * COMMITMENT_TRANSACTION_PER_HTLC_WEIGHT +
195+ 2 * num_htlcs * COMMITMENT_TRANSACTION_PER_HTLC_WEIGHT +
182196 anchor_output_spend_transaction_weight ( context) +
183197 // As an upper bound, it is assumed that each HTLC is resolved in a separate transaction.
184198 // However, they might be aggregated when possible depending on timelocks and expiries.
185- htlc_success_transaction_weight ( context) * ( context . expected_accepted_htlcs as u64 ) +
186- htlc_timeout_transaction_weight ( context) * ( context . expected_accepted_htlcs as u64 ) ,
199+ htlc_success_transaction_weight ( context) * num_htlcs +
200+ htlc_timeout_transaction_weight ( context) * num_htlcs ,
187201 ) ;
188- context. upper_bound_fee_rate . fee_wu ( weight) . unwrap_or ( Amount :: MAX )
202+ context. upper_bound_fee_rate . fee_wu ( weight) . ok_or ( AnchorChannelReserveError :: FeeOverflow )
189203}
190204
191205/// Calculates the number of anchor channels that can be supported by the reserve provided
192206/// by `utxos`.
193207pub fn get_supportable_anchor_channels (
194208 context : & AnchorChannelReserveContext , utxos : & [ Utxo ] ,
195- ) -> u64 {
209+ ) -> Result < u64 , AnchorChannelReserveError > {
196210 // Get the reserve needed per channel, replacing the fee for an initial spend with the actual value
197211 // below.
198212 let default_satisfaction_fee = context
@@ -202,30 +216,36 @@ pub fn get_supportable_anchor_channels(
202216 } else {
203217 P2WPKH_INPUT_WEIGHT
204218 } ) )
205- . unwrap_or ( Amount :: MAX ) ;
206- let reserve_per_channel = get_reserve_per_channel ( context) - default_satisfaction_fee;
219+ . ok_or ( AnchorChannelReserveError :: FeeOverflow ) ? ;
220+ let reserve_per_channel = get_reserve_per_channel ( context) ? - default_satisfaction_fee;
207221
208222 let mut total_fractional_amount = Amount :: from_sat ( 0 ) ;
209223 let mut num_whole_utxos = 0 ;
210224 for utxo in utxos {
211225 let satisfaction_fee = context
212226 . upper_bound_fee_rate
213227 . fee_wu ( Weight :: from_wu ( utxo. satisfaction_weight ) )
214- . unwrap_or ( Amount :: MAX ) ;
215- let amount = utxo. output . value . checked_sub ( satisfaction_fee) . unwrap_or ( Amount :: MIN ) ;
228+ . ok_or ( AnchorChannelReserveError :: FeeOverflow ) ?;
229+ let amount = if let Some ( amount) = utxo. output . value . checked_sub ( satisfaction_fee) {
230+ amount
231+ } else {
232+ // The UTXO is considered dust at the given fee rate.
233+ continue ;
234+ } ;
216235 if amount >= reserve_per_channel {
217236 num_whole_utxos += 1 ;
218237 } else {
219- total_fractional_amount =
220- total_fractional_amount. checked_add ( amount) . unwrap_or ( Amount :: MAX ) ;
238+ total_fractional_amount = total_fractional_amount
239+ . checked_add ( amount)
240+ . ok_or ( AnchorChannelReserveError :: AmountOverflow ) ?;
221241 }
222242 }
223243 // We require disjoint sets of UTXOs for the reserve of each channel,
224244 // as claims are currently only aggregated per channel.
225245 //
226246 // A worst-case coin selection is assumed for fractional UTXOs, selecting up to double the
227247 // required amount.
228- num_whole_utxos + total_fractional_amount. to_sat ( ) / reserve_per_channel. to_sat ( ) / 2
248+ Ok ( num_whole_utxos + total_fractional_amount. to_sat ( ) / reserve_per_channel. to_sat ( ) / 2 )
229249}
230250
231251/// Verifies whether the anchor channel reserve provided by `utxos` is sufficient to support
@@ -258,7 +278,7 @@ pub fn can_support_additional_anchor_channel<
258278> (
259279 context : & AnchorChannelReserveContext , utxos : & [ Utxo ] , a_channel_manager : & AChannelManagerRef ,
260280 chain_monitor : & ChainMonitorRef ,
261- ) -> bool
281+ ) -> Result < bool , AnchorChannelReserveError >
262282where
263283 AChannelManagerRef :: Target : AChannelManager ,
264284 FilterRef :: Target : Filter ,
@@ -289,7 +309,8 @@ where
289309 anchor_channels. insert ( channel. channel_id ) ;
290310 }
291311 }
292- get_supportable_anchor_channels ( context, utxos) > anchor_channels. len ( ) as u64
312+ get_supportable_anchor_channels ( context, utxos)
313+ . map ( |num_channels| num_channels > anchor_channels. len ( ) as u64 )
293314}
294315
295316#[ cfg( test) ]
@@ -308,7 +329,7 @@ mod test {
308329 expected_accepted_htlcs: 1 ,
309330 taproot_wallet: false ,
310331 } ) ,
311- Amount :: from_sat( 4349 )
332+ Ok ( Amount :: from_sat( 4349 ) )
312333 ) ;
313334 }
314335
@@ -329,7 +350,7 @@ mod test {
329350 #[ test]
330351 fn test_get_supportable_anchor_channels ( ) {
331352 let context = AnchorChannelReserveContext :: default ( ) ;
332- let reserve_per_channel = get_reserve_per_channel ( & context) ;
353+ let reserve_per_channel = get_reserve_per_channel ( & context) . unwrap ( ) ;
333354 // Only 3 disjoint sets with a value greater than the required reserve can be created.
334355 let utxos = vec ! [
335356 make_p2wpkh_utxo( reserve_per_channel * 3 / 2 ) ,
@@ -338,7 +359,7 @@ mod test {
338359 make_p2wpkh_utxo( reserve_per_channel * 99 / 100 ) ,
339360 make_p2wpkh_utxo( reserve_per_channel * 20 / 100 ) ,
340361 ] ;
341- assert_eq ! ( get_supportable_anchor_channels( & context, utxos. as_slice( ) ) , 3 ) ;
362+ assert_eq ! ( get_supportable_anchor_channels( & context, utxos. as_slice( ) ) , Ok ( 3 ) ) ;
342363 }
343364
344365 #[ test]
0 commit comments