@@ -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,21 +347,38 @@ 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 {
365+ /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
366+ fn list_confirmed_utxos < ' a > ( & ' a self ) -> AsyncResult < ' a , Vec < Utxo > > ;
367+ /// Returns a script to use for change above dust resulting from a successful coin selection
368+ /// attempt.
369+ fn get_change_script < ' a > ( & self ) -> AsyncResult < ' a , ScriptBuf > ;
370+ /// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
371+ /// the transaction known to the wallet (i.e., any provided via
372+ /// [`WalletSource::list_confirmed_utxos`]).
373+ ///
374+ /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
375+ /// unsigned transaction and then sign it with your wallet.
376+ fn sign_psbt < ' a > ( & self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > ;
377+ }
378+
379+ /// A synchronous version of the [`WalletSource`] trait. Implementations of this trait should be wrapped in
380+ /// WalletSourceSyncWrapper for it to be used within rust-lightning.
381+ pub trait WalletSourceSync {
364382 /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
365383 fn list_confirmed_utxos ( & self ) -> Result < Vec < Utxo > , ( ) > ;
366384 /// Returns a script to use for change above dust resulting from a successful coin selection
@@ -375,13 +393,54 @@ pub trait WalletSource {
375393 fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > ;
376394}
377395
396+ /// A wrapper around [`WalletSourceSync`] to allow for async calls.
397+ pub struct WalletSourceSyncWrapper < T : Deref > ( T )
398+ where
399+ T :: Target : WalletSourceSync ;
400+
401+ impl < T : Deref > WalletSourceSyncWrapper < T >
402+ where
403+ T :: Target : WalletSourceSync ,
404+ {
405+ /// Creates a new [`WalletSourceSyncWrapper`].
406+ pub fn new ( source : T ) -> Self {
407+ Self ( source)
408+ }
409+ }
410+ impl < T : Deref > WalletSource for WalletSourceSyncWrapper < T >
411+ where
412+ T :: Target : WalletSourceSync ,
413+ {
414+ /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend. Wraps
415+ /// [`WalletSourceSync::list_confirmed_utxos`].
416+ fn list_confirmed_utxos < ' a > ( & ' a self ) -> AsyncResult < ' a , Vec < Utxo > > {
417+ let utxos = self . 0 . list_confirmed_utxos ( ) ;
418+ Box :: pin ( async move { utxos } )
419+ }
420+
421+ /// Returns a script to use for change above dust resulting from a successful coin selection attempt. Wraps
422+ /// [`WalletSourceSync::get_change_script`].
423+ fn get_change_script < ' a > ( & self ) -> AsyncResult < ' a , ScriptBuf > {
424+ let script = self . 0 . get_change_script ( ) ;
425+ Box :: pin ( async move { script } )
426+ }
427+
428+ /// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within the transaction
429+ /// known to the wallet (i.e., any provided via [`WalletSource::list_confirmed_utxos`]). Wraps
430+ /// [`WalletSourceSync::sign_psbt`].
431+ fn sign_psbt < ' a > ( & self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
432+ let signed_psbt = self . 0 . sign_psbt ( psbt) ;
433+ Box :: pin ( async move { signed_psbt } )
434+ }
435+ }
436+
378437/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
379438/// avoid conflicting double spends. If not enough UTXOs are available to do so, conflicting double
380439/// spends may happen.
381- pub struct Wallet < W : Deref , L : Deref >
440+ pub struct Wallet < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend >
382441where
383- W :: Target : WalletSource ,
384- L :: Target : Logger ,
442+ W :: Target : WalletSource + MaybeSend ,
443+ L :: Target : Logger + MaybeSend ,
385444{
386445 source : W ,
387446 logger : L ,
@@ -391,10 +450,10 @@ where
391450 locked_utxos : Mutex < HashMap < OutPoint , ClaimId > > ,
392451}
393452
394- impl < W : Deref , L : Deref > Wallet < W , L >
453+ impl < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend > Wallet < W , L >
395454where
396- W :: Target : WalletSource ,
397- L :: Target : Logger ,
455+ W :: Target : WalletSource + MaybeSend ,
456+ L :: Target : Logger + MaybeSend ,
398457{
399458 /// Returns a new instance backed by the given [`WalletSource`] that serves as an implementation
400459 /// of [`CoinSelectionSource`].
@@ -410,7 +469,7 @@ where
410469 /// `tolerate_high_network_feerates` is set, we'll attempt to spend UTXOs that contribute at
411470 /// least 1 satoshi at the current feerate, otherwise, we'll only attempt to spend those which
412471 /// contribute at least twice their fee.
413- fn select_confirmed_utxos_internal (
472+ async fn select_confirmed_utxos_internal (
414473 & self , utxos : & [ Utxo ] , claim_id : ClaimId , force_conflicting_utxo_spend : bool ,
415474 tolerate_high_network_feerates : bool , target_feerate_sat_per_1000_weight : u32 ,
416475 preexisting_tx_weight : u64 , input_amount_sat : Amount , target_amount_sat : Amount ,
@@ -481,7 +540,7 @@ where
481540 }
482541
483542 let remaining_amount = selected_amount - target_amount_sat - total_fees;
484- let change_script = self . source . get_change_script ( ) ?;
543+ let change_script = self . source . get_change_script ( ) . await ?;
485544 let change_output_fee = fee_for_weight (
486545 target_feerate_sat_per_1000_weight,
487546 ( 8 /* value */ + change_script. consensus_encode ( & mut sink ( ) ) . unwrap ( ) as u64 )
@@ -500,62 +559,69 @@ where
500559 }
501560}
502561
503- impl < W : Deref , L : Deref > CoinSelectionSource for Wallet < W , L >
562+ impl < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend > CoinSelectionSource
563+ for Wallet < W , L >
504564where
505- W :: Target : WalletSource ,
506- L :: Target : Logger ,
565+ W :: Target : WalletSource + MaybeSend + MaybeSync ,
566+ L :: Target : Logger + MaybeSend + MaybeSync ,
507567{
508- fn select_confirmed_utxos (
509- & self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & [ TxOut ] ,
568+ fn select_confirmed_utxos < ' a > (
569+ & ' a self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
510570 target_feerate_sat_per_1000_weight : u32 ,
511- ) -> Result < CoinSelection , ( ) > {
512- let utxos = self . source . list_confirmed_utxos ( ) ?;
513- // TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
514- const BASE_TX_SIZE : u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */ ;
515- let total_output_size: u64 = must_pay_to
516- . iter ( )
517- . map ( |output| 8 /* value */ + 1 /* script len */ + output. script_pubkey . len ( ) as u64 )
518- . sum ( ) ;
519- let total_satisfaction_weight: u64 =
520- must_spend. iter ( ) . map ( |input| input. satisfaction_weight ) . sum ( ) ;
521- let total_input_weight =
522- ( BASE_INPUT_WEIGHT * must_spend. len ( ) as u64 ) + total_satisfaction_weight;
523-
524- let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
571+ ) -> AsyncResult < ' a , CoinSelection > {
572+ Box :: pin ( async move {
573+ let utxos = self . source . list_confirmed_utxos ( ) . await ?;
574+ // TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
575+ const BASE_TX_SIZE : u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */ ;
576+ let total_output_size: u64 = must_pay_to
577+ . iter ( )
578+ . map (
579+ |output| 8 /* value */ + 1 /* script len */ + output. script_pubkey . len ( ) as u64 ,
580+ )
581+ . sum ( ) ;
582+ let total_satisfaction_weight: u64 =
583+ must_spend. iter ( ) . map ( |input| input. satisfaction_weight ) . sum ( ) ;
584+ let total_input_weight =
585+ ( BASE_INPUT_WEIGHT * must_spend. len ( ) as u64 ) + total_satisfaction_weight;
586+
587+ let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
525588 ( ( BASE_TX_SIZE + total_output_size) * WITNESS_SCALE_FACTOR as u64 ) ;
526- let input_amount_sat = must_spend. iter ( ) . map ( |input| input. previous_utxo . value ) . sum ( ) ;
527- let target_amount_sat = must_pay_to. iter ( ) . map ( |output| output. value ) . sum ( ) ;
589+ let input_amount_sat = must_spend. iter ( ) . map ( |input| input. previous_utxo . value ) . sum ( ) ;
590+ let target_amount_sat = must_pay_to. iter ( ) . map ( |output| output. value ) . sum ( ) ;
528591
529- let configs = [ ( false , false ) , ( false , true ) , ( true , false ) , ( true , true ) ] ;
530- let mut last_err = None ;
531- for ( force_conflicting_utxo_spend, tolerate_high_network_feerates) in configs {
532- log_debug ! (
592+ let configs = [ ( false , false ) , ( false , true ) , ( true , false ) , ( true , true ) ] ;
593+ let mut last_err = None ;
594+ for ( force_conflicting_utxo_spend, tolerate_high_network_feerates) in configs {
595+ log_debug ! (
533596 self . logger,
534597 "Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})" ,
535598 target_feerate_sat_per_1000_weight,
536599 force_conflicting_utxo_spend,
537600 tolerate_high_network_feerates
538601 ) ;
539- let attempt = self . select_confirmed_utxos_internal (
540- & utxos,
541- claim_id,
542- force_conflicting_utxo_spend,
543- tolerate_high_network_feerates,
544- target_feerate_sat_per_1000_weight,
545- preexisting_tx_weight,
546- input_amount_sat,
547- target_amount_sat,
548- ) ;
549- if attempt. is_ok ( ) {
550- return attempt;
602+ let attempt = self
603+ . select_confirmed_utxos_internal (
604+ & utxos,
605+ claim_id,
606+ force_conflicting_utxo_spend,
607+ tolerate_high_network_feerates,
608+ target_feerate_sat_per_1000_weight,
609+ preexisting_tx_weight,
610+ input_amount_sat,
611+ target_amount_sat,
612+ )
613+ . await ;
614+ if attempt. is_ok ( ) {
615+ return attempt;
616+ }
617+ last_err = Some ( attempt) ;
551618 }
552- last_err = Some ( attempt) ;
553- }
554- last_err. unwrap ( )
619+ last_err. unwrap ( )
620+ } )
555621 }
556622
557- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
558- self . source . sign_psbt ( psbt)
623+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
624+ Box :: pin ( async move { self . source . sign_psbt ( psbt) . await } )
559625 }
560626}
561627
@@ -661,12 +727,15 @@ where
661727
662728 log_debug ! ( self . logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW" ,
663729 package_target_feerate_sat_per_1000_weight) ;
664- let coin_selection: CoinSelection = self . utxo_source . select_confirmed_utxos (
665- claim_id,
666- must_spend,
667- & [ ] ,
668- package_target_feerate_sat_per_1000_weight,
669- ) ?;
730+ let coin_selection: CoinSelection = self
731+ . utxo_source
732+ . select_confirmed_utxos (
733+ claim_id,
734+ must_spend,
735+ & [ ] ,
736+ package_target_feerate_sat_per_1000_weight,
737+ )
738+ . await ?;
670739
671740 let mut anchor_tx = Transaction {
672741 version : Version :: TWO ,
@@ -732,7 +801,7 @@ where
732801 }
733802
734803 log_debug ! ( self . logger, "Signing anchor transaction {}" , anchor_txid) ;
735- anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) ?;
804+ anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) . await ?;
736805
737806 let signer = self
738807 . signer_provider
@@ -820,12 +889,15 @@ where
820889 let must_spend_amount =
821890 must_spend. iter ( ) . map ( |input| input. previous_utxo . value . to_sat ( ) ) . sum :: < u64 > ( ) ;
822891
823- let coin_selection: CoinSelection = self . utxo_source . select_confirmed_utxos (
824- claim_id,
825- must_spend,
826- & htlc_tx. output ,
827- target_feerate_sat_per_1000_weight,
828- ) ?;
892+ let coin_selection: CoinSelection = self
893+ . utxo_source
894+ . select_confirmed_utxos (
895+ claim_id,
896+ must_spend,
897+ & htlc_tx. output ,
898+ target_feerate_sat_per_1000_weight,
899+ )
900+ . await ?;
829901
830902 #[ cfg( debug_assertions) ]
831903 let input_satisfaction_weight: u64 =
@@ -869,7 +941,7 @@ where
869941 "Signing HTLC transaction {}" ,
870942 htlc_psbt. unsigned_tx. compute_txid( )
871943 ) ;
872- htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) ?;
944+ htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) . await ?;
873945
874946 let mut signers = BTreeMap :: new ( ) ;
875947 for ( idx, htlc_descriptor) in htlc_descriptors. iter ( ) . enumerate ( ) {
@@ -994,27 +1066,27 @@ mod tests {
9941066 expected_selects : Mutex < Vec < ( u64 , u64 , u32 , CoinSelection ) > > ,
9951067 }
9961068 impl CoinSelectionSource for TestCoinSelectionSource {
997- fn select_confirmed_utxos (
998- & self , _claim_id : ClaimId , must_spend : Vec < Input > , _must_pay_to : & [ TxOut ] ,
1069+ fn select_confirmed_utxos < ' a > (
1070+ & ' a self , _claim_id : ClaimId , must_spend : Vec < Input > , _must_pay_to : & ' a [ TxOut ] ,
9991071 target_feerate_sat_per_1000_weight : u32 ,
1000- ) -> Result < CoinSelection , ( ) > {
1072+ ) -> AsyncResult < ' a , CoinSelection > {
10011073 let mut expected_selects = self . expected_selects . lock ( ) . unwrap ( ) ;
10021074 let ( weight, value, feerate, res) = expected_selects. remove ( 0 ) ;
10031075 assert_eq ! ( must_spend. len( ) , 1 ) ;
10041076 assert_eq ! ( must_spend[ 0 ] . satisfaction_weight, weight) ;
10051077 assert_eq ! ( must_spend[ 0 ] . previous_utxo. value. to_sat( ) , value) ;
10061078 assert_eq ! ( target_feerate_sat_per_1000_weight, feerate) ;
1007- Ok ( res)
1079+ Box :: pin ( async move { Ok ( res) } )
10081080 }
1009- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
1081+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
10101082 let mut tx = psbt. unsigned_tx ;
10111083 for input in tx. input . iter_mut ( ) {
10121084 if input. previous_output . txid != Txid :: from_byte_array ( [ 44 ; 32 ] ) {
10131085 // Channel output, add a realistic size witness to make the assertions happy
10141086 input. witness = Witness :: from_slice ( & [ vec ! [ 42 ; 162 ] ] ) ;
10151087 }
10161088 }
1017- Ok ( tx)
1089+ Box :: pin ( async move { Ok ( tx) } )
10181090 }
10191091 }
10201092
0 commit comments