Skip to content

Commit 1892700

Browse files
committed
Convert BumpTransactionEventHandler to async
1 parent 35ccef6 commit 1892700

File tree

9 files changed

+400
-105
lines changed

9 files changed

+400
-105
lines changed

lightning/src/events/bump_transaction.rs

Lines changed: 107 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use crate::sign::{
3030
ChannelDerivationParameters, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT,
3131
};
3232
use crate::sync::Mutex;
33+
use crate::util::async_poll::{AsyncResult, MaybeSend, MaybeSync};
3334
use crate::util::logger::Logger;
3435

3536
use bitcoin::amount::Amount;
@@ -346,42 +347,42 @@ 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`].
363364
pub trait WalletSource {
364365
/// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
365-
fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()>;
366+
fn list_confirmed_utxos<'a>(&'a self) -> AsyncResult<'a, Vec<Utxo>>;
366367
/// Returns a script to use for change above dust resulting from a successful coin selection
367368
/// attempt.
368-
fn get_change_script(&self) -> Result<ScriptBuf, ()>;
369+
fn get_change_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf>;
369370
/// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
370371
/// the transaction known to the wallet (i.e., any provided via
371372
/// [`WalletSource::list_confirmed_utxos`]).
372373
///
373374
/// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
374375
/// unsigned transaction and then sign it with your wallet.
375-
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
376+
fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction>;
376377
}
377378

378379
/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
379380
/// avoid conflicting double spends. If not enough UTXOs are available to do so, conflicting double
380381
/// spends may happen.
381-
pub struct Wallet<W: Deref, L: Deref>
382+
pub struct Wallet<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend>
382383
where
383-
W::Target: WalletSource,
384-
L::Target: Logger,
384+
W::Target: WalletSource + MaybeSend,
385+
L::Target: Logger + MaybeSend,
385386
{
386387
source: W,
387388
logger: L,
@@ -391,10 +392,10 @@ where
391392
locked_utxos: Mutex<HashMap<OutPoint, ClaimId>>,
392393
}
393394

394-
impl<W: Deref, L: Deref> Wallet<W, L>
395+
impl<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend> Wallet<W, L>
395396
where
396-
W::Target: WalletSource,
397-
L::Target: Logger,
397+
W::Target: WalletSource + MaybeSend,
398+
L::Target: Logger + MaybeSend,
398399
{
399400
/// Returns a new instance backed by the given [`WalletSource`] that serves as an implementation
400401
/// of [`CoinSelectionSource`].
@@ -410,7 +411,7 @@ where
410411
/// `tolerate_high_network_feerates` is set, we'll attempt to spend UTXOs that contribute at
411412
/// least 1 satoshi at the current feerate, otherwise, we'll only attempt to spend those which
412413
/// contribute at least twice their fee.
413-
fn select_confirmed_utxos_internal(
414+
async fn select_confirmed_utxos_internal(
414415
&self, utxos: &[Utxo], claim_id: ClaimId, force_conflicting_utxo_spend: bool,
415416
tolerate_high_network_feerates: bool, target_feerate_sat_per_1000_weight: u32,
416417
preexisting_tx_weight: u64, input_amount_sat: Amount, target_amount_sat: Amount,
@@ -484,7 +485,7 @@ where
484485
}
485486

486487
let remaining_amount = selected_amount - target_amount_sat - total_fees;
487-
let change_script = self.source.get_change_script()?;
488+
let change_script = self.source.get_change_script().await?;
488489
let change_output_fee = fee_for_weight(
489490
target_feerate_sat_per_1000_weight,
490491
(8 /* value */ + change_script.consensus_encode(&mut sink()).unwrap() as u64)
@@ -503,60 +504,67 @@ where
503504
}
504505
}
505506

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

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

558-
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
559-
self.source.sign_psbt(psbt)
566+
fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction> {
567+
Box::pin(async move { self.source.sign_psbt(psbt).await })
560568
}
561569
}
562570

@@ -635,7 +643,7 @@ where
635643
/// Handles a [`BumpTransactionEvent::ChannelClose`] event variant by producing a fully-signed
636644
/// transaction spending an anchor output of the commitment transaction to bump its fee and
637645
/// broadcasts them to the network as a package.
638-
fn handle_channel_close(
646+
async fn handle_channel_close(
639647
&self, claim_id: ClaimId, package_target_feerate_sat_per_1000_weight: u32,
640648
commitment_tx: &Transaction, commitment_tx_fee_sat: u64,
641649
anchor_descriptor: &AnchorDescriptor,
@@ -662,12 +670,15 @@ where
662670

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

672683
let mut anchor_tx = Transaction {
673684
version: Version::TWO,
@@ -733,7 +744,7 @@ where
733744
}
734745

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

738749
let signer = self
739750
.signer_provider
@@ -780,7 +791,7 @@ where
780791

781792
/// Handles a [`BumpTransactionEvent::HTLCResolution`] event variant by producing a
782793
/// fully-signed, fee-bumped HTLC transaction that is broadcast to the network.
783-
fn handle_htlc_resolution(
794+
async fn handle_htlc_resolution(
784795
&self, claim_id: ClaimId, target_feerate_sat_per_1000_weight: u32,
785796
htlc_descriptors: &[HTLCDescriptor], tx_lock_time: LockTime,
786797
) -> Result<(), ()> {
@@ -821,12 +832,15 @@ where
821832
let must_spend_amount =
822833
must_spend.iter().map(|input| input.previous_utxo.value.to_sat()).sum::<u64>();
823834

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

831845
#[cfg(debug_assertions)]
832846
let input_satisfaction_weight: u64 =
@@ -870,7 +884,7 @@ where
870884
"Signing HTLC transaction {}",
871885
htlc_psbt.unsigned_tx.compute_txid()
872886
);
873-
htlc_tx = self.utxo_source.sign_psbt(htlc_psbt)?;
887+
htlc_tx = self.utxo_source.sign_psbt(htlc_psbt).await?;
874888

875889
let mut signers = BTreeMap::new();
876890
for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
@@ -909,7 +923,7 @@ where
909923
}
910924

911925
/// Handles all variants of [`BumpTransactionEvent`].
912-
pub fn handle_event(&self, event: &BumpTransactionEvent) {
926+
pub async fn handle_event(&self, event: &BumpTransactionEvent) {
913927
match event {
914928
BumpTransactionEvent::ChannelClose {
915929
claim_id,
@@ -925,19 +939,21 @@ where
925939
log_bytes!(claim_id.0),
926940
commitment_tx.compute_txid()
927941
);
928-
if let Err(_) = self.handle_channel_close(
942+
self.handle_channel_close(
929943
*claim_id,
930944
*package_target_feerate_sat_per_1000_weight,
931945
commitment_tx,
932946
*commitment_tx_fee_satoshis,
933947
anchor_descriptor,
934-
) {
948+
)
949+
.await
950+
.unwrap_or_else(|_| {
935951
log_error!(
936952
self.logger,
937953
"Failed bumping commitment transaction fee for {}",
938954
commitment_tx.compute_txid()
939955
);
940-
}
956+
});
941957
},
942958
BumpTransactionEvent::HTLCResolution {
943959
claim_id,
@@ -952,18 +968,20 @@ where
952968
log_bytes!(claim_id.0),
953969
log_iter!(htlc_descriptors.iter().map(|d| d.outpoint()))
954970
);
955-
if let Err(_) = self.handle_htlc_resolution(
971+
self.handle_htlc_resolution(
956972
*claim_id,
957973
*target_feerate_sat_per_1000_weight,
958974
htlc_descriptors,
959975
*tx_lock_time,
960-
) {
976+
)
977+
.await
978+
.unwrap_or_else(|_| {
961979
log_error!(
962980
self.logger,
963981
"Failed bumping HTLC transaction fee for commitment {}",
964982
htlc_descriptors[0].commitment_txid
965983
);
966-
}
984+
});
967985
},
968986
}
969987
}
@@ -973,6 +991,9 @@ where
973991
mod tests {
974992
use super::*;
975993

994+
use crate::events::bump_transaction_sync::{
995+
BumpTransactionEventHandlerSync, CoinSelectionSourceSync,
996+
};
976997
use crate::io::Cursor;
977998
use crate::ln::chan_utils::ChannelTransactionParameters;
978999
use crate::sign::KeysManager;
@@ -988,7 +1009,7 @@ mod tests {
9881009
// (commitment + anchor value, commitment + input weight, target feerate, result)
9891010
expected_selects: Mutex<Vec<(u64, u64, u32, CoinSelection)>>,
9901011
}
991-
impl CoinSelectionSource for TestCoinSelectionSource {
1012+
impl CoinSelectionSourceSync for TestCoinSelectionSource {
9921013
fn select_confirmed_utxos(
9931014
&self, _claim_id: ClaimId, must_spend: Vec<Input>, _must_pay_to: &[TxOut],
9941015
target_feerate_sat_per_1000_weight: u32,
@@ -1073,7 +1094,7 @@ mod tests {
10731094
};
10741095
let signer = KeysManager::new(&[42; 32], 42, 42);
10751096
let logger = TestLogger::new();
1076-
let handler = BumpTransactionEventHandler::new(&broadcaster, &source, &signer, &logger);
1097+
let handler = BumpTransactionEventHandlerSync::new(&broadcaster, &source, &signer, &logger);
10771098

10781099
let mut transaction_parameters = ChannelTransactionParameters::test_dummy(42_000_000);
10791100
transaction_parameters.channel_type_features =

0 commit comments

Comments
 (0)