Drop required `Box`ing of `lightning` trait `Future`s
What changed, and why it matters
This commit is a routine code cleanup in the Lightning Dev Kit Rust library. It takes advantage of a newer Rust language feature (available since Rust 1.75) to remove unnecessary 'Box' heap allocations around futures returned from certain trait methods. There is no indication this change fixes a security bug or introduces a vulnerability; it is a refactoring to simplify the code and reduce allocations.
No security action required. Treat as a normal API/performance refactoring. Downstream users implementing these traits will need to update their code to return impl Future instead of AsyncResult, but this is a compile-time breaking change, not a security issue.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit replaces the Pin
Changed components
lightning/src/events/bump_transaction/mod.rslightning/src/events/bump_transaction/sync.rslightning/src/sign/mod.rslightning/src/util/async_poll.rsInspect captured patch +54 / −41
diff --git a/lightning/src/events/bump_transaction/mod.rs b/lightning/src/events/bump_transaction/mod.rs
index 3d9beb8..e141d9b 100644
--- a/lightning/src/events/bump_transaction/mod.rs
+++ b/lightning/src/events/bump_transaction/mod.rs
@@ -14,6 +14,7 @@
pub mod sync;
use alloc::collections::BTreeMap;
+use core::future::Future;
use core::ops::Deref;
use crate::chain::chaininterface::{
@@ -36,7 +37,7 @@ use crate::sign::{
ChannelDerivationParameters, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT,
};
use crate::sync::Mutex;
-use crate::util::async_poll::{AsyncResult, MaybeSend, MaybeSync};
+use crate::util::async_poll::{MaybeSend, MaybeSync};
use crate::util::logger::Logger;
use bitcoin::amount::Amount;
@@ -394,13 +395,15 @@ pub trait CoinSelectionSource {
fn select_confirmed_utxos<'a>(
&'a self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
- ) -> AsyncResult<'a, CoinSelection, ()>;
+ ) -> impl Future<Output = Result<CoinSelection, ()>> + MaybeSend + 'a;
/// Signs and provides the full witness for all inputs within the transaction known to the
/// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
///
/// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
/// unsigned transaction and then sign it with your wallet.
- fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction, ()>;
+ fn sign_psbt<'a>(
+ &'a self, psbt: Psbt,
+ ) -> impl Future<Output = Result<Transaction, ()>> + MaybeSend + 'a;
}
/// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
@@ -412,17 +415,23 @@ pub trait CoinSelectionSource {
// Note that updates to documentation on this trait should be copied to the synchronous version.
pub trait WalletSource {
/// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
- fn list_confirmed_utxos<'a>(&'a self) -> AsyncResult<'a, Vec<Utxo>, ()>;
+ fn list_confirmed_utxos<'a>(
+ &'a self,
+ ) -> impl Future<Output = Result<Vec<Utxo>, ()>> + MaybeSend + 'a;
/// Returns a script to use for change above dust resulting from a successful coin selection
/// attempt.
- fn get_change_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf, ()>;
+ fn get_change_script<'a>(
+ &'a self,
+ ) -> impl Future<Output = Result<ScriptBuf, ()>> + MaybeSend + 'a;
/// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
/// the transaction known to the wallet (i.e., any provided via
/// [`WalletSource::list_confirmed_utxos`]).
///
/// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
/// unsigned transaction and then sign it with your wallet.
- fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction, ()>;
+ fn sign_psbt<'a>(
+ &'a self, psbt: Psbt,
+ ) -> impl Future<Output = Result<Transaction, ()>> + MaybeSend + 'a;
}
/// A wrapper over [`WalletSource`] that implements [`CoinSelectionSource`] by preferring UTXOs
@@ -617,8 +626,8 @@ where
fn select_confirmed_utxos<'a>(
&'a self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
- ) -> AsyncResult<'a, CoinSelection, ()> {
- Box::pin(async move {
+ ) -> impl Future<Output = Result<CoinSelection, ()>> + MaybeSend + 'a {
+ async move {
let utxos = self.source.list_confirmed_utxos().await?;
// TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0.
let total_output_size: u64 = must_pay_to
@@ -665,10 +674,12 @@ where
}
}
Err(())
- })
+ }
}
- fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction, ()> {
+ fn sign_psbt<'a>(
+ &'a self, psbt: Psbt,
+ ) -> impl Future<Output = Result<Transaction, ()>> + MaybeSend + 'a {
self.source.sign_psbt(psbt)
}
}
diff --git a/lightning/src/events/bump_transaction/sync.rs b/lightning/src/events/bump_transaction/sync.rs
index cbc686e..1328c2c 100644
--- a/lightning/src/events/bump_transaction/sync.rs
+++ b/lightning/src/events/bump_transaction/sync.rs
@@ -18,7 +18,7 @@ use crate::chain::chaininterface::BroadcasterInterface;
use crate::chain::ClaimId;
use crate::prelude::*;
use crate::sign::SignerProvider;
-use crate::util::async_poll::{dummy_waker, AsyncResult, MaybeSend, MaybeSync};
+use crate::util::async_poll::{dummy_waker, MaybeSend, MaybeSync};
use crate::util::logger::Logger;
use bitcoin::{Psbt, ScriptBuf, Transaction, TxOut};
@@ -72,19 +72,25 @@ impl<T: Deref> WalletSource for WalletSourceSyncWrapper<T>
where
T::Target: WalletSourceSync,
{
- fn list_confirmed_utxos<'a>(&'a self) -> AsyncResult<'a, Vec<Utxo>, ()> {
+ fn list_confirmed_utxos<'a>(
+ &'a self,
+ ) -> impl Future<Output = Result<Vec<Utxo>, ()>> + MaybeSend + 'a {
let utxos = self.0.list_confirmed_utxos();
- Box::pin(async move { utxos })
+ async move { utxos }
}
- fn get_change_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf, ()> {
+ fn get_change_script<'a>(
+ &'a self,
+ ) -> impl Future<Output = Result<ScriptBuf, ()>> + MaybeSend + 'a {
let script = self.0.get_change_script();
- Box::pin(async move { script })
+ async move { script }
}
- fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction, ()> {
+ fn sign_psbt<'a>(
+ &'a self, psbt: Psbt,
+ ) -> impl Future<Output = Result<Transaction, ()>> + MaybeSend + 'a {
let signed_psbt = self.0.sign_psbt(psbt);
- Box::pin(async move { signed_psbt })
+ async move { signed_psbt }
}
}
@@ -123,7 +129,7 @@ where
&self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &[TxOut],
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
) -> Result<CoinSelection, ()> {
- let mut fut = self.wallet.select_confirmed_utxos(
+ let fut = self.wallet.select_confirmed_utxos(
claim_id,
must_spend,
must_pay_to,
@@ -132,7 +138,7 @@ where
);
let mut waker = dummy_waker();
let mut ctx = task::Context::from_waker(&mut waker);
- match fut.as_mut().poll(&mut ctx) {
+ match pin!(fut).poll(&mut ctx) {
task::Poll::Ready(result) => result,
task::Poll::Pending => {
unreachable!(
@@ -143,10 +149,10 @@ where
}
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
- let mut fut = self.wallet.sign_psbt(psbt);
+ let fut = self.wallet.sign_psbt(psbt);
let mut waker = dummy_waker();
let mut ctx = task::Context::from_waker(&mut waker);
- match fut.as_mut().poll(&mut ctx) {
+ match pin!(fut).poll(&mut ctx) {
task::Poll::Ready(result) => result,
task::Poll::Pending => {
unreachable!("Wallet::sign_psbt should not be pending in a sync context");
@@ -234,7 +240,7 @@ where
fn select_confirmed_utxos<'a>(
&'a self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
- ) -> AsyncResult<'a, CoinSelection, ()> {
+ ) -> impl Future<Output = Result<CoinSelection, ()>> + MaybeSend + 'a {
let coins = self.0.select_confirmed_utxos(
claim_id,
must_spend,
@@ -242,12 +248,14 @@ where
target_feerate_sat_per_1000_weight,
max_tx_weight,
);
- Box::pin(async move { coins })
+ async move { coins }
}
- fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction, ()> {
+ fn sign_psbt<'a>(
+ &'a self, psbt: Psbt,
+ ) -> impl Future<Output = Result<Transaction, ()>> + MaybeSend + 'a {
let psbt = self.0.sign_psbt(psbt);
- Box::pin(async move { psbt })
+ async move { psbt }
}
}
diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs
index 1d771d2..6d0d5bf 100644
--- a/lightning/src/sign/mod.rs
+++ b/lightning/src/sign/mod.rs
@@ -58,7 +58,7 @@ use crate::ln::script::ShutdownScript;
use crate::offers::invoice::UnsignedBolt12Invoice;
use crate::types::features::ChannelTypeFeatures;
use crate::types::payment::PaymentPreimage;
-use crate::util::async_poll::AsyncResult;
+use crate::util::async_poll::MaybeSend;
use crate::util::ser::{ReadableArgs, Writeable};
use crate::util::transaction_utils;
@@ -68,7 +68,9 @@ use crate::sign::ecdsa::EcdsaChannelSigner;
#[cfg(taproot)]
use crate::sign::taproot::TaprootChannelSigner;
use crate::util::atomic_counter::AtomicCounter;
+
use core::convert::TryInto;
+use core::future::Future;
use core::ops::Deref;
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(taproot)]
@@ -1066,7 +1068,9 @@ pub trait ChangeDestinationSource {
///
/// This method should return a different value each time it is called, to avoid linking
/// on-chain funds controlled to the same user.
- fn get_change_destination_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf, ()>;
+ fn get_change_destination_script<'a>(
+ &'a self,
+ ) -> impl Future<Output = Result<ScriptBuf, ()>> + MaybeSend + 'a;
}
/// A synchronous helper trait that describes an on-chain wallet capable of returning a (change) destination script.
@@ -1101,9 +1105,11 @@ impl<T: Deref> ChangeDestinationSource for ChangeDestinationSourceSyncWrapper<T>
where
T::Target: ChangeDestinationSourceSync,
{
- fn get_change_destination_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf, ()> {
+ fn get_change_destination_script<'a>(
+ &'a self,
+ ) -> impl Future<Output = Result<ScriptBuf, ()>> + MaybeSend + 'a {
let script = self.0.get_change_destination_script();
- Box::pin(async move { script })
+ async move { script }
}
}
diff --git a/lightning/src/util/async_poll.rs b/lightning/src/util/async_poll.rs
index eefa40d..9c2ca4c 100644
--- a/lightning/src/util/async_poll.rs
+++ b/lightning/src/util/async_poll.rs
@@ -9,7 +9,6 @@
//! Some utilities to make working with the standard library's [`Future`]s easier
-use alloc::boxed::Box;
use alloc::vec::Vec;
use core::future::Future;
use core::marker::Unpin;
@@ -92,17 +91,6 @@ pub(crate) fn dummy_waker() -> Waker {
unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &DUMMY_WAKER_VTABLE)) }
}
-#[cfg(feature = "std")]
-/// A type alias for a future that returns a result of type `T` or error `E`.
-///
-/// This is not exported to bindings users as async is only supported in Rust.
-pub type AsyncResult<'a, T, E> = Pin<Box<dyn Future<Output = Result<T, E>> + 'a + Send>>;
-#[cfg(not(feature = "std"))]
-/// A type alias for a future that returns a result of type `T` or error `E`.
-///
-/// This is not exported to bindings users as async is only supported in Rust.
-pub type AsyncResult<'a, T, E> = Pin<Box<dyn Future<Output = Result<T, E>> + 'a>>;
-
/// Marker trait to optionally implement `Sync` under std.
///
/// This is not exported to bindings users as async is only supported in Rust.
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.