Skip to content

Commit d9b1c75

Browse files
committed
Async wallet bump
1 parent 4815164 commit d9b1c75

File tree

2 files changed

+93
-63
lines changed

2 files changed

+93
-63
lines changed

lightning/src/events/bump_transaction.rs

+84-53
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,10 @@ pub trait CoinSelectionSource {
345345
/// other claims, implementations must be willing to double spend their UTXOs. The choice of
346346
/// which UTXOs to double spend is left to the implementation, but it must strive to keep the
347347
/// set of other claims being double spent to a minimum.
348-
fn select_confirmed_utxos(
349-
&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],
350350
target_feerate_sat_per_1000_weight: u32,
351-
) -> Result<CoinSelection, ()>;
351+
) -> AsyncResult<'a, CoinSelection>;
352352
/// Signs and provides the full witness for all inputs within the transaction known to the
353353
/// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
354354
///
@@ -361,7 +361,7 @@ pub trait CoinSelectionSource {
361361
/// provide a default implementation to [`CoinSelectionSource`].
362362
pub trait WalletSource {
363363
/// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
364-
fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()>;
364+
fn list_confirmed_utxos<'a>(&'a self) -> AsyncResult<'a, Vec<Utxo>>;
365365
/// Returns a script to use for change above dust resulting from a successful coin selection
366366
/// attempt.
367367
fn get_change_script(&self) -> Result<ScriptBuf, ()>;
@@ -482,40 +482,54 @@ where
482482
}
483483
}
484484

485-
impl<W: Deref, L: Deref> CoinSelectionSource for Wallet<W, L>
485+
impl<W: Deref + Sync, L: Deref + Sync> CoinSelectionSource for Wallet<W, L>
486486
where
487487
W::Target: WalletSource,
488-
L::Target: Logger
488+
L::Target: Logger,
489489
{
490-
fn select_confirmed_utxos(
491-
&self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &[TxOut],
490+
fn select_confirmed_utxos<'a>(
491+
&'a self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
492492
target_feerate_sat_per_1000_weight: u32,
493-
) -> Result<CoinSelection, ()> {
494-
let utxos = self.source.list_confirmed_utxos()?;
495-
// TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
496-
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;
502-
503-
let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
504-
((BASE_TX_SIZE + total_output_size) * WITNESS_SCALE_FACTOR as u64);
505-
let input_amount_sat = must_spend.iter().map(|input| input.previous_utxo.value).sum();
506-
let target_amount_sat = must_pay_to.iter().map(|output| output.value).sum();
507-
let do_coin_selection = |force_conflicting_utxo_spend: bool, tolerate_high_network_feerates: bool| {
508-
log_debug!(self.logger, "Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})",
509-
target_feerate_sat_per_1000_weight, force_conflicting_utxo_spend, tolerate_high_network_feerates);
510-
self.select_confirmed_utxos_internal(
511-
&utxos, claim_id, force_conflicting_utxo_spend, tolerate_high_network_feerates,
512-
target_feerate_sat_per_1000_weight, preexisting_tx_weight, input_amount_sat, target_amount_sat,
513-
)
514-
};
515-
do_coin_selection(false, false)
516-
.or_else(|_| do_coin_selection(false, true))
517-
.or_else(|_| do_coin_selection(true, false))
518-
.or_else(|_| do_coin_selection(true, true))
493+
) -> AsyncResult<'a, CoinSelection> {
494+
Box::pin(async move {
495+
let utxos = self.source.list_confirmed_utxos().await?;
496+
// TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
497+
const BASE_TX_SIZE: u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */;
498+
let total_output_size: u64 = must_pay_to
499+
.iter()
500+
.map(
501+
|output| 8 /* value */ + 1 /* script len */ + output.script_pubkey.len() as u64,
502+
)
503+
.sum();
504+
let total_satisfaction_weight: u64 =
505+
must_spend.iter().map(|input| input.satisfaction_weight).sum();
506+
let total_input_weight =
507+
(BASE_INPUT_WEIGHT * must_spend.len() as u64) + total_satisfaction_weight;
508+
509+
let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
510+
((BASE_TX_SIZE + total_output_size) * WITNESS_SCALE_FACTOR as u64);
511+
let input_amount_sat = must_spend.iter().map(|input| input.previous_utxo.value).sum();
512+
let target_amount_sat = must_pay_to.iter().map(|output| output.value).sum();
513+
let do_coin_selection =
514+
|force_conflicting_utxo_spend: bool, tolerate_high_network_feerates: bool| {
515+
log_debug!(self.logger, "Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})",
516+
target_feerate_sat_per_1000_weight, force_conflicting_utxo_spend, tolerate_high_network_feerates);
517+
self.select_confirmed_utxos_internal(
518+
&utxos,
519+
claim_id,
520+
force_conflicting_utxo_spend,
521+
tolerate_high_network_feerates,
522+
target_feerate_sat_per_1000_weight,
523+
preexisting_tx_weight,
524+
input_amount_sat,
525+
target_amount_sat,
526+
)
527+
};
528+
do_coin_selection(false, false)
529+
.or_else(|_| do_coin_selection(false, true))
530+
.or_else(|_| do_coin_selection(true, false))
531+
.or_else(|_| do_coin_selection(true, true))
532+
})
519533
}
520534

