@@ -24,6 +24,7 @@ use crate::ln::chan_utils::{
2424} ;
2525use crate :: events:: Event ;
2626use crate :: prelude:: HashMap ;
27+ use crate :: sync:: Mutex ;
2728use crate :: util:: logger:: Logger ;
2829
2930use bitcoin:: { OutPoint , PackedLockTime , PubkeyHash , Sequence , Script , Transaction , Txid , TxIn , TxOut , Witness , WPubkeyHash } ;
@@ -35,6 +36,8 @@ use bitcoin::secp256k1::ecdsa::Signature;
3536
3637const EMPTY_SCRIPT_SIG_WEIGHT : u64 = 1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64 ;
3738
39+ const BASE_INPUT_WEIGHT : u64 = 32 /* txid */ + 4 /* vout */ + 4 /* sequence */ ;
40+
3841/// A descriptor used to sign for a commitment transaction's anchor output.
3942#[ derive( Clone , Debug , PartialEq , Eq ) ]
4043pub struct AnchorDescriptor {
@@ -356,7 +359,8 @@ pub struct CoinSelection {
356359
357360/// An abstraction over a bitcoin wallet that can perform coin selection over a set of UTXOs and can
358361/// sign for them. The coin selection method aims to mimic Bitcoin Core's `fundrawtransaction` RPC,
359- /// which most wallets should be able to satisfy.
362+ /// which most wallets should be able to satisfy. Otherwise, consider implementing [`WalletSource`],
363+ /// which can provide a default implementation of this trait when used with [`Wallet`].
360364pub trait CoinSelectionSource {
361365 /// Performs coin selection of a set of UTXOs, with at least 1 confirmation each, that are
362366 /// available to spend. Implementations are free to pick their coin selection algorithm of
@@ -394,6 +398,143 @@ pub trait CoinSelectionSource {
394398 fn sign_tx ( & self , tx : & mut Transaction ) -> Result < ( ) , ( ) > ;
395399}
396400
401+ /// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
402+ /// provide a default implementation to [`CoinSelectionSource`].
403+ pub trait WalletSource {
404+ /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
405+ fn list_confirmed_utxos ( & self ) -> Result < Vec < Utxo > , ( ) > ;
406+ /// Returns a script to use for change above dust resulting from a successful coin selection
407+ /// attempt.
408+ fn get_change_script ( & self ) -> Result < Script , ( ) > ;
409+ /// Signs and provides the full witness for all inputs within the transaction known to the
410+ /// wallet (i.e., any provided via [`WalletSource::list_confirmed_utxos`]).
411+ fn sign_tx ( & self , tx : & mut Transaction ) -> Result < ( ) , ( ) > ;
412+ }
413+
414+ /// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
415+ /// avoid conflicting double spends. If not enough UTXOs are available to do so, conflicting double
416+ /// spends may happen.
417+ pub struct Wallet < W : Deref > where W :: Target : WalletSource {
418+ source : W ,
419+ // TODO: Do we care about cleaning this up once the UTXOs have a confirmed spend? We can do so
420+ // by checking whether any UTXOs that exist in the map are no longer returned in
421+ // `list_confirmed_utxos`.
422+ locked_utxos : Mutex < HashMap < OutPoint , ClaimId > > ,
423+ }
424+
425+ impl < W : Deref > Wallet < W > where W :: Target : WalletSource {
426+ /// Returns a new instance backed by the given [`WalletSource`] that serves as an implementation
427+ /// of [`CoinSelectionSource`].
428+ pub fn new ( source : W ) -> Self {
429+ Self { source, locked_utxos : Mutex :: new ( HashMap :: new ( ) ) }
430+ }
431+
432+ /// Performs coin selection on the set of UTXOs obtained from
433+ /// [`WalletSource::list_confirmed_utxos`]. Its algorithm can be described as "smallest
434+ /// above-dust-after-spend first", with a slight twist: we may skip UTXOs that are above dust at
435+ /// the target feerate after having spent them in a separate claim transaction if
436+ /// `force_conflicting_utxo_spend` is unset to avoid producing conflicting transactions. If
437+ /// `tolerate_high_network_feerates` is set, we'll attempt to spend UTXOs that contribute at
438+ /// least 1 satoshi at the current feerate, otherwise, we'll only attempt to spend those which
439+ /// contribute at least twice their fee.
440+ fn select_confirmed_utxos_internal (
441+ & self , utxos : & [ Utxo ] , claim_id : ClaimId , force_conflicting_utxo_spend : bool ,
442+ tolerate_high_network_feerates : bool , target_feerate_sat_per_1000_weight : u32 ,
443+ preexisting_tx_weight : u64 , target_amount : u64 ,
444+ ) -> Result < CoinSelection , ( ) > {
445+ let mut locked_utxos = self . locked_utxos . lock ( ) . unwrap ( ) ;
446+ let mut eligible_utxos = utxos. iter ( ) . filter_map ( |utxo| {
447+ if let Some ( utxo_claim_id) = locked_utxos. get ( & utxo. outpoint ) {
448+ if * utxo_claim_id != claim_id && !force_conflicting_utxo_spend {
449+ return None ;
450+ }
451+ }
452+ let fee_to_spend_utxo = target_feerate_sat_per_1000_weight as u64 *
453+ ( ( 40 * WITNESS_SCALE_FACTOR ) as u64 + utxo. satisfaction_weight ) / 1000 ;
454+ let should_spend = if tolerate_high_network_feerates {
455+ utxo. output . value > fee_to_spend_utxo
456+ } else {
457+ utxo. output . value >= fee_to_spend_utxo * 2
458+ } ;
459+ if should_spend {
460+ Some ( ( utxo, fee_to_spend_utxo) )
461+ } else {
462+ None
463+ }
464+ } ) . collect :: < Vec < _ > > ( ) ;
465+ eligible_utxos. sort_unstable_by_key ( |( utxo, _) | utxo. output . value ) ;
466+
467+ let mut selected_amount = 0 ;
468+ let mut total_fees = preexisting_tx_weight * target_feerate_sat_per_1000_weight as u64 ;
469+ let mut selected_utxos = Vec :: new ( ) ;
470+ for ( utxo, fee_to_spend_utxo) in eligible_utxos {
471+ if selected_amount >= target_amount + total_fees {
472+ break ;
473+ }
474+ selected_amount += utxo. output . value ;
475+ total_fees += fee_to_spend_utxo;
476+ selected_utxos. push ( utxo. clone ( ) ) ;
477+ }
478+ if selected_amount < target_amount + total_fees {
479+ return Err ( ( ) ) ;
480+ }
481+ for utxo in & selected_utxos {
482+ locked_utxos. insert ( utxo. outpoint , claim_id) ;
483+ }
484+ core:: mem:: drop ( locked_utxos) ;
485+
486+ let remaining_amount = selected_amount - target_amount - total_fees;
487+ let change_script = self . source . get_change_script ( ) ?;
488+ let change_output_fee = target_feerate_sat_per_1000_weight as u64
489+ * ( 8 /* value */ + change_script. consensus_encode ( & mut sink ( ) ) . unwrap ( ) as u64 ) ;
490+ let change_output_amount = remaining_amount. saturating_sub ( change_output_fee) ;
491+ let change_output = if change_output_amount < change_script. dust_value ( ) . to_sat ( ) {
492+ None
493+ } else {
494+ Some ( TxOut { script_pubkey : change_script, value : change_output_amount } )
495+ } ;
496+
497+ Ok ( CoinSelection {
498+ confirmed_utxos : selected_utxos,
499+ change_output,
500+ } )
501+ }
502+ }
503+
504+ impl < W : Deref > CoinSelectionSource for Wallet < W > where W :: Target : WalletSource {
505+ fn select_confirmed_utxos (
506+ & self , claim_id : ClaimId , must_spend : & [ Input ] , must_pay_to : & [ TxOut ] ,
507+ target_feerate_sat_per_1000_weight : u32 ,
508+ ) -> Result < CoinSelection , ( ) > {
509+ let utxos = self . source . list_confirmed_utxos ( ) ?;
510+ // TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
511+ const BASE_TX_WEIGHT : u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */ ;
512+ let total_output_weight: u64 = must_pay_to. iter ( ) . map ( |output|
513+ 8 /* value */ + 1 /* script len */ + output. script_pubkey . len ( ) as u64
514+ ) . sum ( ) ;
515+ let total_non_witness_weight = BASE_TX_WEIGHT + ( BASE_INPUT_WEIGHT * must_spend. len ( ) as u64 ) + total_output_weight;
516+ let total_satisfaction_weight: u64 = must_spend. iter ( ) . map ( |input| input. satisfaction_weight ) . sum ( ) ;
517+
518+ let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_satisfaction_weight +
519+ ( total_non_witness_weight * WITNESS_SCALE_FACTOR as u64 ) ;
520+ let target_amount = must_pay_to. iter ( ) . map ( |output| output. value ) . sum ( ) ;
521+ let do_coin_selection = |force_conflicting_utxo_spend : bool , tolerate_high_network_feerates : bool | {
522+ self . select_confirmed_utxos_internal (
523+ & utxos, claim_id, force_conflicting_utxo_spend, tolerate_high_network_feerates,
524+ target_feerate_sat_per_1000_weight, preexisting_tx_weight, target_amount,
525+ )
526+ } ;
527+ do_coin_selection ( false , false )
528+ . or_else ( |_| do_coin_selection ( false , true ) )
529+ . or_else ( |_| do_coin_selection ( true , false ) )
530+ . or_else ( |_| do_coin_selection ( true , true ) )
531+ }
532+
533+ fn sign_tx ( & self , tx : & mut Transaction ) -> Result < ( ) , ( ) > {
534+ self . source . sign_tx ( tx)
535+ }
536+ }
537+
397538/// A handler for [`Event::BumpTransaction`] events that sources confirmed UTXOs from a
398539/// [`CoinSelectionSource`] to fee bump transactions via Child-Pays-For-Parent (CPFP) or
399540/// Replace-By-Fee (RBF).
0 commit comments