@@ -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 ,
@@ -484,7 +543,7 @@ where
484543 }
485544
486545 let remaining_amount = selected_amount - target_amount_sat - total_fees;
487- let change_script = self . source . get_change_script ( ) ?;
546+ let change_script = self . source . get_change_script ( ) . await ?;
488547 let change_output_fee = fee_for_weight (
489548 target_feerate_sat_per_1000_weight,
490549 ( 8 /* value */ + change_script. consensus_encode ( & mut sink ( ) ) . unwrap ( ) as u64 )
@@ -503,60 +562,67 @@ where
503562 }
504563}
505564
506- impl < W : Deref , L : Deref > CoinSelectionSource for Wallet < W , L >
565+ impl < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend > CoinSelectionSource
566+ for Wallet < W , L >
507567where
508- W :: Target : WalletSource ,
509- L :: Target : Logger ,
568+ W :: Target : WalletSource + MaybeSend + MaybeSync ,
569+ L :: Target : Logger + MaybeSend + MaybeSync ,
510570{
511- fn select_confirmed_utxos (
512- & self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & [ TxOut ] ,
571+ fn select_confirmed_utxos < ' a > (
572+ & ' a self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
513573 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 +
574+ ) -> AsyncResult < ' a , CoinSelection > {
575+ Box :: pin ( async move {
576+ let utxos = self . source . list_confirmed_utxos ( ) . await ?;
577+ // TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
578+ const BASE_TX_SIZE : u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */ ;
579+ let total_output_size: u64 = must_pay_to
580+ . iter ( )
581+ . map (
582+ |output| 8 /* value */ + 1 /* script len */ + output. script_pubkey . len ( ) as u64 ,
583+ )
584+ . sum ( ) ;
585+ let total_satisfaction_weight: u64 =
586+ must_spend. iter ( ) . map ( |input| input. satisfaction_weight ) . sum ( ) ;
587+ let total_input_weight =
588+ ( BASE_INPUT_WEIGHT * must_spend. len ( ) as u64 ) + total_satisfaction_weight;
589+
590+ let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
528591 ( ( 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 ( ) ;
592+ let input_amount_sat = must_spend. iter ( ) . map ( |input| input. previous_utxo . value ) . sum ( ) ;
593+ let target_amount_sat = must_pay_to. iter ( ) . map ( |output| output. value ) . sum ( ) ;
531594
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;
595+ let configs = [ ( false , false ) , ( false , true ) , ( true , false ) , ( true , true ) ] ;
596+ for ( force_conflicting_utxo_spend, tolerate_high_network_feerates) in configs {
597+ log_debug ! (
598+ self . logger,
599+ "Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})" ,
600+ target_feerate_sat_per_1000_weight,
601+ force_conflicting_utxo_spend,
602+ tolerate_high_network_feerates
603+ ) ;
604+ let attempt = self
605+ . select_confirmed_utxos_internal (
606+ & utxos,
607+ claim_id,
608+ force_conflicting_utxo_spend,
609+ tolerate_high_network_feerates,
610+ target_feerate_sat_per_1000_weight,
611+ preexisting_tx_weight,
612+ input_amount_sat,
613+ target_amount_sat,
614+ )
615+ . await ;
616+ if attempt. is_ok ( ) {
617+ return attempt;
618+ }
553619 }
554- }
555- Err ( ( ) )
620+ Err ( ( ) )
621+ } )
556622 }
557623
558- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
559- self . source . sign_psbt ( psbt)
624+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
625+ Box :: pin ( async move { self . source . sign_psbt ( psbt) . await } )
560626 }
561627}
562628
@@ -662,12 +728,15 @@ where
662728
663729 log_debug ! ( self . logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW" ,
664730 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- ) ?;
731+ let coin_selection: CoinSelection = self
732+ . utxo_source
733+ . select_confirmed_utxos (
734+ claim_id,
735+ must_spend,
736+ & [ ] ,
737+ package_target_feerate_sat_per_1000_weight,
738+ )
739+ . await ?;
671740
672741 let mut anchor_tx = Transaction {
673742 version : Version :: TWO ,
@@ -733,7 +802,7 @@ where
733802 }
734803
735804 log_debug ! ( self . logger, "Signing anchor transaction {}" , anchor_txid) ;
736- anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) ?;
805+ anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) . await ?;
737806
738807 let signer = self
739808 . signer_provider
@@ -821,12 +890,15 @@ where
821890 let must_spend_amount =
822891 must_spend. iter ( ) . map ( |input| input. previous_utxo . value . to_sat ( ) ) . sum :: < u64 > ( ) ;
823892
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- ) ?;
893+ let coin_selection: CoinSelection = self
894+ . utxo_source
895+ . select_confirmed_utxos (
896+ claim_id,
897+ must_spend,
898+ & htlc_tx. output ,
899+ target_feerate_sat_per_1000_weight,
900+ )
901+ . await ?;
830902
831903 #[ cfg( debug_assertions) ]
832904 let input_satisfaction_weight: u64 =
@@ -870,7 +942,7 @@ where
870942 "Signing HTLC transaction {}" ,
871943 htlc_psbt. unsigned_tx. compute_txid( )
872944 ) ;
873- htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) ?;
945+ htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) . await ?;
874946
875947 let mut signers = BTreeMap :: new ( ) ;
876948 for ( idx, htlc_descriptor) in htlc_descriptors. iter ( ) . enumerate ( ) {
@@ -993,27 +1065,27 @@ mod tests {
9931065 expected_selects : Mutex < Vec < ( u64 , u64 , u32 , CoinSelection ) > > ,
9941066 }
9951067 impl CoinSelectionSource for TestCoinSelectionSource {
996- fn select_confirmed_utxos (
997- & self , _claim_id : ClaimId , must_spend : Vec < Input > , _must_pay_to : & [ TxOut ] ,
1068+ fn select_confirmed_utxos < ' a > (
1069+ & ' a self , _claim_id : ClaimId , must_spend : Vec < Input > , _must_pay_to : & ' a [ TxOut ] ,
9981070 target_feerate_sat_per_1000_weight : u32 ,
999- ) -> Result < CoinSelection , ( ) > {
1071+ ) -> AsyncResult < ' a , CoinSelection > {
10001072 let mut expected_selects = self . expected_selects . lock ( ) . unwrap ( ) ;
10011073 let ( weight, value, feerate, res) = expected_selects. remove ( 0 ) ;
10021074 assert_eq ! ( must_spend. len( ) , 1 ) ;
10031075 assert_eq ! ( must_spend[ 0 ] . satisfaction_weight, weight) ;
10041076 assert_eq ! ( must_spend[ 0 ] . previous_utxo. value. to_sat( ) , value) ;
10051077 assert_eq ! ( target_feerate_sat_per_1000_weight, feerate) ;
1006- Ok ( res)
1078+ Box :: pin ( async move { Ok ( res) } )
10071079 }
1008- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
1080+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
10091081 let mut tx = psbt. unsigned_tx ;
10101082 for input in tx. input . iter_mut ( ) {
10111083 if input. previous_output . txid != Txid :: from_byte_array ( [ 44 ; 32 ] ) {
10121084 // Channel output, add a realistic size witness to make the assertions happy
10131085 input. witness = Witness :: from_slice ( & [ vec ! [ 42 ; 162 ] ] ) ;
10141086 }
10151087 }
1016- Ok ( tx)
1088+ Box :: pin ( async move { Ok ( tx) } )
10171089 }
10181090 }
10191091
0 commit comments