521535
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
@@ -630,8 +644,11 @@ where
630644
log_debug!(self.logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW",
631645
package_target_feerate_sat_per_1000_weight);
632646
let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
633-
claim_id, must_spend, &[], package_target_feerate_sat_per_1000_weight,
634-
)?;
647+
claim_id,
648+
must_spend,
649+
&[],
650+
package_target_feerate_sat_per_1000_weight,
651+
).await?;
635652

636653
let mut anchor_tx = Transaction {
637654
version: Version::TWO,
@@ -752,8 +769,11 @@ where
752769
let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value.to_sat()).sum::<u64>();
753770

754771
let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
755-
claim_id, must_spend, &htlc_tx.output, target_feerate_sat_per_1000_weight,
756-
)?;
772+
claim_id,
773+
must_spend,
774+
&htlc_tx.output,
775+
target_feerate_sat_per_1000_weight,
776+
).await?;
757777

758778
#[cfg(debug_assertions)]
759779
let total_satisfaction_weight = must_spend_satisfaction_weight +
@@ -828,11 +848,17 @@ where
828848
log_info!(self.logger, "Handling channel close bump (claim_id = {}, commitment_txid = {})",
829849
log_bytes!(claim_id.0), commitment_tx.compute_txid());
830850
if let Err(_) = self.handle_channel_close(
831-
*claim_id, *package_target_feerate_sat_per_1000_weight, commitment_tx,
832-
*commitment_tx_fee_satoshis, anchor_descriptor,
851+
*claim_id,
852+
*package_target_feerate_sat_per_1000_weight,
853+
commitment_tx,
854+
*commitment_tx_fee_satoshis,
855+
anchor_descriptor,
833856
).await {
834-
log_error!(self.logger, "Failed bumping commitment transaction fee for {}",
835-
commitment_tx.compute_txid());
857+
log_error!(
858+
self.logger,
859+
"Failed bumping commitment transaction fee for {}",
860+
commitment_tx.compute_txid()
861+
);
836862
}
837863
}
838864
BumpTransactionEvent::HTLCResolution {
@@ -841,10 +867,16 @@ where
841867
log_info!(self.logger, "Handling HTLC bump (claim_id = {}, htlcs_to_claim = {})",
842868
log_bytes!(claim_id.0), log_iter!(htlc_descriptors.iter().map(|d| d.outpoint())));
843869
if let Err(_) = self.handle_htlc_resolution(
844-
*claim_id, *target_feerate_sat_per_1000_weight, htlc_descriptors, *tx_lock_time,
870+
*claim_id,
871+
*target_feerate_sat_per_1000_weight,
872+
htlc_descriptors,
873+
*tx_lock_time,
845874
).await {
846-
log_error!(self.logger, "Failed bumping HTLC transaction fee for commitment {}",
847-
htlc_descriptors[0].commitment_txid);
875+
log_error!(
876+
self.logger,
877+
"Failed bumping HTLC transaction fee for commitment {}",
878+
htlc_descriptors[0].commitment_txid
879+
);
848880
}
849881
}
850882
}
@@ -871,20 +903,19 @@ mod tests {
871903
expected_selects: Mutex<Vec<(u64, u64, u32, CoinSelection)>>,
872904
}
873905
impl CoinSelectionSource for TestCoinSelectionSource {
874-
fn select_confirmed_utxos(
875-
&self,
876-
_claim_id: ClaimId,
877-
must_spend: Vec<Input>,
878-
_must_pay_to: &[TxOut],
879-
target_feerate_sat_per_1000_weight: u32
880-
) -> Result<CoinSelection, ()> {
906+
fn select_confirmed_utxos<'a>(
907+
&'a self, _claim_id: ClaimId, must_spend: Vec<Input>, _must_pay_to: &'a [TxOut],
908+
target_feerate_sat_per_1000_weight: u32,
909+
) -> AsyncResult<'a, CoinSelection> {
881910
let mut expected_selects = self.expected_selects.lock().unwrap();
882911
let (weight, value, feerate, res) = expected_selects.remove(0);
883912
assert_eq!(must_spend.len(), 1);
884913
assert_eq!(must_spend[0].satisfaction_weight, weight);
885914
assert_eq!(must_spend[0].previous_utxo.value.to_sat(), value);
886915
assert_eq!(target_feerate_sat_per_1000_weight, feerate);
887-
Ok(res)
916+
Box::pin(async move {
917+
Ok(res)
918+
})
888919
}
889920
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
890921
let mut tx = psbt.unsigned_tx;

