Skip to content

Commit 4bfc36a

Browse files
committed
Async wallet bump
1 parent f64039a commit 4bfc36a

File tree

2 files changed

+94
-64
lines changed

2 files changed

+94
-64
lines changed

lightning/src/events/bump_transaction.rs

+85-54
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::ln::chan_utils::{
2626
};
2727
use crate::prelude::*;
2828
use crate::sign::{
29-
ChannelDerivationParameters, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT
29+
AsyncResult, ChannelDerivationParameters, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT
3030
};
3131
use crate::sign::ecdsa::EcdsaChannelSigner;
3232
use crate::sync::Mutex;
@@ -344,10 +344,10 @@ pub trait CoinSelectionSource {
344344
/// other claims, implementations must be willing to double spend their UTXOs. The choice of
345345
/// which UTXOs to double spend is left to the implementation, but it must strive to keep the
346346
/// set of other claims being double spent to a minimum.
347-
fn select_confirmed_utxos(
348-
&self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &[TxOut],
347+
fn select_confirmed_utxos<'a>(
348+
&'a self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &'a[TxOut],
349349
target_feerate_sat_per_1000_weight: u32,
350-
) -> Result<CoinSelection, ()>;
350+
) -> AsyncResult<'a, CoinSelection>;
351351
/// Signs and provides the full witness for all inputs within the transaction known to the
352352
/// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
353353
///
@@ -360,7 +360,7 @@ pub trait CoinSelectionSource {
360360
/// provide a default implementation to [`CoinSelectionSource`].
361361
pub trait WalletSource {
362362
/// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
363-
fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()>;
363+
fn list_confirmed_utxos<'a>(&'a self) -> AsyncResult<'a, Vec<Utxo>>;
364364
/// Returns a script to use for change above dust resulting from a successful coin selection
365365
/// attempt.
366366
fn get_change_script(&self) -> Result<ScriptBuf, ()>;
@@ -481,40 +481,54 @@ where
481481
}
482482
}
483483

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

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

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

753770
let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
754-
claim_id, must_spend, &htlc_tx.output, target_feerate_sat_per_1000_weight,
755-
)?;
771+
claim_id,
772+
must_spend,
773+
&htlc_tx.output,
774+
target_feerate_sat_per_1000_weight,
775+
).await?;
756776

757777
#[cfg(debug_assertions)]
758778
let total_satisfaction_weight = must_spend_satisfaction_weight +
@@ -827,11 +847,17 @@ where
827847
log_info!(self.logger, "Handling channel close bump (claim_id = {}, commitment_txid = {})",
828848
log_bytes!(claim_id.0), commitment_tx.compute_txid());
829849
if let Err(_) = self.handle_channel_close(
830-
*claim_id, *package_target_feerate_sat_per_1000_weight, commitment_tx,
831-
*commitment_tx_fee_satoshis, anchor_descriptor,
850+
*claim_id,
851+
*package_target_feerate_sat_per_1000_weight,
852+
commitment_tx,
853+
*commitment_tx_fee_satoshis,
854+
anchor_descriptor,
832855
).await {
833-
log_error!(self.logger, "Failed bumping commitment transaction fee for {}",
834-
commitment_tx.compute_txid());
856+
log_error!(
857+
self.logger,
858+
"Failed bumping commitment transaction fee for {}",
859+
commitment_tx.compute_txid()
860+
);
835861
}
836862
}
837863
BumpTransactionEvent::HTLCResolution {
@@ -840,10 +866,16 @@ where
840866
log_info!(self.logger, "Handling HTLC bump (claim_id = {}, htlcs_to_claim = {})",
841867
log_bytes!(claim_id.0), log_iter!(htlc_descriptors.iter().map(|d| d.outpoint())));
842868
if let Err(_) = self.handle_htlc_resolution(
843-
*claim_id, *target_feerate_sat_per_1000_weight, htlc_descriptors, *tx_lock_time,
869+
*claim_id,
870+
*target_feerate_sat_per_1000_weight,
871+
htlc_descriptors,
872+
*tx_lock_time,
844873
).await {
845-
log_error!(self.logger, "Failed bumping HTLC transaction fee for commitment {}",
846-
htlc_descriptors[0].commitment_txid);
874+
log_error!(
875+
self.logger,
876+
"Failed bumping HTLC transaction fee for commitment {}",
877+
htlc_descriptors[0].commitment_txid
878+
);
847879
}
848880
}
849881
}
@@ -870,20 +902,19 @@ mod tests {
870902
expected_selects: Mutex<Vec<(u64, u64, u32, CoinSelection)>>,
871903
}
872904
impl CoinSelectionSource for TestCoinSelectionSource {
873-
fn select_confirmed_utxos(
874-
&self,
875-
_claim_id: ClaimId,
876-
must_spend: Vec<Input>,
877-
_must_pay_to: &[TxOut],
878-
target_feerate_sat_per_1000_weight: u32
879-
) -> Result<CoinSelection, ()> {
905+
fn select_confirmed_utxos<'a>(
906+
&'a self, _claim_id: ClaimId, must_spend: Vec<Input>, _must_pay_to: &'a [TxOut],
907+
target_feerate_sat_per_1000_weight: u32,
908+
) -> AsyncResult<'a, CoinSelection> {
880909
let mut expected_selects = self.expected_selects.lock().unwrap();
881910
let (weight, value, feerate, res) = expected_selects.remove(0);
882911
assert_eq!(must_spend.len(), 1);
883912
assert_eq!(must_spend[0].satisfaction_weight, weight);
884913
assert_eq!(must_spend[0].previous_utxo.value.to_sat(), value);
885914
assert_eq!(target_feerate_sat_per_1000_weight, feerate);
886-
Ok(res)
915+
Box::pin(async move {
916+
Ok(res)
917+
})
887918
}
888919
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
889920
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;
@@ -1871,36 +1870,36 @@ impl Drop for TestScorer {
18711870

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

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

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

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

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

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

19061905
fn get_change_script(&self) -> Result<ScriptBuf, ()> {
@@ -1910,7 +1909,7 @@ impl WalletSource for TestWalletSource {
19101909

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

0 commit comments

Comments
 (0)