Skip to content

Commit 0820322

Browse files
committed
Convert CoinSelectionSource and WalletSource traits to async
This commit converts the `CoinSelectionSource` and `WalletSource` traits to `async` and bubbles up the `async` keyword where needed. To preserve usability in a synchronous context, sync versions of these traits are still provided and can be used via the `WalletSync` and `BumpTransactionEventHandlerSync` wrappers. Existing synchronous tests remain unchanged and also use these wrappers. Additionally, the `MaybeSync` and `MaybeSend` marker traits are introduced to support `no_std` compilation. Due to limitations in Rust 1.63's type inference, these marker traits are used more frequently than might be necessary in later versions.
1 parent 048230a commit 0820322

File tree

8 files changed

+369
-105
lines changed

8 files changed

+369
-105
lines changed

lightning/src/events/bump_transaction.rs renamed to lightning/src/events/bump_transaction/mod.rs

Lines changed: 117 additions & 86 deletions
Large diffs are not rendered by default.
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! This module provides synchronous wrappers around [`BumpTransactionEventHandler`] and related types.
11+
12+
use core::future::Future;
13+
use core::ops::Deref;
14+
use core::task;
15+
16+
use crate::chain::chaininterface::BroadcasterInterface;
17+
use crate::chain::ClaimId;
18+
use crate::prelude::*;
19+
use crate::sign::SignerProvider;
20+
use crate::sync::Arc;
21+
use crate::util::async_poll::{dummy_waker, AsyncResult, MaybeSend, MaybeSync};
22+
use crate::util::logger::Logger;
23+
24+
use bitcoin::{Psbt, ScriptBuf, Transaction, TxOut};
25+
26+
use super::BumpTransactionEvent;
27+
use super::{
28+
BumpTransactionEventHandler, CoinSelection, CoinSelectionSource, Input, Utxo, Wallet,
29+
WalletSource,
30+
};
31+
32+
/// A synchronous version of the [`WalletSource`] trait.
33+
pub trait WalletSourceSync {
34+
/// A synchronous version of [`WalletSource::list_confirmed_utxos`].
35+
fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()>;
36+
/// A synchronous version of [`WalletSource::get_change_script`].
37+
fn get_change_script(&self) -> Result<ScriptBuf, ()>;
38+
/// A Synchronous version of [`WalletSource::sign_psbt`].
39+
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
40+
}
41+
42+
pub(crate) struct WalletSourceSyncWrapper<T: Deref>(T)
43+
where
44+
T::Target: WalletSourceSync;
45+
46+
impl<T: Deref> WalletSource for WalletSourceSyncWrapper<T>
47+
where
48+
T::Target: WalletSourceSync,
49+
{
50+
fn list_confirmed_utxos<'a>(&'a self) -> AsyncResult<'a, Vec<Utxo>> {
51+
let utxos = self.0.list_confirmed_utxos();
52+
Box::pin(async move { utxos })
53+
}
54+
55+
fn get_change_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf> {
56+
let script = self.0.get_change_script();
57+
Box::pin(async move { script })
58+
}
59+
60+
fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction> {
61+
let signed_psbt = self.0.sign_psbt(psbt);
62+
Box::pin(async move { signed_psbt })
63+
}
64+
}
65+
66+
/// A synchronous wrapper around [`Wallet`] to be used in contexts where async is not available.
67+
pub struct WalletSync<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend>
68+
where
69+
W::Target: WalletSourceSync + MaybeSend,
70+
L::Target: Logger + MaybeSend,
71+
{
72+
wallet: Wallet<Arc<WalletSourceSyncWrapper<W>>, L>,
73+
}
74+
75+
impl<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend> WalletSync<W, L>
76+
where
77+
W::Target: WalletSourceSync + MaybeSend,
78+
L::Target: Logger + MaybeSend,
79+
{
80+
/// Constructs a new [`WalletSync`] instance.
81+
pub fn new(source: W, logger: L) -> Self {
82+
Self { wallet: Wallet::new(Arc::new(WalletSourceSyncWrapper(source)), logger) }
83+
}
84+
}
85+
86+
impl<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend> CoinSelectionSourceSync
87+
for WalletSync<W, L>
88+
where
89+
W::Target: WalletSourceSync + MaybeSend + MaybeSync,
90+
L::Target: Logger + MaybeSend + MaybeSync,
91+
{
92+
fn select_confirmed_utxos(
93+
&self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &[TxOut],
94+
target_feerate_sat_per_1000_weight: u32,
95+
) -> Result<CoinSelection, ()> {
96+
let mut fut = self.wallet.select_confirmed_utxos(
97+
claim_id,
98+
must_spend,
99+
must_pay_to,
100+
target_feerate_sat_per_1000_weight,
101+
);
102+
let mut waker = dummy_waker();
103+
let mut ctx = task::Context::from_waker(&mut waker);
104+
match fut.as_mut().poll(&mut ctx) {
105+
task::Poll::Ready(result) => result,
106+
task::Poll::Pending => {
107+
unreachable!(
108+
"Wallet::select_confirmed_utxos should not be pending in a sync context"
109+
);
110+
},
111+
}
112+
}
113+
114+
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
115+
let mut fut = self.wallet.sign_psbt(psbt);
116+
let mut waker = dummy_waker();
117+
let mut ctx = task::Context::from_waker(&mut waker);
118+
match fut.as_mut().poll(&mut ctx) {
119+
task::Poll::Ready(result) => result,
120+
task::Poll::Pending => {
121+
unreachable!("Wallet::sign_psbt should not be pending in a sync context");
122+
},
123+
}
124+
}
125+
}
126+
127+
/// A synchronous version of the [`CoinSelectionSource`] trait.
128+
pub trait CoinSelectionSourceSync {
129+
/// A synchronous version of [`CoinSelectionSource::select_confirmed_utxos`].
130+
fn select_confirmed_utxos(
131+
&self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &[TxOut],
132+
target_feerate_sat_per_1000_weight: u32,
133+
) -> Result<CoinSelection, ()>;
134+
135+
/// A synchronous version of [`CoinSelectionSource::sign_psbt`].
136+
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
137+
}
138+
139+
struct CoinSelectionSourceSyncWrapper<T: Deref>(T)
140+
where
141+
T::Target: CoinSelectionSourceSync;
142+
143+
impl<T: Deref> CoinSelectionSource for CoinSelectionSourceSyncWrapper<T>
144+
where
145+
T::Target: CoinSelectionSourceSync,
146+
{
147+
fn select_confirmed_utxos<'a>(
148+
&'a self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
149+
target_feerate_sat_per_1000_weight: u32,
150+
) -> AsyncResult<'a, CoinSelection> {
151+
let coins = self.0.select_confirmed_utxos(
152+
claim_id,
153+
must_spend,
154+
must_pay_to,
155+
target_feerate_sat_per_1000_weight,
156+
);
157+
Box::pin(async move { coins })
158+
}
159+
160+
fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction> {
161+
let psbt = self.0.sign_psbt(psbt);
162+
Box::pin(async move { psbt })
163+
}
164+
}
165+
166+
/// A synchronous wrapper around [`BumpTransactionEventHandler`] to be used in contexts where async is not available.
167+
pub struct BumpTransactionEventHandlerSync<B: Deref, C: Deref, SP: Deref, L: Deref>
168+
where
169+
B::Target: BroadcasterInterface,
170+
C::Target: CoinSelectionSourceSync,
171+
SP::Target: SignerProvider,
172+
L::Target: Logger,
173+
{
174+
bump_transaction_event_handler:
175+
Arc<BumpTransactionEventHandler<B, Arc<CoinSelectionSourceSyncWrapper<C>>, SP, L>>,
176+
}
177+
178+
impl<B: Deref, C: Deref, SP: Deref, L: Deref> BumpTransactionEventHandlerSync<B, C, SP, L>
179+
where
180+
B::Target: BroadcasterInterface,
181+
C::Target: CoinSelectionSourceSync,
182+
SP::Target: SignerProvider,
183+
L::Target: Logger,
184+
{
185+
/// Constructs a new instance of [`BumpTransactionEventHandlerSync`].
186+
pub fn new(broadcaster: B, utxo_source: C, signer_provider: SP, logger: L) -> Self {
187+
let bump_transaction_event_handler = Arc::new(BumpTransactionEventHandler::new(
188+
broadcaster,
189+
Arc::new(CoinSelectionSourceSyncWrapper(utxo_source)),
190+
signer_provider,
191+
logger,
192+
));
193+
Self { bump_transaction_event_handler }
194+
}
195+
196+
/// Handles all variants of [`BumpTransactionEvent`].
197+
pub fn handle_event(&self, event: &BumpTransactionEvent) {
198+
let mut fut = Box::pin(self.bump_transaction_event_handler.handle_event(event));
199+
let mut waker = dummy_waker();
200+
let mut ctx = task::Context::from_waker(&mut waker);
201+
match fut.as_mut().poll(&mut ctx) {
202+
task::Poll::Ready(result) => result,
203+
task::Poll::Pending => {
204+
// In a sync context, we can't wait for the future to complete.
205+
unreachable!("BumpTransactionEventHandlerSync::handle_event should not be pending in a sync context");
206+
},
207+
}
208+
}
209+
}