lightning/src/util/test_utils.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ use crate::routing::router::{
4545
};
4646
use crate::routing::scoring::{ChannelUsage, ScoreLookUp, ScoreUpdate};
4747
use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult};
48-
use crate::sign;
4948
use crate::sign::ChannelSigner;
49+
use crate::sign::{self, AsyncResult};
5050
use crate::sync::RwLock;
5151
use crate::types::features::{ChannelFeatures, InitFeatures, NodeFeatures};
5252
use crate::util::config::UserConfig;
@@ -85,7 +85,6 @@ use crate::io;
8585
use crate::prelude::*;
8686
use crate::sign::{EntropySource, NodeSigner, RandomBytes, Recipient, SignerProvider};
8787
use crate::sync::{Arc, Mutex};
88-
use core::cell::RefCell;
8988
use core::mem;
9089
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
9190
use core::time::Duration;
@@ -1872,36 +1871,36 @@ impl Drop for TestScorer {
18721871

18731872
pub struct TestWalletSource {
18741873
secret_key: SecretKey,
1875-
utxos: RefCell<Vec<Utxo>>,
1874+
utxos: Mutex<Vec<Utxo>>,
18761875
secp: Secp256k1<bitcoin::secp256k1::All>,
18771876
}
18781877

18791878
impl TestWalletSource {
18801879
pub fn new(secret_key: SecretKey) -> Self {
1881-
Self { secret_key, utxos: RefCell::new(Vec::new()), secp: Secp256k1::new() }
1880+
Self { secret_key, utxos: Mutex::new(Vec::new()), secp: Secp256k1::new() }
18821881
}
18831882

18841883
pub fn add_utxo(&self, outpoint: bitcoin::OutPoint, value: Amount) -> TxOut {
18851884
let public_key = bitcoin::PublicKey::new(self.secret_key.public_key(&self.secp));
18861885
let utxo = Utxo::new_p2pkh(outpoint, value, &public_key.pubkey_hash());
1887-
self.utxos.borrow_mut().push(utxo.clone());
1886+
self.utxos.lock().unwrap().push(utxo.clone());
18881887
utxo.output
18891888
}
18901889

18911890
pub fn add_custom_utxo(&self, utxo: Utxo) -> TxOut {
18921891
let output = utxo.output.clone();
1893-
self.utxos.borrow_mut().push(utxo);
1892+
self.utxos.lock().unwrap().push(utxo);
18941893
output
18951894
}
18961895

18971896
pub fn remove_utxo(&self, outpoint: bitcoin::OutPoint) {
1898-
self.utxos.borrow_mut().retain(|utxo| utxo.outpoint != outpoint);
1897+
self.utxos.lock().unwrap().retain(|utxo| utxo.outpoint != outpoint);
18991898
}
19001899
}
19011900

19021901
impl WalletSource for TestWalletSource {
1903-
fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()> {
1904-
Ok(self.utxos.borrow().clone())
1902+
fn list_confirmed_utxos<'a>(&'a self) -> AsyncResult<'a, Vec<Utxo>> {
1903+
Box::pin(async move { Ok(self.utxos.lock().unwrap().clone()) })
19051904
}
19061905

19071906
fn get_change_script(&self) -> Result<ScriptBuf, ()> {
@@ -1911,7 +1910,7 @@ impl WalletSource for TestWalletSource {
19111910

19121911
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
19131912
let mut tx = psbt.extract_tx_unchecked_fee_rate();
1914-
let utxos = self.utxos.borrow();
1913+
let utxos = self.utxos.lock().unwrap();
19151914
for i in 0..tx.input.len() {
19161915
if let Some(utxo) =
19171916
utxos.iter().find(|utxo| utxo.outpoint == tx.input[i].previous_output)

0 commit comments

Comments
 (0)