@@ -30,6 +30,7 @@ use crate::sign::{
3030} ;
3131use crate :: sign:: ecdsa:: EcdsaChannelSigner ;
3232use crate :: sync:: Mutex ;
33+ use crate :: util:: async_poll:: { AsyncResult , MaybeSync , MaybeSend } ;
3334use crate :: util:: logger:: Logger ;
3435
3536use bitcoin:: { OutPoint , Psbt , PubkeyHash , Sequence , ScriptBuf , Transaction , TxIn , TxOut , Witness , WPubkeyHash } ;
@@ -344,21 +345,38 @@ pub trait CoinSelectionSource {
344345 /// other claims, implementations must be willing to double spend their UTXOs. The choice of
345346 /// which UTXOs to double spend is left to the implementation, but it must strive to keep the
346347 /// set of other claims being double spent to a minimum.
347- fn select_confirmed_utxos (
348- & self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & [ TxOut ] ,
348+ fn select_confirmed_utxos < ' a > (
349+ & ' a self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
349350 target_feerate_sat_per_1000_weight : u32 ,
350- ) -> Result < CoinSelection , ( ) > ;
351+ ) -> AsyncResult < ' a , CoinSelection > ;
351352 /// Signs and provides the full witness for all inputs within the transaction known to the
352353 /// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
353354 ///
354355 /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
355356 /// unsigned transaction and then sign it with your wallet.
356- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > ;
357+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > ;
357358}
358359
359360/// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
360361/// provide a default implementation to [`CoinSelectionSource`].
361362pub trait WalletSource {
363+ /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
364+ fn list_confirmed_utxos < ' a > ( & ' a self ) -> AsyncResult < ' a , Vec < Utxo > > ;
365+ /// Returns a script to use for change above dust resulting from a successful coin selection
366+ /// attempt.
367+ fn get_change_script < ' a > ( & self ) -> AsyncResult < ' a , ScriptBuf > ;
368+ /// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
369+ /// the transaction known to the wallet (i.e., any provided via
370+ /// [`WalletSource::list_confirmed_utxos`]).
371+ ///
372+ /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
373+ /// unsigned transaction and then sign it with your wallet.
374+ fn sign_psbt < ' a > ( & self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > ;
375+ }
376+
377+ /// A synchronous version of the [`WalletSource`] trait. Implementations of this trait should be wrapped in
378+ /// WalletSourceSyncWrapper for it to be used within rust-lightning.
379+ pub trait WalletSourceSync {
362380 /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
363381 fn list_confirmed_utxos ( & self ) -> Result < Vec < Utxo > , ( ) > ;
364382 /// Returns a script to use for change above dust resulting from a successful coin selection
@@ -373,13 +391,54 @@ pub trait WalletSource {
373391 fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > ;
374392}
375393
394+ /// A wrapper around [`WalletSourceSync`] to allow for async calls.
395+ pub struct WalletSourceSyncWrapper < T : Deref > ( T )
396+ where
397+ T :: Target : WalletSourceSync ;
398+
399+ impl < T : Deref > WalletSourceSyncWrapper < T >
400+ where
401+ T :: Target : WalletSourceSync ,
402+ {
403+ /// Creates a new [`WalletSourceSyncWrapper`].
404+ pub fn new ( source : T ) -> Self {
405+ Self ( source)
406+ }
407+ }
408+ impl < T : Deref > WalletSource for WalletSourceSyncWrapper < T >
409+ where
410+ T :: Target : WalletSourceSync ,
411+ {
412+ /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend. Wraps
413+ /// [`WalletSourceSync::list_confirmed_utxos`].
414+ fn list_confirmed_utxos < ' a > ( & ' a self ) -> AsyncResult < ' a , Vec < Utxo > > {
415+ let utxos = self . 0 . list_confirmed_utxos ( ) ;
416+ Box :: pin ( async move { utxos } )
417+ }
418+
419+ /// Returns a script to use for change above dust resulting from a successful coin selection attempt. Wraps
420+ /// [`WalletSourceSync::get_change_script`].
421+ fn get_change_script < ' a > ( & self ) -> AsyncResult < ' a , ScriptBuf > {
422+ let script = self . 0 . get_change_script ( ) ;
423+ Box :: pin ( async move { script } )
424+ }
425+
426+ /// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within the transaction
427+ /// known to the wallet (i.e., any provided via [`WalletSource::list_confirmed_utxos`]). Wraps
428+ /// [`WalletSourceSync::sign_psbt`].
429+ fn sign_psbt < ' a > ( & self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
430+ let signed_psbt = self . 0 . sign_psbt ( psbt) ;
431+ Box :: pin ( async move { signed_psbt } )
432+ }
433+ }
434+
376435/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
377436/// avoid conflicting double spends. If not enough UTXOs are available to do so, conflicting double
378437/// spends may happen.
379- pub struct Wallet < W : Deref , L : Deref >
438+ pub struct Wallet < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend >
380439where
381- W :: Target : WalletSource ,
382- L :: Target : Logger
440+ W :: Target : WalletSource + MaybeSend ,
441+ L :: Target : Logger + MaybeSend
383442{
384443 source : W ,
385444 logger : L ,
@@ -389,10 +448,10 @@ where
389448 locked_utxos : Mutex < HashMap < OutPoint , ClaimId > > ,
390449}
391450
392- impl < W : Deref , L : Deref > Wallet < W , L >
451+ impl < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend > Wallet < W , L >
393452where
394- W :: Target : WalletSource ,
395- L :: Target : Logger
453+ W :: Target : WalletSource + MaybeSend ,
454+ L :: Target : Logger + MaybeSend
396455{
397456 /// Returns a new instance backed by the given [`WalletSource`] that serves as an implementation
398457 /// of [`CoinSelectionSource`].
@@ -408,7 +467,7 @@ where
408467 /// `tolerate_high_network_feerates` is set, we'll attempt to spend UTXOs that contribute at
409468 /// least 1 satoshi at the current feerate, otherwise, we'll only attempt to spend those which
410469 /// contribute at least twice their fee.
411- fn select_confirmed_utxos_internal (
470+ async fn select_confirmed_utxos_internal (
412471 & self , utxos : & [ Utxo ] , claim_id : ClaimId , force_conflicting_utxo_spend : bool ,
413472 tolerate_high_network_feerates : bool , target_feerate_sat_per_1000_weight : u32 ,
414473 preexisting_tx_weight : u64 , input_amount_sat : Amount , target_amount_sat : Amount ,
@@ -461,7 +520,7 @@ where
461520 }
462521
463522 let remaining_amount = selected_amount - target_amount_sat - total_fees;
464- let change_script = self . source . get_change_script ( ) ?;
523+ let change_script = self . source . get_change_script ( ) . await ?;
465524 let change_output_fee = fee_for_weight (
466525 target_feerate_sat_per_1000_weight,
467526 ( 8 /* value */ + change_script. consensus_encode ( & mut sink ( ) ) . unwrap ( ) as u64 ) *
@@ -482,23 +541,29 @@ where
482541 }
483542}
484543
485- impl < W : Deref , L : Deref > CoinSelectionSource for Wallet < W , L >
544+ impl < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend > CoinSelectionSource for Wallet < W , L >
486545where
487- W :: Target : WalletSource ,
488- L :: Target : Logger
546+ W :: Target : WalletSource + MaybeSend + MaybeSync ,
547+ L :: Target : Logger + MaybeSend + MaybeSync ,
489548{
490- fn select_confirmed_utxos (
491- & self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & [ TxOut ] ,
549+ fn select_confirmed_utxos < ' a > (
550+ & ' a self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
492551 target_feerate_sat_per_1000_weight : u32 ,
493- ) -> Result < CoinSelection , ( ) > {
494- let utxos = self . source . list_confirmed_utxos ( ) ?;
552+ ) -> AsyncResult < ' a , CoinSelection > {
553+ Box :: pin ( async move {
554+ let utxos = self . source . list_confirmed_utxos ( ) . await ?;
495555 // TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
496556 const BASE_TX_SIZE : u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */ ;
497- let total_output_size: u64 = must_pay_to. iter ( ) . map ( |output|
498- 8 /* value */ + 1 /* script len */ + output. script_pubkey . len ( ) as u64
499- ) . sum ( ) ;
500- let total_satisfaction_weight: u64 = must_spend. iter ( ) . map ( |input| input. satisfaction_weight ) . sum ( ) ;
501- let total_input_weight = ( BASE_INPUT_WEIGHT * must_spend. len ( ) as u64 ) + total_satisfaction_weight;
557+ let total_output_size: u64 = must_pay_to
558+ . iter ( )
559+ . map (
560+ |output| 8 /* value */ + 1 /* script len */ + output. script_pubkey . len ( ) as u64 ,
561+ )
562+ . sum ( ) ;
563+ let total_satisfaction_weight: u64 =
564+ must_spend. iter ( ) . map ( |input| input. satisfaction_weight ) . sum ( ) ;
565+ let total_input_weight =
566+ ( BASE_INPUT_WEIGHT * must_spend. len ( ) as u64 ) + total_satisfaction_weight;
502567
503568 let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
504569 ( ( BASE_TX_SIZE + total_output_size) * WITNESS_SCALE_FACTOR as u64 ) ;
@@ -524,16 +589,17 @@ where
524589 preexisting_tx_weight,
525590 input_amount_sat,
526591 target_amount_sat,
527- ) ;
592+ ) . await ;
528593 if attempt. is_ok ( ) {
529594 return attempt;
530595 }
531596 last_err = Some ( attempt) ;
532597 }
533598 last_err. unwrap ( )
599+ } )
534600 }
535601
536- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
602+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
537603 self . source . sign_psbt ( psbt)
538604 }
539605}
@@ -645,8 +711,11 @@ where
645711 log_debug ! ( self . logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW" ,
646712 package_target_feerate_sat_per_1000_weight) ;
647713 let coin_selection: CoinSelection = self . utxo_source . select_confirmed_utxos (
648- claim_id, must_spend, & [ ] , package_target_feerate_sat_per_1000_weight,
649- ) ?;
714+ claim_id,
715+ must_spend,
716+ & [ ] ,
717+ package_target_feerate_sat_per_1000_weight,
718+ ) . await ?;
650719
651720 let mut anchor_tx = Transaction {
652721 version : Version :: TWO ,
@@ -699,7 +768,7 @@ where
699768 }
700769
701770 log_debug ! ( self . logger, "Signing anchor transaction {}" , anchor_txid) ;
702- anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) ?;
771+ anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) . await ?;
703772
704773 let signer = self . signer_provider . derive_channel_signer ( anchor_descriptor. channel_derivation_parameters . keys_id ) ;
705774 let channel_parameters = & anchor_descriptor. channel_derivation_parameters . transaction_parameters ;
@@ -767,8 +836,11 @@ where
767836 let must_spend_amount = must_spend. iter ( ) . map ( |input| input. previous_utxo . value . to_sat ( ) ) . sum :: < u64 > ( ) ;
768837
769838 let coin_selection: CoinSelection = self . utxo_source . select_confirmed_utxos (
770- claim_id, must_spend, & htlc_tx. output , target_feerate_sat_per_1000_weight,
771- ) ?;
839+ claim_id,
840+ must_spend,
841+ & htlc_tx. output ,
842+ target_feerate_sat_per_1000_weight,
843+ ) . await ?;
772844
773845 #[ cfg( debug_assertions) ]
774846 let total_satisfaction_weight = must_spend_satisfaction_weight +
@@ -800,7 +872,7 @@ where
800872 let unsigned_tx_weight = htlc_psbt. unsigned_tx . weight ( ) . to_wu ( ) - ( htlc_psbt. unsigned_tx . input . len ( ) as u64 * EMPTY_SCRIPT_SIG_WEIGHT ) ;
801873
802874 log_debug ! ( self . logger, "Signing HTLC transaction {}" , htlc_psbt. unsigned_tx. compute_txid( ) ) ;
803- htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) ?;
875+ htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) . await ?;
804876
805877 let mut signers = BTreeMap :: new ( ) ;
806878 for ( idx, htlc_descriptor) in htlc_descriptors. iter ( ) . enumerate ( ) {
@@ -843,11 +915,17 @@ where
843915 log_info ! ( self . logger, "Handling channel close bump (claim_id = {}, commitment_txid = {})" ,
844916 log_bytes!( claim_id. 0 ) , commitment_tx. compute_txid( ) ) ;
845917 if let Err ( _) = self . handle_channel_close (
846- * claim_id, * package_target_feerate_sat_per_1000_weight, commitment_tx,
847- * commitment_tx_fee_satoshis, anchor_descriptor,
918+ * claim_id,
919+ * package_target_feerate_sat_per_1000_weight,
920+ commitment_tx,
921+ * commitment_tx_fee_satoshis,
922+ anchor_descriptor,
848923 ) . await {
849- log_error ! ( self . logger, "Failed bumping commitment transaction fee for {}" ,
850- commitment_tx. compute_txid( ) ) ;
924+ log_error ! (
925+ self . logger,
926+ "Failed bumping commitment transaction fee for {}" ,
927+ commitment_tx. compute_txid( )
928+ ) ;
851929 }
852930 }
853931 BumpTransactionEvent :: HTLCResolution {
@@ -856,10 +934,16 @@ where
856934 log_info ! ( self . logger, "Handling HTLC bump (claim_id = {}, htlcs_to_claim = {})" ,
857935 log_bytes!( claim_id. 0 ) , log_iter!( htlc_descriptors. iter( ) . map( |d| d. outpoint( ) ) ) ) ;
858936 if let Err ( _) = self . handle_htlc_resolution (
859- * claim_id, * target_feerate_sat_per_1000_weight, htlc_descriptors, * tx_lock_time,
937+ * claim_id,
938+ * target_feerate_sat_per_1000_weight,
939+ htlc_descriptors,
940+ * tx_lock_time,
860941 ) . await {
861- log_error ! ( self . logger, "Failed bumping HTLC transaction fee for commitment {}" ,
862- htlc_descriptors[ 0 ] . commitment_txid) ;
942+ log_error ! (
943+ self . logger,
944+ "Failed bumping HTLC transaction fee for commitment {}" ,
945+ htlc_descriptors[ 0 ] . commitment_txid
946+ ) ;
863947 }
864948 }
865949 }
@@ -886,30 +970,29 @@ mod tests {
886970 expected_selects : Mutex < Vec < ( u64 , u64 , u32 , CoinSelection ) > > ,
887971 }
888972 impl CoinSelectionSource for TestCoinSelectionSource {
889- fn select_confirmed_utxos (
890- & self ,
891- _claim_id : ClaimId ,
892- must_spend : Vec < Input > ,
893- _must_pay_to : & [ TxOut ] ,
894- target_feerate_sat_per_1000_weight : u32
895- ) -> Result < CoinSelection , ( ) > {
973+ fn select_confirmed_utxos < ' a > (
974+ & ' a self , _claim_id : ClaimId , must_spend : Vec < Input > , _must_pay_to : & ' a [ TxOut ] ,
975+ target_feerate_sat_per_1000_weight : u32 ,
976+ ) -> AsyncResult < ' a , CoinSelection > {
896977 let mut expected_selects = self . expected_selects . lock ( ) . unwrap ( ) ;
897978 let ( weight, value, feerate, res) = expected_selects. remove ( 0 ) ;
898979 assert_eq ! ( must_spend. len( ) , 1 ) ;
899980 assert_eq ! ( must_spend[ 0 ] . satisfaction_weight, weight) ;
900981 assert_eq ! ( must_spend[ 0 ] . previous_utxo. value. to_sat( ) , value) ;
901982 assert_eq ! ( target_feerate_sat_per_1000_weight, feerate) ;
983+ Box :: pin ( async move {
902984 Ok ( res)
985+ } )
903986 }
904- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
987+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
905988 let mut tx = psbt. unsigned_tx ;
906989 for input in tx. input . iter_mut ( ) {
907990 if input. previous_output . txid != Txid :: from_byte_array ( [ 44 ; 32 ] ) {
908991 // Channel output, add a realistic size witness to make the assertions happy
909992 input. witness = Witness :: from_slice ( & [ vec ! [ 42 ; 162 ] ] ) ;
910993 }
911994 }
912- Ok ( tx)
995+ Box :: pin ( async move { Ok ( tx) } )
913996 }
914997 }
915998
0 commit comments