@@ -30,6 +30,7 @@ use crate::sign::{
30
30
ChannelDerivationParameters , HTLCDescriptor , SignerProvider , P2WPKH_WITNESS_WEIGHT ,
31
31
} ;
32
32
use crate :: sync:: Mutex ;
33
+ use crate :: util:: async_poll:: { AsyncResult , MaybeSend , MaybeSync } ;
33
34
use crate :: util:: logger:: Logger ;
34
35
35
36
use bitcoin:: amount:: Amount ;
@@ -346,21 +347,38 @@ pub trait CoinSelectionSource {
346
347
/// other claims, implementations must be willing to double spend their UTXOs. The choice of
347
348
/// which UTXOs to double spend is left to the implementation, but it must strive to keep the
348
349
/// 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 ] ,
351
352
target_feerate_sat_per_1000_weight : u32 ,
352
- ) -> Result < CoinSelection , ( ) > ;
353
+ ) -> AsyncResult < ' a , CoinSelection > ;
353
354
/// Signs and provides the full witness for all inputs within the transaction known to the
354
355
/// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
355
356
///
356
357
/// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
357
358
/// 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 > ;
359
360
}
360
361
361
362
/// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
362
363
/// provide a default implementation to [`CoinSelectionSource`].
363
364
pub 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 {
364
382
/// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
365
383
fn list_confirmed_utxos ( & self ) -> Result < Vec < Utxo > , ( ) > ;
366
384
/// Returns a script to use for change above dust resulting from a successful coin selection
@@ -375,13 +393,54 @@ pub trait WalletSource {
375
393
fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > ;
376
394
}
377
395
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
+
378
437
/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
379
438
/// avoid conflicting double spends. If not enough UTXOs are available to do so, conflicting double
380
439
/// spends may happen.
381
- pub struct Wallet < W : Deref , L : Deref >
440
+ pub struct Wallet < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend >
382
441
where
383
- W :: Target : WalletSource ,
384
- L :: Target : Logger ,
442
+ W :: Target : WalletSource + MaybeSend ,
443
+ L :: Target : Logger + MaybeSend ,
385
444
{
386
445
source : W ,
387
446
logger : L ,
@@ -391,10 +450,10 @@ where
391
450
locked_utxos : Mutex < HashMap < OutPoint , ClaimId > > ,
392
451
}
393
452
394
- impl < W : Deref , L : Deref > Wallet < W , L >
453
+ impl < W : Deref + MaybeSync + MaybeSend , L : Deref + MaybeSync + MaybeSend > Wallet < W , L >
395
454
where
396
- W :: Target : WalletSource ,
397
- L :: Target : Logger ,
455
+ W :: Target : WalletSource + MaybeSend ,
456
+ L :: Target : Logger + MaybeSend ,
398
457
{
399
458
/// Returns a new instance backed by the given [`WalletSource`] that serves as an implementation
400
459
/// of [`CoinSelectionSource`].
@@ -410,7 +469,7 @@ where
410
469
/// `tolerate_high_network_feerates` is set, we'll attempt to spend UTXOs that contribute at
411
470
/// least 1 satoshi at the current feerate, otherwise, we'll only attempt to spend those which
412
471
/// contribute at least twice their fee.
413
- fn select_confirmed_utxos_internal (
472
+ async fn select_confirmed_utxos_internal (
414
473
& self , utxos : & [ Utxo ] , claim_id : ClaimId , force_conflicting_utxo_spend : bool ,
415
474
tolerate_high_network_feerates : bool , target_feerate_sat_per_1000_weight : u32 ,
416
475
preexisting_tx_weight : u64 , input_amount_sat : Amount , target_amount_sat : Amount ,
@@ -484,7 +543,7 @@ where
484
543
}
485
544
486
545
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 ?;
488
547
let change_output_fee = fee_for_weight (
489
548
target_feerate_sat_per_1000_weight,
490
549
( 8 /* value */ + change_script. consensus_encode ( & mut sink ( ) ) . unwrap ( ) as u64 )
@@ -503,60 +562,67 @@ where
503
562
}
504
563
}
505
564
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 >
507
567
where
508
- W :: Target : WalletSource ,
509
- L :: Target : Logger ,
568
+ W :: Target : WalletSource + MaybeSend + MaybeSync ,
569
+ L :: Target : Logger + MaybeSend + MaybeSync ,
510
570
{
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 ] ,
513
573
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 +
528
591
( ( 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 ( ) ;
531
594
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 ! (
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 ! (
535
598
self . logger,
536
599
"Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})" ,
537
600
target_feerate_sat_per_1000_weight,
538
601
force_conflicting_utxo_spend,
539
602
tolerate_high_network_feerates
540
603
) ;
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;
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
+ }
553
619
}
554
- }
555
- Err ( ( ) )
620
+ Err ( ( ) )
621
+ } )
556
622
}
557
623
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 } )
560
626
}
561
627
}
562
628
@@ -662,12 +728,15 @@ where
662
728
663
729
log_debug ! ( self . logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW" ,
664
730
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 ?;
671
740
672
741
let mut anchor_tx = Transaction {
673
742
version : Version :: TWO ,
@@ -733,7 +802,7 @@ where
733
802
}
734
803
735
804
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 ?;
737
806
738
807
let signer = self
739
808
. signer_provider
@@ -821,12 +890,15 @@ where
821
890
let must_spend_amount =
822
891
must_spend. iter ( ) . map ( |input| input. previous_utxo . value . to_sat ( ) ) . sum :: < u64 > ( ) ;
823
892
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 ?;
830
902
831
903
#[ cfg( debug_assertions) ]
832
904
let input_satisfaction_weight: u64 =
@@ -870,7 +942,7 @@ where
870
942
"Signing HTLC transaction {}" ,
871
943
htlc_psbt. unsigned_tx. compute_txid( )
872
944
) ;
873
- htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) ?;
945
+ htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) . await ?;
874
946
875
947
let mut signers = BTreeMap :: new ( ) ;
876
948
for ( idx, htlc_descriptor) in htlc_descriptors. iter ( ) . enumerate ( ) {
@@ -993,27 +1065,27 @@ mod tests {
993
1065
expected_selects : Mutex < Vec < ( u64 , u64 , u32 , CoinSelection ) > > ,
994
1066
}
995
1067
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 ] ,
998
1070
target_feerate_sat_per_1000_weight : u32 ,
999
- ) -> Result < CoinSelection , ( ) > {
1071
+ ) -> AsyncResult < ' a , CoinSelection > {
1000
1072
let mut expected_selects = self . expected_selects . lock ( ) . unwrap ( ) ;
1001
1073
let ( weight, value, feerate, res) = expected_selects. remove ( 0 ) ;
1002
1074
assert_eq ! ( must_spend. len( ) , 1 ) ;
1003
1075
assert_eq ! ( must_spend[ 0 ] . satisfaction_weight, weight) ;
1004
1076
assert_eq ! ( must_spend[ 0 ] . previous_utxo. value. to_sat( ) , value) ;
1005
1077
assert_eq ! ( target_feerate_sat_per_1000_weight, feerate) ;
1006
- Ok ( res)
1078
+ Box :: pin ( async move { Ok ( res) } )
1007
1079
}
1008
- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
1080
+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
1009
1081
let mut tx = psbt. unsigned_tx ;
1010
1082
for input in tx. input . iter_mut ( ) {
1011
1083
if input. previous_output . txid != Txid :: from_byte_array ( [ 44 ; 32 ] ) {
1012
1084
// Channel output, add a realistic size witness to make the assertions happy
1013
1085
input. witness = Witness :: from_slice ( & [ vec ! [ 42 ; 162 ] ] ) ;
1014
1086
}
1015
1087
}
1016
- Ok ( tx)
1088
+ Box :: pin ( async move { Ok ( tx) } )
1017
1089
}
1018
1090
}
1019
1091
0 commit comments