@@ -30,6 +30,7 @@ use crate::sign::{
3030 ChannelDerivationParameters , HTLCDescriptor , SignerProvider , P2WPKH_WITNESS_WEIGHT ,
3131} ;
3232use crate :: sync:: Mutex ;
33+ use crate :: util:: async_poll:: { AsyncResult , MaybeSend , MaybeSync } ;
3334use crate :: util:: logger:: Logger ;
3435
3536use bitcoin:: amount:: Amount ;
@@ -346,42 +347,42 @@ pub trait CoinSelectionSource {
346347 /// other claims, implementations must be willing to double spend their UTXOs. The choice of
347348 /// which UTXOs to double spend is left to the implementation, but it must strive to keep the
348349 /// 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 ] ,
350+ fn select_confirmed_utxos < ' a > (
351+ & ' a self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
351352 target_feerate_sat_per_1000_weight : u32 ,
352- ) -> Result < CoinSelection , ( ) > ;
353+ ) -> AsyncResult < ' a , CoinSelection > ;
353354 /// Signs and provides the full witness for all inputs within the transaction known to the
354355 /// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
355356 ///
356357 /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
357358 /// unsigned transaction and then sign it with your wallet.
358- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > ;
359+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > ;
359360}
360361
361362/// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
362363/// provide a default implementation to [`CoinSelectionSource`].
363364pub trait WalletSource {
364365 /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
365- fn list_confirmed_utxos ( & self ) -> Result < Vec < Utxo > , ( ) > ;
366+ fn list_confirmed_utxos < ' a > ( & ' a self ) -> AsyncResult < ' a , Vec < Utxo > > ;
366367 /// Returns a script to use for change above dust resulting from a successful coin selection
367368 /// attempt.
368- fn get_change_script ( & self ) -> Result < ScriptBuf , ( ) > ;
369+ fn get_change_script < ' a > ( & ' a self ) -> AsyncResult < ' a , ScriptBuf > ;
369370 /// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
370371 /// the transaction known to the wallet (i.e., any provided via
371372 /// [`WalletSource::list_confirmed_utxos`]).
372373 ///
373374 /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
374375 /// unsigned transaction and then sign it with your wallet.
375- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > ;
376+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > ;
376377}
377378
378379/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
379380/// avoid conflicting double spends. If not enough UTXOs are available to do so, conflicting double
380381/// spends may happen.
381- pub struct Wallet < W : Deref , L : Deref >
382+ pub struct Wallet < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend >
382383where
383- W :: Target : WalletSource ,
384- L :: Target : Logger ,
384+ W :: Target : WalletSource + MaybeSend ,
385+ L :: Target : Logger + MaybeSend ,
385386{
386387 source : W ,
387388 logger : L ,
@@ -391,10 +392,10 @@ where
391392 locked_utxos : Mutex < HashMap < OutPoint , ClaimId > > ,
392393}
393394
394- impl < W : Deref , L : Deref > Wallet < W , L >
395+ impl < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend > Wallet < W , L >
395396where
396- W :: Target : WalletSource ,
397- L :: Target : Logger ,
397+ W :: Target : WalletSource + MaybeSend ,
398+ L :: Target : Logger + MaybeSend ,
398399{
399400 /// Returns a new instance backed by the given [`WalletSource`] that serves as an implementation
400401 /// of [`CoinSelectionSource`].
@@ -410,7 +411,7 @@ where
410411 /// `tolerate_high_network_feerates` is set, we'll attempt to spend UTXOs that contribute at
411412 /// least 1 satoshi at the current feerate, otherwise, we'll only attempt to spend those which
412413 /// contribute at least twice their fee.
413- fn select_confirmed_utxos_internal (
414+ async fn select_confirmed_utxos_internal (
414415 & self , utxos : & [ Utxo ] , claim_id : ClaimId , force_conflicting_utxo_spend : bool ,
415416 tolerate_high_network_feerates : bool , target_feerate_sat_per_1000_weight : u32 ,
416417 preexisting_tx_weight : u64 , input_amount_sat : Amount , target_amount_sat : Amount ,
@@ -484,7 +485,7 @@ where
484485 }
485486
486487 let remaining_amount = selected_amount - target_amount_sat - total_fees;
487- let change_script = self . source . get_change_script ( ) ?;
488+ let change_script = self . source . get_change_script ( ) . await ?;
488489 let change_output_fee = fee_for_weight (
489490 target_feerate_sat_per_1000_weight,
490491 ( 8 /* value */ + change_script. consensus_encode ( & mut sink ( ) ) . unwrap ( ) as u64 )
@@ -503,60 +504,67 @@ where
503504 }
504505}
505506
506- impl < W : Deref , L : Deref > CoinSelectionSource for Wallet < W , L >
507+ impl < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend > CoinSelectionSource
508+ for Wallet < W , L >
507509where
508- W :: Target : WalletSource ,
509- L :: Target : Logger ,
510+ W :: Target : WalletSource + MaybeSend + MaybeSync ,
511+ L :: Target : Logger + MaybeSend + MaybeSync ,
510512{
511- fn select_confirmed_utxos (
512- & self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & [ TxOut ] ,
513+ fn select_confirmed_utxos < ' a > (
514+ & ' a self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
513515 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 +
516+ ) -> AsyncResult < ' a , CoinSelection > {
517+ Box :: pin ( async move {
518+ let utxos = self . source . list_confirmed_utxos ( ) . await ?;
519+ // TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
520+ const BASE_TX_SIZE : u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */ ;
521+ let total_output_size: u64 = must_pay_to
522+ . iter ( )
523+ . map (
524+ |output| 8 /* value */ + 1 /* script len */ + output. script_pubkey . len ( ) as u64 ,
525+ )
526+ . sum ( ) ;
527+ let total_satisfaction_weight: u64 =
528+ must_spend. iter ( ) . map ( |input| input. satisfaction_weight ) . sum ( ) ;
529+ let total_input_weight =
530+ ( BASE_INPUT_WEIGHT * must_spend. len ( ) as u64 ) + total_satisfaction_weight;
531+
532+ let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
528533 ( ( 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 ( ) ;
534+ let input_amount_sat = must_spend. iter ( ) . map ( |input| input. previous_utxo . value ) . sum ( ) ;
535+ let target_amount_sat = must_pay_to. iter ( ) . map ( |output| output. value ) . sum ( ) ;
531536
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;
537+ let configs = [ ( false , false ) , ( false , true ) , ( true , false ) , ( true , true ) ] ;
538+ for ( force_conflicting_utxo_spend, tolerate_high_network_feerates) in configs {
539+ log_debug ! (
540+ self . logger,
541+ "Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})" ,
542+ target_feerate_sat_per_1000_weight,
543+ force_conflicting_utxo_spend,
544+ tolerate_high_network_feerates
545+ ) ;
546+ let attempt = self
547+ . select_confirmed_utxos_internal (
548+ & utxos,
549+ claim_id,
550+ force_conflicting_utxo_spend,
551+ tolerate_high_network_feerates,
552+ target_feerate_sat_per_1000_weight,
553+ preexisting_tx_weight,
554+ input_amount_sat,
555+ target_amount_sat,
556+ )
557+ . await ;
558+ if attempt. is_ok ( ) {
559+ return attempt;
560+ }
553561 }
554- }
555- Err ( ( ) )
562+ Err ( ( ) )
563+ } )
556564 }
557565
558- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
559- self . source . sign_psbt ( psbt)
566+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
567+ Box :: pin ( async move { self . source . sign_psbt ( psbt) . await } )
560568 }
561569}
562570
@@ -635,7 +643,7 @@ where
635643 /// Handles a [`BumpTransactionEvent::ChannelClose`] event variant by producing a fully-signed
636644 /// transaction spending an anchor output of the commitment transaction to bump its fee and
637645 /// broadcasts them to the network as a package.
638- fn handle_channel_close (
646+ async fn handle_channel_close (
639647 & self , claim_id : ClaimId , package_target_feerate_sat_per_1000_weight : u32 ,
640648 commitment_tx : & Transaction , commitment_tx_fee_sat : u64 ,
641649 anchor_descriptor : & AnchorDescriptor ,
@@ -662,12 +670,15 @@ where
662670
663671 log_debug ! ( self . logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW" ,
664672 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- ) ?;
673+ let coin_selection: CoinSelection = self
674+ . utxo_source
675+ . select_confirmed_utxos (
676+ claim_id,
677+ must_spend,
678+ & [ ] ,
679+ package_target_feerate_sat_per_1000_weight,
680+ )
681+ . await ?;
671682
672683 let mut anchor_tx = Transaction {
673684 version : Version :: TWO ,
@@ -733,7 +744,7 @@ where
733744 }
734745
735746 log_debug ! ( self . logger, "Signing anchor transaction {}" , anchor_txid) ;
736- anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) ?;
747+ anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) . await ?;
737748
738749 let signer = self
739750 . signer_provider
@@ -780,7 +791,7 @@ where
780791
781792 /// Handles a [`BumpTransactionEvent::HTLCResolution`] event variant by producing a
782793 /// fully-signed, fee-bumped HTLC transaction that is broadcast to the network.
783- fn handle_htlc_resolution (
794+ async fn handle_htlc_resolution (
784795 & self , claim_id : ClaimId , target_feerate_sat_per_1000_weight : u32 ,
785796 htlc_descriptors : & [ HTLCDescriptor ] , tx_lock_time : LockTime ,
786797 ) -> Result < ( ) , ( ) > {
@@ -821,12 +832,15 @@ where
821832 let must_spend_amount =
822833 must_spend. iter ( ) . map ( |input| input. previous_utxo . value . to_sat ( ) ) . sum :: < u64 > ( ) ;
823834
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- ) ?;
835+ let coin_selection: CoinSelection = self
836+ . utxo_source
837+ . select_confirmed_utxos (
838+ claim_id,
839+ must_spend,
840+ & htlc_tx. output ,
841+ target_feerate_sat_per_1000_weight,
842+ )
843+ . await ?;
830844
831845 #[ cfg( debug_assertions) ]
832846 let input_satisfaction_weight: u64 =
@@ -870,7 +884,7 @@ where
870884 "Signing HTLC transaction {}" ,
871885 htlc_psbt. unsigned_tx. compute_txid( )
872886 ) ;
873- htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) ?;
887+ htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) . await ?;
874888
875889 let mut signers = BTreeMap :: new ( ) ;
876890 for ( idx, htlc_descriptor) in htlc_descriptors. iter ( ) . enumerate ( ) {
@@ -909,7 +923,7 @@ where
909923 }
910924
911925 /// Handles all variants of [`BumpTransactionEvent`].
912- pub fn handle_event ( & self , event : & BumpTransactionEvent ) {
926+ pub async fn handle_event ( & self , event : & BumpTransactionEvent ) {
913927 match event {
914928 BumpTransactionEvent :: ChannelClose {
915929 claim_id,
@@ -925,19 +939,21 @@ where
925939 log_bytes!( claim_id. 0 ) ,
926940 commitment_tx. compute_txid( )
927941 ) ;
928- if let Err ( _ ) = self . handle_channel_close (
942+ self . handle_channel_close (
929943 * claim_id,
930944 * package_target_feerate_sat_per_1000_weight,
931945 commitment_tx,
932946 * commitment_tx_fee_satoshis,
933947 anchor_descriptor,
934- ) {
948+ )
949+ . await
950+ . unwrap_or_else ( |_| {
935951 log_error ! (
936952 self . logger,
937953 "Failed bumping commitment transaction fee for {}" ,
938954 commitment_tx. compute_txid( )
939955 ) ;
940- }
956+ } ) ;
941957 } ,
942958 BumpTransactionEvent :: HTLCResolution {
943959 claim_id,
@@ -952,18 +968,20 @@ where
952968 log_bytes!( claim_id. 0 ) ,
953969 log_iter!( htlc_descriptors. iter( ) . map( |d| d. outpoint( ) ) )
954970 ) ;
955- if let Err ( _ ) = self . handle_htlc_resolution (
971+ self . handle_htlc_resolution (
956972 * claim_id,
957973 * target_feerate_sat_per_1000_weight,
958974 htlc_descriptors,
959975 * tx_lock_time,
960- ) {
976+ )
977+ . await
978+ . unwrap_or_else ( |_| {
961979 log_error ! (
962980 self . logger,
963981 "Failed bumping HTLC transaction fee for commitment {}" ,
964982 htlc_descriptors[ 0 ] . commitment_txid
965983 ) ;
966- }
984+ } ) ;
967985 } ,
968986 }
969987 }
@@ -973,6 +991,9 @@ where
973991mod tests {
974992 use super :: * ;
975993
994+ use crate :: events:: bump_transaction_sync:: {
995+ BumpTransactionEventHandlerSync , CoinSelectionSourceSync ,
996+ } ;
976997 use crate :: io:: Cursor ;
977998 use crate :: ln:: chan_utils:: ChannelTransactionParameters ;
978999 use crate :: sign:: KeysManager ;
@@ -988,7 +1009,7 @@ mod tests {
9881009 // (commitment + anchor value, commitment + input weight, target feerate, result)
9891010 expected_selects : Mutex < Vec < ( u64 , u64 , u32 , CoinSelection ) > > ,
9901011 }
991- impl CoinSelectionSource for TestCoinSelectionSource {
1012+ impl CoinSelectionSourceSync for TestCoinSelectionSource {
9921013 fn select_confirmed_utxos (
9931014 & self , _claim_id : ClaimId , must_spend : Vec < Input > , _must_pay_to : & [ TxOut ] ,
9941015 target_feerate_sat_per_1000_weight : u32 ,
@@ -1073,7 +1094,7 @@ mod tests {
10731094 } ;
10741095 let signer = KeysManager :: new ( & [ 42 ; 32 ] , 42 , 42 ) ;
10751096 let logger = TestLogger :: new ( ) ;
1076- let handler = BumpTransactionEventHandler :: new ( & broadcaster, & source, & signer, & logger) ;
1097+ let handler = BumpTransactionEventHandlerSync :: new ( & broadcaster, & source, & signer, & logger) ;
10771098
10781099 let mut transaction_parameters = ChannelTransactionParameters :: test_dummy ( 42_000_000 ) ;
10791100 transaction_parameters. channel_type_features =
0 commit comments