@@ -24,6 +24,7 @@ use crate::ln::chan_utils::{
24
24
} ;
25
25
use crate :: events:: Event ;
26
26
use crate :: prelude:: HashMap ;
27
+ use crate :: sync:: Mutex ;
27
28
use crate :: util:: logger:: Logger ;
28
29
29
30
use bitcoin:: { OutPoint , PackedLockTime , Sequence , Script , Transaction , Txid , TxIn , TxOut , Witness } ;
@@ -306,7 +307,8 @@ pub struct CoinSelection {
306
307
307
308
/// An abstraction over a bitcoin wallet that can perform coin selection over a set of UTXOs and can
308
309
/// sign for them. The coin selection method aims to mimic Bitcoin Core's `fundrawtransaction` RPC,
309
- /// which most wallets should be able to satisfy.
310
+ /// which most wallets should be able to satisfy. Otherwise, consider implementing [`WalletSource`],
311
+ /// which can provide a default implementation of this trait when used with [`Wallet`].
310
312
pub trait CoinSelectionSource {
311
313
/// Performs coin selection of a set of UTXOs, with at least 1 confirmation each, that are
312
314
/// available to spend. Implementations are free to pick their coin selection algorithm of
@@ -347,6 +349,138 @@ pub trait CoinSelectionSource {
347
349
fn sign_tx ( & self , tx : & mut Transaction ) -> Result < ( ) , ( ) > ;
348
350
}
349
351
352
+ /// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
353
+ /// provide a default implementation to [`CoinSelectionSource`].
354
+ pub trait WalletSource {
355
+ /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
356
+ fn list_confirmed_utxos ( & self ) -> Result < Vec < Utxo > , ( ) > ;
357
+ /// Returns a script to use for change above dust resulting from a successful coin selection
358
+ /// attempt.
359
+ fn get_change_script ( & self ) -> Result < Script , ( ) > ;
360
+ /// Signs and provides the full witness for all inputs within the transaction known to the
361
+ /// wallet (i.e., any provided via [`WalletSource::list_confirmed_utxos`]).
362
+ fn sign_tx ( & self , tx : & mut Transaction ) -> Result < ( ) , ( ) > ;
363
+ }
364
+
365
+ /// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
366
+ /// avoid conflicting double spends. If not enough UTXOs are available to do so, conflicting double
367
+ /// spends may happen.
368
+ pub struct Wallet < W : Deref > where W :: Target : WalletSource {
369
+ source : W ,
370
+ // TODO: Do we care about cleaning this up once the UTXOs have a confirmed spend? We can do so
371
+ // by checking whether any UTXOs that exist in the map are no longer returned in
372
+ // `list_confirmed_utxos`.
373
+ locked_utxos : Mutex < HashMap < OutPoint , ClaimId > > ,
374
+ }
375
+
376
+ impl < W : Deref > Wallet < W > where W :: Target : WalletSource {
377
+ /// Returns a new instance backed by the given [`WalletSource`] that serves as an implementation
378
+ /// of [`CoinSelectionSource`].
379
+ pub fn new ( source : W ) -> Self {
380
+ Self { source, locked_utxos : Mutex :: new ( HashMap :: new ( ) ) }
381
+ }
382
+
383
+ /// Performs coin selection on the set of UTXOs obtained from
384
+ /// [`WalletSource::list_confirmed_utxos`]. Its algorithm can be described as "smallest
385
+ /// above-dust-after-spend first", with a slight twist: we may skip UTXOs that are above dust at
386
+ /// the target feerate after having spent them in a separate claim transaction if
387
+ /// `force_conflicting_utxo_spend` is unset to avoid producing conflicting transactions.
388
+ fn select_confirmed_utxos_internal (
389
+ & self , claim_id : ClaimId , force_conflicting_utxo_spend : bool ,
390
+ target_feerate_sat_per_1000_weight : u32 , preexisting_tx_weight : u64 , target_amount : u64 ,
391
+ ) -> Result < CoinSelection , ( ) > {
392
+ let utxos = self . source . list_confirmed_utxos ( ) ?;
393
+ let mut locked_utxos = self . locked_utxos . lock ( ) . unwrap ( ) ;
394
+ let mut eligible_utxos = utxos. into_iter ( ) . filter_map ( |utxo| {
395
+ if let Some ( utxo_claim_id) = locked_utxos. get ( & utxo. outpoint ) {
396
+ if * utxo_claim_id != claim_id && !force_conflicting_utxo_spend {
397
+ return None ;
398
+ }
399
+ }
400
+ let fee_to_spend_utxo = target_feerate_sat_per_1000_weight as u64 *
401
+ ( ( 41 * WITNESS_SCALE_FACTOR ) as u64 + utxo. witness_weight ) ;
402
+ let utxo_value_after_fee = utxo. output . value . saturating_sub ( fee_to_spend_utxo) ;
403
+ if utxo_value_after_fee > 0 {
404
+ Some ( ( utxo, fee_to_spend_utxo) )
405
+ } else {
406
+ None
407
+ }
408
+ } ) . collect :: < Vec < _ > > ( ) ;
409
+ eligible_utxos. sort_unstable_by_key ( |( utxo, _) | utxo. output . value ) ;
410
+
411
+ let mut selected_amount = 0 ;
412
+ let mut total_fees = preexisting_tx_weight * target_feerate_sat_per_1000_weight as u64 ;
413
+ let selected_utxos = eligible_utxos. into_iter ( ) . scan (
414
+ ( & mut selected_amount, & mut total_fees) , |( selected_amount, total_fees) , ( utxo, fee_to_spend_utxo) | {
415
+ let need_more_inputs = * * selected_amount < target_amount + * * total_fees;
416
+ if need_more_inputs {
417
+ * * selected_amount += utxo. output . value ;
418
+ * * total_fees += fee_to_spend_utxo;
419
+ Some ( utxo)
420
+ } else {
421
+ None
422
+ }
423
+ }
424
+ ) . collect :: < Vec < _ > > ( ) ;
425
+ let need_more_inputs = selected_amount < target_amount + total_fees;
426
+ if need_more_inputs {
427
+ return Err ( ( ) ) ;
428
+ }
429
+ for utxo in & selected_utxos {
430
+ locked_utxos. insert ( utxo. outpoint , claim_id) ;
431
+ }
432
+ core:: mem:: drop ( locked_utxos) ;
433
+
434
+ let remaining_amount = selected_amount - target_amount - total_fees;
435
+ let change_script = self . source . get_change_script ( ) ?;
436
+ let change_output_fee = target_feerate_sat_per_1000_weight as u64
437
+ * ( 8 + change_script. consensus_encode ( & mut sink ( ) ) . unwrap ( ) as u64 ) ;
438
+ let change_output_amount = remaining_amount. saturating_sub ( change_output_fee) ;
439
+ let change_output = if change_output_amount < change_script. dust_value ( ) . to_sat ( ) {
440
+ None
441
+ } else {
442
+ Some ( TxOut { script_pubkey : change_script, value : change_output_amount } )
443
+ } ;
444
+
445
+ Ok ( CoinSelection {
446
+ confirmed_utxos : selected_utxos,
447
+ change_output,
448
+ } )
449
+ }
450
+ }
451
+
452
+ impl < W : Deref > CoinSelectionSource for Wallet < W > where W :: Target : WalletSource {
453
+ fn select_confirmed_utxos (
454
+ & self , claim_id : ClaimId , must_spend : & [ Input ] , must_pay_to : & [ TxOut ] ,
455
+ target_feerate_sat_per_1000_weight : u32 ,
456
+ ) -> Result < CoinSelection , ( ) > {
457
+ // TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
458
+ let base_tx_weight = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */ ;
459
+ let total_input_weight = must_spend. len ( ) *
460
+ ( 32 /* txid */ + 4 /* vout */ + 4 /* sequence */ + 1 /* script sig */ ) ;
461
+ let total_output_weight: usize = must_pay_to. iter ( ) . map ( |output|
462
+ 8 /* value */ + 1 /* script len */ + output. script_pubkey . len ( )
463
+ ) . sum ( ) ;
464
+ let total_non_witness_weight = base_tx_weight + total_input_weight + total_output_weight;
465
+ let total_witness_weight: u64 = must_spend. iter ( ) . map ( |input| input. witness_weight ) . sum ( ) ;
466
+
467
+ let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_witness_weight +
468
+ ( total_non_witness_weight * WITNESS_SCALE_FACTOR ) as u64 ;
469
+ let target_amount = must_pay_to. iter ( ) . map ( |output| output. value ) . sum ( ) ;
470
+ let do_coin_selection = |force_conflicting_utxo_spend : bool | {
471
+ self . select_confirmed_utxos_internal (
472
+ claim_id, force_conflicting_utxo_spend, target_feerate_sat_per_1000_weight,
473
+ preexisting_tx_weight, target_amount,
474
+ )
475
+ } ;
476
+ do_coin_selection ( false ) . or_else ( |_| do_coin_selection ( true ) )
477
+ }
478
+
479
+ fn sign_tx ( & self , tx : & mut Transaction ) -> Result < ( ) , ( ) > {
480
+ self . source . sign_tx ( tx)
481
+ }
482
+ }
483
+
350
484
/// A handler for [`Event::BumpTransaction`] events that sources confirmed UTXOs from a
351
485
/// [`CoinSelectionSource`] to fee bump transactions via Child-Pays-For-Parent (CPFP) or
352
486
/// Replace-By-Fee (RBF).
0 commit comments