lightning/src/ln/async_signer_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use bitcoin::transaction::Version;
2020

2121
use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
2222
use crate::chain::ChannelMonitorUpdateStatus;
23-
use crate::events::bump_transaction::WalletSource;
23+
use crate::events::bump_transaction::sync::WalletSourceSync;
2424
use crate::events::{ClosureReason, Event};
2525
use crate::ln::chan_utils::ClosingTransaction;
2626
use crate::ln::channel::DISCONNECT_PEER_AWAITING_RESPONSE_TICKS;

lightning/src/ln/functional_test_utils.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch
1616
use crate::chain::channelmonitor::ChannelMonitor;
1717
use crate::chain::transaction::OutPoint;
1818
use crate::events::{ClaimedHTLC, ClosureReason, Event, HTLCHandlingFailureType, PaidBolt12Invoice, PathFailure, PaymentFailureReason, PaymentPurpose};
19-
use crate::events::bump_transaction::{BumpTransactionEvent, BumpTransactionEventHandler, Wallet, WalletSource};
19+
use crate::events::bump_transaction::{BumpTransactionEvent};
20+
use crate::events::bump_transaction::sync::{BumpTransactionEventHandlerSync, WalletSourceSync, WalletSync};
2021
use crate::ln::types::ChannelId;
2122
use crate::types::features::ChannelTypeFeatures;
2223
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
@@ -472,9 +473,9 @@ pub struct Node<'chan_man, 'node_cfg: 'chan_man, 'chan_mon_cfg: 'node_cfg> {
472473
pub connect_style: Rc<RefCell<ConnectStyle>>,
473474
pub override_init_features: Rc<RefCell<Option<InitFeatures>>>,
474475
pub wallet_source: Arc<test_utils::TestWalletSource>,
475-
pub bump_tx_handler: BumpTransactionEventHandler<
476+
pub bump_tx_handler: BumpTransactionEventHandlerSync<
476477
&'chan_mon_cfg test_utils::TestBroadcaster,
477-
Arc<Wallet<Arc<test_utils::TestWalletSource>, &'chan_mon_cfg test_utils::TestLogger>>,
478+
Arc<WalletSync<Arc<test_utils::TestWalletSource>, &'chan_mon_cfg test_utils::TestLogger>>,
478479
&'chan_mon_cfg test_utils::TestKeysInterface,
479480
&'chan_mon_cfg test_utils::TestLogger,
480481
>,
@@ -3424,6 +3425,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
34243425
);
34253426
let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
34263427
let wallet_source = Arc::new(test_utils::TestWalletSource::new(SecretKey::from_slice(&[i as u8 + 1; 32]).unwrap()));
3428+
let wallet = Arc::new(WalletSync::new(wallet_source.clone(), cfgs[i].logger));
34273429
nodes.push(Node{
34283430
chain_source: cfgs[i].chain_source, tx_broadcaster: cfgs[i].tx_broadcaster,
34293431
fee_estimator: cfgs[i].fee_estimator, router: &cfgs[i].router,
@@ -3435,9 +3437,9 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
34353437
blocks: Arc::clone(&cfgs[i].tx_broadcaster.blocks),
34363438
connect_style: Rc::clone(&connect_style),
34373439
override_init_features: Rc::clone(&cfgs[i].override_init_features),
3438-
wallet_source: Arc::clone(&wallet_source),
3439-
bump_tx_handler: BumpTransactionEventHandler::new(
3440-
cfgs[i].tx_broadcaster, Arc::new(Wallet::new(Arc::clone(&wallet_source), cfgs[i].logger)),
3440+
wallet_source: wallet_source.clone(),
3441+
bump_tx_handler: BumpTransactionEventHandlerSync::new(
3442+
cfgs[i].tx_broadcaster, wallet,
34413443
&cfgs[i].keys_manager, cfgs[i].logger,
34423444
),
34433445
})

lightning/src/ln/functional_tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ use crate::chain::channelmonitor::{
2020
};
2121
use crate::chain::transaction::OutPoint;
2222
use crate::chain::{ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
23-
use crate::events::bump_transaction::WalletSource;
2423
use crate::events::{
2524
ClosureReason, Event, FundingInfo, HTLCHandlingFailureType, PathFailure, PaymentFailureReason,
2625
PaymentPurpose,
@@ -1474,6 +1473,8 @@ pub fn claim_htlc_outputs() {
14741473
// This is a regression test for https://github.com/lightningdevkit/rust-lightning/issues/3537.
14751474
#[xtest(feature = "_externalize_tests")]
14761475
pub fn test_multiple_package_conflicts() {
1476+
use crate::events::bump_transaction::sync::WalletSourceSync;
1477+
14771478
let chanmon_cfgs = create_chanmon_cfgs(3);
14781479
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
14791480
let mut user_cfg = test_default_channel_config();

lightning/src/ln/monitor_tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111

1212
//! Further functional tests which test blockchain reorganizations.
1313
14+
use crate::events::bump_transaction::sync::WalletSourceSync;
1415
use crate::sign::{ecdsa::EcdsaChannelSigner, OutputSpender, SignerProvider, SpendableOutputDescriptor};
1516
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, ARCHIVAL_DELAY_BLOCKS,LATENCY_GRACE_PERIOD_BLOCKS, COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE, Balance, BalanceSource, ChannelMonitorUpdateStep};
1617
use crate::chain::transaction::OutPoint;
1718
use crate::chain::chaininterface::{ConfirmationTarget, LowerBoundedFeeEstimator, compute_feerate_sat_per_1000_weight};
18-
use crate::events::bump_transaction::{BumpTransactionEvent, WalletSource};
19+
use crate::events::bump_transaction::{BumpTransactionEvent};
1920
use crate::events::{Event, ClosureReason, HTLCHandlingFailureType};
2021
use crate::ln::channel;
2122
use crate::ln::types::ChannelId;

lightning/src/util/async_poll.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,24 @@ pub(crate) fn dummy_waker() -> Waker {
9797
}
9898

9999
/// A type alias for a future that returns a result of type T.
100+
#[cfg(feature = "std")]
100101
pub type AsyncResult<'a, T> = Pin<Box<dyn Future<Output = Result<T, ()>> + 'a + Send>>;
102+
#[cfg(not(feature = "std"))]
103+
pub type AsyncResult<'a, T> = Pin<Box<dyn Future<Output = Result<T, ()>> + 'a>>;
104+
105+
// Marker trait to optionally implement `Sync` under std.
106+
#[cfg(feature = "std")]
107+
pub use core::marker::Sync as MaybeSync;
108+
109+
#[cfg(not(feature = "std"))]
110+
pub trait MaybeSync {}
111+
#[cfg(not(feature = "std"))]
112+
impl<T> MaybeSync for T where T: ?Sized {}
113+
114+
// Marker trait to optionally implement `Send` under std.
115+
#[cfg(feature = "std")]
116+
pub use core::marker::Send as MaybeSend;
117+
#[cfg(not(feature = "std"))]
118+
pub trait MaybeSend {}
119+
#[cfg(not(feature = "std"))]
120+
impl<T> MaybeSend for T where T: ?Sized {}

lightning/src/util/test_utils.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ use crate::chain::channelmonitor::{
2121
};
2222
use crate::chain::transaction::OutPoint;
2323
use crate::chain::WatchedOutput;
24-
use crate::events::bump_transaction::{Utxo, WalletSource};
24+
use crate::events::bump_transaction::sync::WalletSourceSync;
25+
use crate::events::bump_transaction::Utxo;
2526
#[cfg(any(test, feature = "_externalize_tests"))]
2627
use crate::ln::chan_utils::CommitmentTransaction;
2728
use crate::ln::channel_state::ChannelDetails;
@@ -85,7 +86,6 @@ use crate::io;
8586
use crate::prelude::*;
8687
use crate::sign::{EntropySource, NodeSigner, RandomBytes, Recipient, SignerProvider};
8788
use crate::sync::{Arc, Mutex};
88-
use core::cell::RefCell;
8989
use core::mem;
9090
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
9191
use core::time::Duration;
@@ -1874,36 +1874,36 @@ impl Drop for TestScorer {
18741874

18751875
pub struct TestWalletSource {
18761876
secret_key: SecretKey,
1877-
utxos: RefCell<Vec<Utxo>>,
1877+
utxos: Mutex<Vec<Utxo>>,
18781878
secp: Secp256k1<bitcoin::secp256k1::All>,
18791879
}
18801880

18811881
impl TestWalletSource {
18821882
pub fn new(secret_key: SecretKey) -> Self {
1883-
Self { secret_key, utxos: RefCell::new(Vec::new()), secp: Secp256k1::new() }
1883+
Self { secret_key, utxos: Mutex::new(Vec::new()), secp: Secp256k1::new() }
18841884
}
18851885

18861886
pub fn add_utxo(&self, outpoint: bitcoin::OutPoint, value: Amount) -> TxOut {
18871887
let public_key = bitcoin::PublicKey::new(self.secret_key.public_key(&self.secp));
18881888
let utxo = Utxo::new_p2pkh(outpoint, value, &public_key.pubkey_hash());
1889-
self.utxos.borrow_mut().push(utxo.clone());
1889+
self.utxos.lock().unwrap().push(utxo.clone());
18901890
utxo.output
18911891
}
18921892

18931893
pub fn add_custom_utxo(&self, utxo: Utxo) -> TxOut {
18941894
let output = utxo.output.clone();
1895-
self.utxos.borrow_mut().push(utxo);
1895+
self.utxos.lock().unwrap().push(utxo);
18961896
output
18971897
}
18981898

18991899
pub fn remove_utxo(&self, outpoint: bitcoin::OutPoint) {
1900-
self.utxos.borrow_mut().retain(|utxo| utxo.outpoint != outpoint);
1900+
self.utxos.lock().unwrap().retain(|utxo| utxo.outpoint != outpoint);
19011901
}
19021902
}
19031903

1904-
impl WalletSource for TestWalletSource {
1904+
impl WalletSourceSync for TestWalletSource {
19051905
fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()> {
1906-
Ok(self.utxos.borrow().clone())
1906+
Ok(self.utxos.lock().unwrap().clone())
19071907
}
19081908

19091909
fn get_change_script(&self) -> Result<ScriptBuf, ()> {
@@ -1913,7 +1913,7 @@ impl WalletSource for TestWalletSource {
19131913

19141914
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
19151915
let mut tx = psbt.extract_tx_unchecked_fee_rate();
1916-
let utxos = self.utxos.borrow();
1916+
let utxos = self.utxos.lock().unwrap();
19171917
for i in 0..tx.input.len() {
19181918
if let Some(utxo) =
19191919
utxos.iter().find(|utxo| utxo.outpoint == tx.input[i].previous_output)

0 commit comments

Comments
 (0)