1111//!
1212//! [`Event`]: crate::events::Event
1313
14+ pub mod sync;
15+
1416use alloc:: collections:: BTreeMap ;
1517use core:: ops:: Deref ;
1618
@@ -30,6 +32,7 @@ use crate::sign::{
3032 ChannelDerivationParameters , HTLCDescriptor , SignerProvider , P2WPKH_WITNESS_WEIGHT ,
3133} ;
3234use crate :: sync:: Mutex ;
35+ use crate :: util:: async_poll:: { AsyncResult , MaybeSend , MaybeSync } ;
3336use crate :: util:: logger:: Logger ;
3437
3538use bitcoin:: amount:: Amount ;
@@ -346,42 +349,42 @@ pub trait CoinSelectionSource {
346349 /// other claims, implementations must be willing to double spend their UTXOs. The choice of
347350 /// which UTXOs to double spend is left to the implementation, but it must strive to keep the
348351 /// set of other claims being double spent to a minimum.
349- fn select_confirmed_utxos (
350- & self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & [ TxOut ] ,
352+ fn select_confirmed_utxos < ' a > (
353+ & ' a self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
351354 target_feerate_sat_per_1000_weight : u32 ,
352- ) -> Result < CoinSelection , ( ) > ;
355+ ) -> AsyncResult < ' a , CoinSelection > ;
353356 /// Signs and provides the full witness for all inputs within the transaction known to the
354357 /// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
355358 ///
356359 /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
357360 /// unsigned transaction and then sign it with your wallet.
358- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > ;
361+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > ;
359362}
360363
361364/// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
362365/// provide a default implementation to [`CoinSelectionSource`].
363366pub trait WalletSource {
364367 /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
365- fn list_confirmed_utxos ( & self ) -> Result < Vec < Utxo > , ( ) > ;
368+ fn list_confirmed_utxos < ' a > ( & ' a self ) -> AsyncResult < ' a , Vec < Utxo > > ;
366369 /// Returns a script to use for change above dust resulting from a successful coin selection
367370 /// attempt.
368- fn get_change_script ( & self ) -> Result < ScriptBuf , ( ) > ;
371+ fn get_change_script < ' a > ( & ' a self ) -> AsyncResult < ' a , ScriptBuf > ;
369372 /// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
370373 /// the transaction known to the wallet (i.e., any provided via
371374 /// [`WalletSource::list_confirmed_utxos`]).
372375 ///
373376 /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
374377 /// unsigned transaction and then sign it with your wallet.
375- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > ;
378+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > ;
376379}
377380
378381/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
379382/// avoid conflicting double spends. If not enough UTXOs are available to do so, conflicting double
380383/// spends may happen.
381- pub struct Wallet < W : Deref , L : Deref >
384+ pub struct Wallet < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend >
382385where
383- W :: Target : WalletSource ,
384- L :: Target : Logger ,
386+ W :: Target : WalletSource + MaybeSend ,
387+ L :: Target : Logger + MaybeSend ,
385388{
386389 source : W ,
387390 logger : L ,
@@ -391,10 +394,10 @@ where
391394 locked_utxos : Mutex < HashMap < OutPoint , ClaimId > > ,
392395}
393396
394- impl < W : Deref , L : Deref > Wallet < W , L >
397+ impl < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend > Wallet < W , L >
395398where
396- W :: Target : WalletSource ,
397- L :: Target : Logger ,
399+ W :: Target : WalletSource + MaybeSend ,
400+ L :: Target : Logger + MaybeSend ,
398401{
399402 /// Returns a new instance backed by the given [`WalletSource`] that serves as an implementation
400403 /// of [`CoinSelectionSource`].
@@ -410,7 +413,7 @@ where
410413 /// `tolerate_high_network_feerates` is set, we'll attempt to spend UTXOs that contribute at
411414 /// least 1 satoshi at the current feerate, otherwise, we'll only attempt to spend those which
412415 /// contribute at least twice their fee.
413- fn select_confirmed_utxos_internal (
416+ async fn select_confirmed_utxos_internal (
414417 & self , utxos : & [ Utxo ] , claim_id : ClaimId , force_conflicting_utxo_spend : bool ,
415418 tolerate_high_network_feerates : bool , target_feerate_sat_per_1000_weight : u32 ,
416419 preexisting_tx_weight : u64 , input_amount_sat : Amount , target_amount_sat : Amount ,
@@ -484,7 +487,7 @@ where
484487 }
485488
486489 let remaining_amount = selected_amount - target_amount_sat - total_fees;
487- let change_script = self . source . get_change_script ( ) ?;
490+ let change_script = self . source . get_change_script ( ) . await ?;
488491 let change_output_fee = fee_for_weight (
489492 target_feerate_sat_per_1000_weight,
490493 ( 8 /* value */ + change_script. consensus_encode ( & mut sink ( ) ) . unwrap ( ) as u64 )
@@ -503,60 +506,67 @@ where
503506 }
504507}
505508
506- impl < W : Deref , L : Deref > CoinSelectionSource for Wallet < W , L >
509+ impl < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend > CoinSelectionSource
510+ for Wallet < W , L >
507511where
508- W :: Target : WalletSource ,
509- L :: Target : Logger ,
512+ W :: Target : WalletSource + MaybeSend + MaybeSync ,
513+ L :: Target : Logger + MaybeSend + MaybeSync ,
510514{
511- fn select_confirmed_utxos (
512- & self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & [ TxOut ] ,
515+ fn select_confirmed_utxos < ' a > (
516+ & ' a self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
513517 target_feerate_sat_per_1000_weight : u32 ,
514- ) -> Result < CoinSelection , ( ) > {
515- let utxos = self . source . list_confirmed_utxos ( ) ?;
516- // TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
517- const BASE_TX_SIZE : u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */ ;
518- let total_output_size: u64 = must_pay_to
519- . iter ( )
520- . map ( |output| 8 /* value */ + 1 /* script len */ + output. script_pubkey . len ( ) as u64 )
521- . sum ( ) ;
522- let total_satisfaction_weight: u64 =
523- must_spend. iter ( ) . map ( |input| input. satisfaction_weight ) . sum ( ) ;
524- let total_input_weight =
525- ( BASE_INPUT_WEIGHT * must_spend. len ( ) as u64 ) + total_satisfaction_weight;
526-
527- let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
518+ ) -> AsyncResult < ' a , CoinSelection > {
519+ Box :: pin ( async move {
520+ let utxos = self . source . list_confirmed_utxos ( ) . await ?;
521+ // TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
522+ const BASE_TX_SIZE : u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */ ;
523+ let total_output_size: u64 = must_pay_to
524+ . iter ( )
525+ . map (
526+ |output| 8 /* value */ + 1 /* script len */ + output. script_pubkey . len ( ) as u64 ,
527+ )
528+ . sum ( ) ;
529+ let total_satisfaction_weight: u64 =
530+ must_spend. iter ( ) . map ( |input| input. satisfaction_weight ) . sum ( ) ;
531+ let total_input_weight =
532+ ( BASE_INPUT_WEIGHT * must_spend. len ( ) as u64 ) + total_satisfaction_weight;
533+
534+ let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
528535 ( ( BASE_TX_SIZE + total_output_size) * WITNESS_SCALE_FACTOR as u64 ) ;
529- let input_amount_sat = must_spend. iter ( ) . map ( |input| input. previous_utxo . value ) . sum ( ) ;
530- let target_amount_sat = must_pay_to. iter ( ) . map ( |output| output. value ) . sum ( ) ;
536+ let input_amount_sat = must_spend. iter ( ) . map ( |input| input. previous_utxo . value ) . sum ( ) ;
537+ let target_amount_sat = must_pay_to. iter ( ) . map ( |output| output. value ) . sum ( ) ;
531538
532- let configs = [ ( false , false ) , ( false , true ) , ( true , false ) , ( true , true ) ] ;
533- for ( force_conflicting_utxo_spend, tolerate_high_network_feerates) in configs {
534- log_debug ! (
535- self . logger,
536- "Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})" ,
537- target_feerate_sat_per_1000_weight,
538- force_conflicting_utxo_spend,
539- tolerate_high_network_feerates
540- ) ;
541- let attempt = self . select_confirmed_utxos_internal (
542- & utxos,
543- claim_id,
544- force_conflicting_utxo_spend,
545- tolerate_high_network_feerates,
546- target_feerate_sat_per_1000_weight,
547- preexisting_tx_weight,
548- input_amount_sat,
549- target_amount_sat,
550- ) ;
551- if attempt. is_ok ( ) {
552- return attempt;
539+ let configs = [ ( false , false ) , ( false , true ) , ( true , false ) , ( true , true ) ] ;
540+ for ( force_conflicting_utxo_spend, tolerate_high_network_feerates) in configs {
541+ log_debug ! (
542+ self . logger,
543+ "Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})" ,
544+ target_feerate_sat_per_1000_weight,
545+ force_conflicting_utxo_spend,
546+ tolerate_high_network_feerates
547+ ) ;
548+ let attempt = self
549+ . select_confirmed_utxos_internal (
550+ & utxos,
551+ claim_id,
552+ force_conflicting_utxo_spend,
553+ tolerate_high_network_feerates,
554+ target_feerate_sat_per_1000_weight,
555+ preexisting_tx_weight,
556+ input_amount_sat,
557+ target_amount_sat,
558+ )
559+ . await ;
560+ if attempt. is_ok ( ) {
561+ return attempt;
562+ }
553563 }
554- }
555- Err ( ( ) )
564+ Err ( ( ) )
565+ } )
556566 }
557567
558- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
559- self . source . sign_psbt ( psbt)
568+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
569+ Box :: pin ( async move { self . source . sign_psbt ( psbt) . await } )
560570 }
561571}
562572
@@ -635,7 +645,7 @@ where
635645 /// Handles a [`BumpTransactionEvent::ChannelClose`] event variant by producing a fully-signed
636646 /// transaction spending an anchor output of the commitment transaction to bump its fee and
637647 /// broadcasts them to the network as a package.
638- fn handle_channel_close (
648+ async fn handle_channel_close (
639649 & self , claim_id : ClaimId , package_target_feerate_sat_per_1000_weight : u32 ,
640650 commitment_tx : & Transaction , commitment_tx_fee_sat : u64 ,
641651 anchor_descriptor : & AnchorDescriptor ,
@@ -662,12 +672,15 @@ where
662672
663673 log_debug ! ( self . logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW" ,
664674 package_target_feerate_sat_per_1000_weight) ;
665- let coin_selection: CoinSelection = self . utxo_source . select_confirmed_utxos (
666- claim_id,
667- must_spend,
668- & [ ] ,
669- package_target_feerate_sat_per_1000_weight,
670- ) ?;
675+ let coin_selection: CoinSelection = self
676+ . utxo_source
677+ . select_confirmed_utxos (
678+ claim_id,
679+ must_spend,
680+ & [ ] ,
681+ package_target_feerate_sat_per_1000_weight,
682+ )
683+ . await ?;
671684
672685 let mut anchor_tx = Transaction {
673686 version : Version :: TWO ,
@@ -733,7 +746,7 @@ where
733746 }
734747
735748 log_debug ! ( self . logger, "Signing anchor transaction {}" , anchor_txid) ;
736- anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) ?;
749+ anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) . await ?;
737750
738751 let signer = self
739752 . signer_provider
@@ -780,7 +793,7 @@ where
780793
781794 /// Handles a [`BumpTransactionEvent::HTLCResolution`] event variant by producing a
782795 /// fully-signed, fee-bumped HTLC transaction that is broadcast to the network.
783- fn handle_htlc_resolution (
796+ async fn handle_htlc_resolution (
784797 & self , claim_id : ClaimId , target_feerate_sat_per_1000_weight : u32 ,
785798 htlc_descriptors : & [ HTLCDescriptor ] , tx_lock_time : LockTime ,
786799 ) -> Result < ( ) , ( ) > {
@@ -821,12 +834,15 @@ where
821834 let must_spend_amount =
822835 must_spend. iter ( ) . map ( |input| input. previous_utxo . value . to_sat ( ) ) . sum :: < u64 > ( ) ;
823836
824- let coin_selection: CoinSelection = self . utxo_source . select_confirmed_utxos (
825- claim_id,
826- must_spend,
827- & htlc_tx. output ,
828- target_feerate_sat_per_1000_weight,
829- ) ?;
837+ let coin_selection: CoinSelection = self
838+ . utxo_source
839+ . select_confirmed_utxos (
840+ claim_id,
841+ must_spend,
842+ & htlc_tx. output ,
843+ target_feerate_sat_per_1000_weight,
844+ )
845+ . await ?;
830846
831847 #[ cfg( debug_assertions) ]
832848 let input_satisfaction_weight: u64 =
@@ -870,7 +886,7 @@ where
870886 "Signing HTLC transaction {}" ,
871887 htlc_psbt. unsigned_tx. compute_txid( )
872888 ) ;
873- htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) ?;
889+ htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) . await ?;
874890
875891 let mut signers = BTreeMap :: new ( ) ;
876892 for ( idx, htlc_descriptor) in htlc_descriptors. iter ( ) . enumerate ( ) {
@@ -909,7 +925,7 @@ where
909925 }
910926
911927 /// Handles all variants of [`BumpTransactionEvent`].
912- pub fn handle_event ( & self , event : & BumpTransactionEvent ) {
928+ pub async fn handle_event ( & self , event : & BumpTransactionEvent ) {
913929 match event {
914930 BumpTransactionEvent :: ChannelClose {
915931 claim_id,
@@ -925,19 +941,21 @@ where
925941 log_bytes!( claim_id. 0 ) ,
926942 commitment_tx. compute_txid( )
927943 ) ;
928- if let Err ( _ ) = self . handle_channel_close (
944+ self . handle_channel_close (
929945 * claim_id,
930946 * package_target_feerate_sat_per_1000_weight,
931947 commitment_tx,
932948 * commitment_tx_fee_satoshis,
933949 anchor_descriptor,
934- ) {
950+ )
951+ . await
952+ . unwrap_or_else ( |_| {
935953 log_error ! (
936954 self . logger,
937955 "Failed bumping commitment transaction fee for {}" ,
938956 commitment_tx. compute_txid( )
939957 ) ;
940- }
958+ } ) ;
941959 } ,
942960 BumpTransactionEvent :: HTLCResolution {
943961 claim_id,
@@ -952,18 +970,20 @@ where
952970 log_bytes!( claim_id. 0 ) ,
953971 log_iter!( htlc_descriptors. iter( ) . map( |d| d. outpoint( ) ) )
954972 ) ;
955- if let Err ( _ ) = self . handle_htlc_resolution (
973+ self . handle_htlc_resolution (
956974 * claim_id,
957975 * target_feerate_sat_per_1000_weight,
958976 htlc_descriptors,
959977 * tx_lock_time,
960- ) {
978+ )
979+ . await
980+ . unwrap_or_else ( |_| {
961981 log_error ! (
962982 self . logger,
963983 "Failed bumping HTLC transaction fee for commitment {}" ,
964984 htlc_descriptors[ 0 ] . commitment_txid
965985 ) ;
966- }
986+ } ) ;
967987 } ,
968988 }
969989 }
@@ -973,6 +993,9 @@ where
973993mod tests {
974994 use super :: * ;
975995
996+ use crate :: events:: bump_transaction:: sync:: {
997+ BumpTransactionEventHandlerSync , CoinSelectionSourceSync ,
998+ } ;
976999 use crate :: io:: Cursor ;
9771000 use crate :: ln:: chan_utils:: ChannelTransactionParameters ;
9781001 use crate :: sign:: KeysManager ;
@@ -988,7 +1011,7 @@ mod tests {
9881011 // (commitment + anchor value, commitment + input weight, target feerate, result)
9891012 expected_selects : Mutex < Vec < ( u64 , u64 , u32 , CoinSelection ) > > ,
9901013 }
991- impl CoinSelectionSource for TestCoinSelectionSource {
1014+ impl CoinSelectionSourceSync for TestCoinSelectionSource {
9921015 fn select_confirmed_utxos (
9931016 & self , _claim_id : ClaimId , must_spend : Vec < Input > , _must_pay_to : & [ TxOut ] ,
9941017 target_feerate_sat_per_1000_weight : u32 ,
@@ -1073,7 +1096,7 @@ mod tests {
10731096 } ;
10741097 let signer = KeysManager :: new ( & [ 42 ; 32 ] , 42 , 42 ) ;
10751098 let logger = TestLogger :: new ( ) ;
1076- let handler = BumpTransactionEventHandler :: new ( & broadcaster, & source, & signer, & logger) ;
1099+ let handler = BumpTransactionEventHandlerSync :: new ( & broadcaster, & source, & signer, & logger) ;
10771100
10781101 let mut transaction_parameters = ChannelTransactionParameters :: test_dummy ( 42_000_000 ) ;
10791102 transaction_parameters. channel_type_features =
0 commit comments