Skip to content

Commit 94bbb5c

Browse files
committed
Convert BumpTransactionEventHandler to async
1 parent 048230a commit 94bbb5c

File tree

8 files changed

+384
-105
lines changed

8 files changed

+384
-105
lines changed

lightning/src/events/bump_transaction.rs

Lines changed: 109 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
//!
1212
//! [`Event`]: crate::events::Event
1313
14+
pub mod sync;
15+
1416
use alloc::collections::BTreeMap;
1517
use core::ops::Deref;
1618

@@ -30,6 +32,7 @@ use crate::sign::{
3032
ChannelDerivationParameters, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT,
3133
};
3234
use crate::sync::Mutex;
35+
use crate::util::async_poll::{AsyncResult, MaybeSend, MaybeSync};
3336
use crate::util::logger::Logger;
3437

3538
use bitcoin::amount::Amount;
@@ -346,42 +349,42 @@ pub trait CoinSelectionSource {
346349
/// other claims, implementations must be willing to double spend their UTXOs. The choice of
347350
/// which UTXOs to double spend is left to the implementation, but it must strive to keep the
348351
/// 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],
352+
fn select_confirmed_utxos<'a>(
353+
&'a self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
351354
target_feerate_sat_per_1000_weight: u32,
352-
) -> Result<CoinSelection, ()>;
355+
) -> AsyncResult<'a, CoinSelection>;
353356
/// Signs and provides the full witness for all inputs within the transaction known to the
354357
/// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
355358
///
356359
/// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
357360
/// unsigned transaction and then sign it with your wallet.
358-
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
361+
fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction>;
359362
}
360363

361364
/// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
362365
/// provide a default implementation to [`CoinSelectionSource`].
363366
pub trait WalletSource {
364367
/// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
365-
fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()>;
368+
fn list_confirmed_utxos<'a>(&'a self) -> AsyncResult<'a, Vec<Utxo>>;
366369
/// Returns a script to use for change above dust resulting from a successful coin selection
367370
/// attempt.
368-
fn get_change_script(&self) -> Result<ScriptBuf, ()>;
371+
fn get_change_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf>;
369372
/// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
370373
/// the transaction known to the wallet (i.e., any provided via
371374
/// [`WalletSource::list_confirmed_utxos`]).
372375
///
373376
/// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
374377
/// unsigned transaction and then sign it with your wallet.
375-
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
378+
fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction>;
376379
}
377380

378381
/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
379382
/// avoid conflicting double spends. If not enough UTXOs are available to do so, conflicting double
380383
/// spends may happen.
381-
pub struct Wallet<W: Deref, L: Deref>
384+
pub struct Wallet<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend>
382385
where
383-
W::Target: WalletSource,
384-
L::Target: Logger,
386+
W::Target: WalletSource + MaybeSend,
387+
L::Target: Logger + MaybeSend,
385388
{
386389
source: W,
387390
logger: L,
@@ -391,10 +394,10 @@ where
391394
locked_utxos: Mutex<HashMap<OutPoint, ClaimId>>,
392395
}
393396

