@@ -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 ,
@@ -481,7 +540,7 @@ where
481
540
}
482
541
483
542
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 ?;
485
544
let change_output_fee = fee_for_weight (
486
545
target_feerate_sat_per_1000_weight,
487
546
( 8 /* value */ + change_script. consensus_encode ( & mut sink ( ) ) . unwrap ( ) as u64 )
@@ -500,60 +559,67 @@ where
500
559
}
501
560
}
502
561
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 >
504
564
where
505
- W :: Target : WalletSource ,
506
- L :: Target : Logger ,
565
+ W :: Target : WalletSource + MaybeSend + MaybeSync ,
566
+ L :: Target : Logger + MaybeSend + MaybeSync ,
507
567
{
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 ] ,
510
570
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 +
525
588
( ( 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 ( ) ;
528
591
529
- let configs = [ ( false , false ) , ( false , true ) , ( true , false ) , ( true , true ) ] ;
530
- for ( force_conflicting_utxo_spend, tolerate_high_network_feerates) in configs {
531
- log_debug ! (
592
+ let configs = [ ( false , false ) , ( false , true ) , ( true , false ) , ( true , true ) ] ;
593
+ for ( force_conflicting_utxo_spend, tolerate_high_network_feerates) in configs {
594
+ log_debug ! (
532
595
self . logger,
533
596
"Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})" ,
534
597
target_feerate_sat_per_1000_weight,
535
598
force_conflicting_utxo_spend,
536
599
tolerate_high_network_feerates
537
600
) ;
538
- let attempt = self . select_confirmed_utxos_internal (
539
- & utxos,
540
- claim_id,
541
- force_conflicting_utxo_spend,
542
- tolerate_high_network_feerates,
543
- target_feerate_sat_per_1000_weight,
544
- preexisting_tx_weight,
545
- input_amount_sat,
546
- target_amount_sat,
547
- ) ;
548
- if attempt. is_ok ( ) {
549
- return attempt;
601
+ let attempt = self
602
+ . select_confirmed_utxos_internal (
603
+ & utxos,
604
+ claim_id,
605
+ force_conflicting_utxo_spend,
606
+ tolerate_high_network_feerates,
607
+ target_feerate_sat_per_1000_weight,
608
+ preexisting_tx_weight,
609
+ input_amount_sat,
610
+ target_amount_sat,
611
+ )
612
+ . await ;
613
+ if attempt. is_ok ( ) {
614
+ return attempt;
615
+ }
550
616
}
551
- }
552
- Err ( ( ) )
617
+ Err ( ( ) )
618
+ } )
553
619
}
554
620
555
- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
556
- self . source . sign_psbt ( psbt)
621
+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
622
+ Box :: pin ( async move { self . source . sign_psbt ( psbt) . await } )
557
623
}
558
624
}
559
625
@@ -659,12 +725,15 @@ where
659
725
660
726
log_debug ! ( self . logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW" ,
661
727
package_target_feerate_sat_per_1000_weight) ;
662
- let coin_selection: CoinSelection = self . utxo_source . select_confirmed_utxos (
663
- claim_id,
664
- must_spend,
665
- & [ ] ,
666
- package_target_feerate_sat_per_1000_weight,
667
- ) ?;
728
+ let coin_selection: CoinSelection = self
729
+ . utxo_source
730
+ . select_confirmed_utxos (
731
+ claim_id,
732
+ must_spend,
733
+ & [ ] ,
734
+ package_target_feerate_sat_per_1000_weight,
735
+ )
736
+ . await ?;
668
737
669
738
let mut anchor_tx = Transaction {
670
739
version : Version :: TWO ,
@@ -730,7 +799,7 @@ where
730
799
}
731
800
732
801
log_debug ! ( self . logger, "Signing anchor transaction {}" , anchor_txid) ;
733
- anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) ?;
802
+ anchor_tx = self . utxo_source . sign_psbt ( anchor_psbt) . await ?;
734
803
735
804
let signer = self
736
805
. signer_provider
@@ -818,12 +887,15 @@ where
818
887
let must_spend_amount =
819
888
must_spend. iter ( ) . map ( |input| input. previous_utxo . value . to_sat ( ) ) . sum :: < u64 > ( ) ;
820
889
821
- let coin_selection: CoinSelection = self . utxo_source . select_confirmed_utxos (
822
- claim_id,
823
- must_spend,
824
- & htlc_tx. output ,
825
- target_feerate_sat_per_1000_weight,
826
- ) ?;
890
+ let coin_selection: CoinSelection = self
891
+ . utxo_source
892
+ . select_confirmed_utxos (
893
+ claim_id,
894
+ must_spend,
895
+ & htlc_tx. output ,
896
+ target_feerate_sat_per_1000_weight,
897
+ )
898
+ . await ?;
827
899
828
900
#[ cfg( debug_assertions) ]
829
901
let input_satisfaction_weight: u64 =
@@ -867,7 +939,7 @@ where
867
939
"Signing HTLC transaction {}" ,
868
940
htlc_psbt. unsigned_tx. compute_txid( )
869
941
) ;
870
- htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) ?;
942
+ htlc_tx = self . utxo_source . sign_psbt ( htlc_psbt) . await ?;
871
943
872
944
let mut signers = BTreeMap :: new ( ) ;
873
945
for ( idx, htlc_descriptor) in htlc_descriptors. iter ( ) . enumerate ( ) {
@@ -992,27 +1064,27 @@ mod tests {
992
1064
expected_selects : Mutex < Vec < ( u64 , u64 , u32 , CoinSelection ) > > ,
993
1065
}
994
1066
impl CoinSelectionSource for TestCoinSelectionSource {
995
- fn select_confirmed_utxos (
996
- & self , _claim_id : ClaimId , must_spend : Vec < Input > , _must_pay_to : & [ TxOut ] ,
1067
+ fn select_confirmed_utxos < ' a > (
1068
+ & ' a self , _claim_id : ClaimId , must_spend : Vec < Input > , _must_pay_to : & ' a [ TxOut ] ,
997
1069
target_feerate_sat_per_1000_weight : u32 ,
998
- ) -> Result < CoinSelection , ( ) > {
1070
+ ) -> AsyncResult < ' a , CoinSelection > {
999
1071
let mut expected_selects = self . expected_selects . lock ( ) . unwrap ( ) ;
1000
1072
let ( weight, value, feerate, res) = expected_selects. remove ( 0 ) ;
1001
1073
assert_eq ! ( must_spend. len( ) , 1 ) ;
1002
1074
assert_eq ! ( must_spend[ 0 ] . satisfaction_weight, weight) ;
1003
1075
assert_eq ! ( must_spend[ 0 ] . previous_utxo. value. to_sat( ) , value) ;
1004
1076
assert_eq ! ( target_feerate_sat_per_1000_weight, feerate) ;
1005
- Ok ( res)
1077
+ Box :: pin ( async move { Ok ( res) } )
1006
1078
}
1007
- fn sign_psbt ( & self , psbt : Psbt ) -> Result < Transaction , ( ) > {
1079
+ fn sign_psbt < ' a > ( & ' a self , psbt : Psbt ) -> AsyncResult < ' a , Transaction > {
1008
1080
let mut tx = psbt. unsigned_tx ;
1009
1081
for input in tx. input . iter_mut ( ) {
1010
1082
if input. previous_output . txid != Txid :: from_byte_array ( [ 44 ; 32 ] ) {
1011
1083
// Channel output, add a realistic size witness to make the assertions happy
1012
1084
input. witness = Witness :: from_slice ( & [ vec ! [ 42 ; 162 ] ] ) ;
1013
1085
}
1014
1086
}
1015
- Ok ( tx)
1087
+ Box :: pin ( async move { Ok ( tx) } )
1016
1088
}
1017
1089
}
1018
1090
0 commit comments