@@ -25,6 +25,7 @@ use crate::chain::chainmonitor::ChainMonitor;
25
25
use crate :: chain:: chainmonitor:: Persist ;
26
26
use crate :: chain:: Filter ;
27
27
use crate :: events:: bump_transaction:: Utxo ;
28
+ use crate :: ln:: chan_utils:: MAX_HTLCS ;
28
29
use crate :: ln:: channelmanager:: AChannelManager ;
29
30
use crate :: prelude:: new_hash_set;
30
31
use crate :: sign:: ecdsa:: EcdsaChannelSigner ;
@@ -33,6 +34,7 @@ use bitcoin::constants::WITNESS_SCALE_FACTOR;
33
34
use bitcoin:: Amount ;
34
35
use bitcoin:: FeeRate ;
35
36
use bitcoin:: Weight ;
37
+ use core:: cmp:: min;
36
38
use core:: ops:: Deref ;
37
39
38
40
// Transaction weights based on:
@@ -150,6 +152,15 @@ pub struct AnchorChannelReserveContext {
150
152
pub taproot_wallet : bool ,
151
153
}
152
154
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
+
153
164
/// A default for the [AnchorChannelReserveContext] parameters is provided as follows:
154
165
/// - The upper bound fee rate is set to the 99th percentile of the median block fee rate since 2019:
155
166
/// ~50 sats/vbyte.
@@ -171,28 +182,31 @@ impl Default for AnchorChannelReserveContext {
171
182
///
172
183
/// This reserve currently needs to be allocated as a disjoint set of UTXOs per channel,
173
184
/// 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 ;
175
189
let weight = Weight :: from_wu (
176
190
COMMITMENT_TRANSACTION_BASE_WEIGHT +
177
191
// Reserves are calculated in terms of accepted HTLCs, as their timeout defines the urgency of
178
192
// on-chain resolution. Each accepted HTLC is assumed to be forwarded to calculate an upper
179
193
// bound for the reserve, resulting in `expected_accepted_htlcs` inbound HTLCs and
180
194
// `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 +
182
196
anchor_output_spend_transaction_weight ( context) +
183
197
// As an upper bound, it is assumed that each HTLC is resolved in a separate transaction.
184
198
// 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 ,
187
201
) ;
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 )
189
203
}
190
204
191
205
/// Calculates the number of anchor channels that can be supported by the reserve provided
192
206
/// by `utxos`.
193
207
pub fn get_supportable_anchor_channels (
194
208
context : & AnchorChannelReserveContext , utxos : & [ Utxo ] ,
195
- ) -> u64 {
209
+ ) -> Result < u64 , AnchorChannelReserveError > {
196
210
// Get the reserve needed per channel, replacing the fee for an initial spend with the actual value
197
211
// below.
198
212
let default_satisfaction_fee = context
@@ -202,30 +216,36 @@ pub fn get_supportable_anchor_channels(
202
216
} else {
203
217
P2WPKH_INPUT_WEIGHT
204
218
} ) )
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;
207
221
208
222
let mut total_fractional_amount = Amount :: from_sat ( 0 ) ;
209
223
let mut num_whole_utxos = 0 ;
210
224
for utxo in utxos {
211
225
let satisfaction_fee = context
212
226
. upper_bound_fee_rate
213
227
. 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
+ } ;
216
235
if amount >= reserve_per_channel {
217
236
num_whole_utxos += 1 ;
218
237
} 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 ) ?;
221
241
}
222
242
}
223
243
// We require disjoint sets of UTXOs for the reserve of each channel,
224
244
// as claims are currently only aggregated per channel.
225
245
//
226
246
// A worst-case coin selection is assumed for fractional UTXOs, selecting up to double the
227
247
// 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 )
229
249
}
230
250
231
251
/// Verifies whether the anchor channel reserve provided by `utxos` is sufficient to support
@@ -258,7 +278,7 @@ pub fn can_support_additional_anchor_channel<
258
278
> (
259
279
context : & AnchorChannelReserveContext , utxos : & [ Utxo ] , a_channel_manager : & AChannelManagerRef ,
260
280
chain_monitor : & ChainMonitorRef ,
261
- ) -> bool
281
+ ) -> Result < bool , AnchorChannelReserveError >
262
282
where
263
283
AChannelManagerRef :: Target : AChannelManager ,
264
284
FilterRef :: Target : Filter ,
@@ -289,7 +309,8 @@ where
289
309
anchor_channels. insert ( channel. channel_id ) ;
290
310
}
291
311
}
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 )
293
314
}
294
315
295
316
#[ cfg( test) ]
@@ -308,7 +329,7 @@ mod test {
308
329
expected_accepted_htlcs: 1 ,
309
330
taproot_wallet: false ,
310
331
} ) ,
311
- Amount :: from_sat( 4349 )
332
+ Ok ( Amount :: from_sat( 4349 ) )
312
333
) ;
313
334
}
314
335
@@ -329,7 +350,7 @@ mod test {
329
350
#[ test]
330
351
fn test_get_supportable_anchor_channels ( ) {
331
352
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 ( ) ;
333
354
// Only 3 disjoint sets with a value greater than the required reserve can be created.
334
355
let utxos = vec ! [
335
356
make_p2wpkh_utxo( reserve_per_channel * 3 / 2 ) ,
@@ -338,7 +359,7 @@ mod test {
338
359
make_p2wpkh_utxo( reserve_per_channel * 99 / 100 ) ,
339
360
make_p2wpkh_utxo( reserve_per_channel * 20 / 100 ) ,
340
361
] ;
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 ) ) ;
342
363
}
343
364
344
365
#[ test]
0 commit comments