394-
impl<W: Deref, L: Deref> Wallet<W, L>
397+
impl<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend> Wallet<W, L>
395398
where
396-
W::Target: WalletSource,
397-
L::Target: Logger,
399+
W::Target: WalletSource + MaybeSend,
400+
L::Target: Logger + MaybeSend,
398401
{
399402
/// Returns a new instance backed by the given [`WalletSource`] that serves as an implementation
400403
/// of [`CoinSelectionSource`].
@@ -410,7 +413,7 @@ where
410413
/// `tolerate_high_network_feerates` is set, we'll attempt to spend UTXOs that contribute at
411414
/// least 1 satoshi at the current feerate, otherwise, we'll only attempt to spend those which
412415
/// contribute at least twice their fee.
413-
fn select_confirmed_utxos_internal(
416+
async fn select_confirmed_utxos_internal(
414417
&self, utxos: &[Utxo], claim_id: ClaimId, force_conflicting_utxo_spend: bool,
415418
tolerate_high_network_feerates: bool, target_feerate_sat_per_1000_weight: u32,
416419
preexisting_tx_weight: u64, input_amount_sat: Amount, target_amount_sat: Amount,
@@ -484,7 +487,7 @@ where
484487
}
485488

486489
let remaining_amount = selected_amount - target_amount_sat - total_fees;
487-
let change_script = self.source.get_change_script()?;
490+
let change_script = self.source.get_change_script().await?;
488491
let change_output_fee = fee_for_weight(
489492
target_feerate_sat_per_1000_weight,
490493
(8 /* value */ + change_script.consensus_encode(&mut sink()).unwrap() as u64)
@@ -503,60 +506,67 @@ where
503506
}
504507
}
505508

506-
impl<W: Deref, L: Deref> CoinSelectionSource for Wallet<W, L>
509+
impl<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend> CoinSelectionSource
510+
for Wallet<W, L>
507511
where
508-
W::Target: WalletSource,
509-
L::Target: Logger,
512+
W::Target: WalletSource + MaybeSend + MaybeSync,
513+
L::Target: Logger + MaybeSend + MaybeSync,
510514
{
511-
fn select_confirmed_utxos(
512-
&self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &[TxOut],
515+
fn select_confirmed_utxos<'a>(
516+
&'a self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
513517
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 +
518+
) -> AsyncResult<'a, CoinSelection> {
519+
Box::pin(async move {
520+
let utxos = self.source.list_confirmed_utxos().await?;
521+
// TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
522+
const BASE_TX_SIZE: u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */;
523+
let total_output_size: u64 = must_pay_to
524+
.iter()
525+
.map(
526+
|output| 8 /* value */ + 1 /* script len */ + output.script_pubkey.len() as u64,
527+
)
528+
.sum();
529+
let total_satisfaction_weight: u64 =
530+
must_spend.iter().map(|input| input.satisfaction_weight).sum();
531+
let total_input_weight =
532+
(BASE_INPUT_WEIGHT * must_spend.len() as u64) + total_satisfaction_weight;
533+
534+
let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
528535
((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();
536+
let input_amount_sat = must_spend.iter().map(|input| input.previous_utxo.value).sum();
537+
let target_amount_sat = must_pay_to.iter().map(|output| output.value).sum();
531538

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;
539+
let configs = [(false, false), (false, true), (true, false), (true, true)];
540+
for (force_conflicting_utxo_spend, tolerate_high_network_feerates) in configs {
541+
log_debug!(
542+
self.logger,
543+
"Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})",
544+
target_feerate_sat_per_1000_weight,
545+
force_conflicting_utxo_spend,
546+
tolerate_high_network_feerates
547+
);
548+
let attempt = self
549+
.select_confirmed_utxos_internal(
550+
&utxos,
551+
claim_id,
552+
force_conflicting_utxo_spend,
553+
tolerate_high_network_feerates,
554+
target_feerate_sat_per_1000_weight,
555+
preexisting_tx_weight,
556+
input_amount_sat,
557+
target_amount_sat,
558+
)
559+
.await;
560+
if attempt.is_ok() {
561+
return attempt;
562+
}
553563
}
554-
}
555-
Err(())
564+
Err(())
565+
})
556566
}
557567

558-
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
559-
self.source.sign_psbt(psbt)
568+
fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction> {
569+
Box::pin(async move { self.source.sign_psbt(psbt).await })
560570
}
561571
}
562572

@@ -635,7 +645,7 @@ where
635645
/// Handles a [`BumpTransactionEvent::ChannelClose`] event variant by producing a fully-signed
636646
/// transaction spending an anchor output of the commitment transaction to bump its fee and
637647
/// broadcasts them to the network as a package.
638-
fn handle_channel_close(
648+
async fn handle_channel_close(
639649
&self, claim_id: ClaimId, package_target_feerate_sat_per_1000_weight: u32,
640650
commitment_tx: &Transaction, commitment_tx_fee_sat: u64,
641651
anchor_descriptor: &AnchorDescriptor,
@@ -662,12 +672,15 @@ where
662672

663673
log_debug!(self.logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW",
664674
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-
)?;
675+
let coin_selection: CoinSelection = self
676+
.utxo_source
677+
.select_confirmed_utxos(
678+
claim_id,
679+
must_spend,
680+
&[],
681+
package_target_feerate_sat_per_1000_weight,
682+
)
683+
.await?;
671684

672685
let mut anchor_tx = Transaction {
673686
version: Version::TWO,
@@ -733,7 +746,7 @@ where
733746
}
734747

735748
log_debug!(self.logger, "Signing anchor transaction {}", anchor_txid);
736-
anchor_tx = self.utxo_source.sign_psbt(anchor_psbt)?;
749+
anchor_tx = self.utxo_source.sign_psbt(anchor_psbt).await?;
737750

738751
let signer = self
739752
.signer_provider
@@ -780,7 +793,7 @@ where
780793

781794
/// Handles a [`BumpTransactionEvent::HTLCResolution`] event variant by producing a
782795
/// fully-signed, fee-bumped HTLC transaction that is broadcast to the network.
783-
fn handle_htlc_resolution(
796+
async fn handle_htlc_resolution(
784797
&self, claim_id: ClaimId, target_feerate_sat_per_1000_weight: u32,
785798
htlc_descriptors: &[HTLCDescriptor], tx_lock_time: LockTime,
786799
) -> Result<(), ()> {
@@ -821,12 +834,15 @@ where
821834
let must_spend_amount =
822835
must_spend.iter().map(|input| input.previous_utxo.value.to_sat()).sum::<u64>();
823836

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-
)?;
837+
let coin_selection: CoinSelection = self
838+
.utxo_source
839+
.select_confirmed_utxos(
840+
claim_id,
841+
must_spend,
842+
&htlc_tx.output,
843+
target_feerate_sat_per_1000_weight,
844+
)
845+
.await?;
830846

831847
#[cfg(debug_assertions)]
832848
let input_satisfaction_weight: u64 =
@@ -870,7 +886,7 @@ where
870886
"Signing HTLC transaction {}",
871887
htlc_psbt.unsigned_tx.compute_txid()
872888
);
873-
htlc_tx = self.utxo_source.sign_psbt(htlc_psbt)?;
889+
htlc_tx = self.utxo_source.sign_psbt(htlc_psbt).await?;
874890

875891
let mut signers = BTreeMap::new();
876892
for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
@@ -909,7 +925,7 @@ where
909925
}
910926

911927
/// Handles all variants of [`BumpTransactionEvent`].
912-
pub fn handle_event(&self, event: &BumpTransactionEvent) {
928+
pub async fn handle_event(&self, event: &BumpTransactionEvent) {
913929
match event {
914930
BumpTransactionEvent::ChannelClose {
915931
claim_id,
@@ -925,19 +941,21 @@ where
925941
log_bytes!(claim_id.0),
926942
commitment_tx.compute_txid()
927943
);
928-
if let Err(_) = self.handle_channel_close(
944+
self.handle_channel_close(
929945
*claim_id,
930946
*package_target_feerate_sat_per_1000_weight,
931947
commitment_tx,
932948
*commitment_tx_fee_satoshis,
933949
anchor_descriptor,
934-
) {
950+
)
951+
.await
952+
.unwrap_or_else(|_| {
935953
log_error!(
936954
self.logger,
937955
"Failed bumping commitment transaction fee for {}",
938956
commitment_tx.compute_txid()
939957
);
940-
}
958+
});
941959
},
942960
BumpTransactionEvent::HTLCResolution {
943961
claim_id,
@@ -952,18 +970,20 @@ where
952970
log_bytes!(claim_id.0),
953971
log_iter!(htlc_descriptors.iter().map(|d| d.outpoint()))
954972
);
955-
if let Err(_) = self.handle_htlc_resolution(
973+
self.handle_htlc_resolution(
956974
*claim_id,
957975
*target_feerate_sat_per_1000_weight,
958976
htlc_descriptors,
959977
*tx_lock_time,
960-
) {
978+
)
979+
.await
980+
.unwrap_or_else(|_| {
961981
log_error!(
962982
self.logger,
963983
"Failed bumping HTLC transaction fee for commitment {}",
964984
htlc_descriptors[0].commitment_txid
965985
);
966-
}
986+
});
967987
},
968988
}
969989
}
@@ -973,6 +993,9 @@ where
973993
mod tests {
974994
use super::*;
975995

996+
use crate::events::bump_transaction::sync::{
997+
BumpTransactionEventHandlerSync, CoinSelectionSourceSync,
998+
};
976999
use crate::io::Cursor;
9771000
use crate::ln::chan_utils::ChannelTransactionParameters;
9781001
use crate::sign::KeysManager;
@@ -988,7 +1011,7 @@ mod tests {
9881011
// (commitment + anchor value, commitment + input weight, target feerate, result)
9891012
expected_selects: Mutex<Vec<(u64, u64, u32, CoinSelection)>>,
9901013
}
991-
impl CoinSelectionSource for TestCoinSelectionSource {
1014+
impl CoinSelectionSourceSync for TestCoinSelectionSource {
9921015
fn select_confirmed_utxos(
9931016
&self, _claim_id: ClaimId, must_spend: Vec<Input>, _must_pay_to: &[TxOut],
9941017
target_feerate_sat_per_1000_weight: u32,
@@ -1073,7 +1096,7 @@ mod tests {
10731096
};
10741097
let signer = KeysManager::new(&[42; 32], 42, 42);
10751098
let logger = TestLogger::new();
1076-
let handler = BumpTransactionEventHandler::new(&broadcaster, &source, &signer, &logger);
1099+
let handler = BumpTransactionEventHandlerSync::new(&broadcaster, &source, &signer, &logger);
10771100

10781101
let mut transaction_parameters = ChannelTransactionParameters::test_dummy(42_000_000);
10791102
transaction_parameters.channel_type_features =

0 commit comments

Comments
 (0)