Make background-processor `NO_*` constants actually `Send` + `Sync`
What changed, and why it matters
This commit fixes Rust type definitions for placeholder constants used when starting the Lightning background processor without optional components. The constants were supposed to be usable in multi-threaded and async code, but they were missing required Send and Sync markers, meaning async users could not actually compile code using them. The fix adds those markers and adjusts related type aliases. It is a compile-time API usability fix, not a runtime vulnerability patch.
No immediate security response is required. Users of the async background processor who encountered compile failures with the NO_* constants should upgrade to the patched version. Reviewers should verify the new Send + Sync bounds do not exclude legitimate no-std or c_bindings use cases, which the patch already gates with cfg attributes.
Security signals we found
Trait-object bounds changed from bare dyn Trait to dyn Trait + Send + Sync
Scorer wrapper switched from RefCell to RwLock under std to satisfy Sync
Constants explicitly intended for async use were previously not Send + Sync
Test converted to async to cover the previously broken compile path
Evidence from the diff
The patch updates the NO_ONION_MESSENGER, NO_LIQUIDITY_MANAGER, and NO_LIQUIDITY_MANAGER_SYNC constants in lightning-background-processor so their associated trait object type parameters carry + Send + Sync bounds. It also updates DynRouter, DynMessageRouter, DynSignerProvider, and DynChannelManager type aliases to use Send + Sync trait objects, and replaces core::cell::RefCell with std::sync::RwLock under the std feature for the scorer wrapper. The test is converted to an async tokio test that exercises both sync and async background-processor entry points with the constants, confirming they now compile and run in an async Send + Sync context.
Changed components
lightning-background-processor/src/lib.rsNO_ONION_MESSENGER constantNO_LIQUIDITY_MANAGER constantNO_LIQUIDITY_MANAGER_SYNC constantDynRouter type aliasDynMessageRouter type aliasDynSignerProvider type aliasDynChannelManager type aliasInspect captured patch +100 / −56
diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs
index 288e228..b222b2a 100644
--- a/lightning-background-processor/src/lib.rs
+++ b/lightning-background-processor/src/lib.rs
@@ -331,52 +331,60 @@ fn update_scorer<'a, S: Deref<Target = SC> + Send + Sync, SC: 'a + WriteableScor
true
}
+#[cfg(all(not(c_bindings), feature = "std"))]
+type ScorerWrapper<T> = std::sync::RwLock<T>;
+
+#[cfg(all(not(c_bindings), not(feature = "std")))]
+type ScorerWrapper<T> = core::cell::RefCell<T>;
+
#[cfg(not(c_bindings))]
type DynRouter = lightning::routing::router::DefaultRouter<
- &'static NetworkGraph<&'static dyn Logger>,
- &'static dyn Logger,
- &'static dyn EntropySource,
- &'static core::cell::RefCell<
+ &'static NetworkGraph<&'static (dyn Logger + Send + Sync)>,
+ &'static (dyn Logger + Send + Sync),
+ &'static (dyn EntropySource + Send + Sync),
+ &'static ScorerWrapper<
lightning::routing::scoring::ProbabilisticScorer<
- &'static NetworkGraph<&'static dyn Logger>,
- &'static dyn Logger,
+ &'static NetworkGraph<&'static (dyn Logger + Send + Sync)>,
+ &'static (dyn Logger + Send + Sync),
>,
>,
lightning::routing::scoring::ProbabilisticScoringFeeParameters,
lightning::routing::scoring::ProbabilisticScorer<
- &'static NetworkGraph<&'static dyn Logger>,
- &'static dyn Logger,
+ &'static NetworkGraph<&'static (dyn Logger + Send + Sync)>,
+ &'static (dyn Logger + Send + Sync),
>,
>;
#[cfg(not(c_bindings))]
type DynMessageRouter = lightning::onion_message::messenger::DefaultMessageRouter<
- &'static NetworkGraph<&'static dyn Logger>,
- &'static dyn Logger,
- &'static dyn EntropySource,
+ &'static NetworkGraph<&'static (dyn Logger + Send + Sync)>,
+ &'static (dyn Logger + Send + Sync),
+ &'static (dyn EntropySource + Send + Sync),
>;
#[cfg(all(not(c_bindings), not(taproot)))]
-type DynSignerProvider =
- dyn lightning::sign::SignerProvider<EcdsaSigner = lightning::sign::InMemorySigner>;
+type DynSignerProvider = dyn lightning::sign::SignerProvider<EcdsaSigner = lightning::sign::InMemorySigner>
+ + Send
+ + Sync;
#[cfg(all(not(c_bindings), taproot))]
-type DynSignerProvider = dyn lightning::sign::SignerProvider<
+type DynSignerProvider = (dyn lightning::sign::SignerProvider<
EcdsaSigner = lightning::sign::InMemorySigner,
TaprootSigner = lightning::sign::InMemorySigner,
->;
+> + Send
+ + Sync);
#[cfg(not(c_bindings))]
type DynChannelManager = lightning::ln::channelmanager::ChannelManager<
- &'static dyn chain::Watch<lightning::sign::InMemorySigner>,
- &'static dyn BroadcasterInterface,
- &'static dyn EntropySource,
- &'static dyn lightning::sign::NodeSigner,
+ &'static (dyn chain::Watch<lightning::sign::InMemorySigner> + Send + Sync),
+ &'static (dyn BroadcasterInterface + Send + Sync),
+ &'static (dyn EntropySource + Send + Sync),
+ &'static (dyn lightning::sign::NodeSigner + Send + Sync),
&'static DynSignerProvider,
- &'static dyn FeeEstimator,
+ &'static (dyn FeeEstimator + Send + Sync),
&'static DynRouter,
&'static DynMessageRouter,
- &'static dyn Logger,
+ &'static (dyn Logger + Send + Sync),
>;
/// When initializing a background processor without an onion messenger, this can be used to avoid
@@ -385,12 +393,12 @@ type DynChannelManager = lightning::ln::channelmanager::ChannelManager<
pub const NO_ONION_MESSENGER: Option<
Arc<
dyn AOnionMessenger<
- EntropySource = dyn EntropySource,
- ES = &dyn EntropySource,
- NodeSigner = dyn lightning::sign::NodeSigner,
- NS = &dyn lightning::sign::NodeSigner,
- Logger = dyn Logger,
- L = &'static dyn Logger,
+ EntropySource = dyn EntropySource + Send + Sync,
+ ES = &(dyn EntropySource + Send + Sync),
+ NodeSigner = dyn lightning::sign::NodeSigner + Send + Sync,
+ NS = &(dyn lightning::sign::NodeSigner + Send + Sync),
+ Logger = dyn Logger + Send + Sync,
+ L = &'static (dyn Logger + Send + Sync),
NodeIdLookUp = DynChannelManager,
NL = &'static DynChannelManager,
MessageRouter = DynMessageRouter,
@@ -414,20 +422,22 @@ pub const NO_ONION_MESSENGER: Option<
pub const NO_LIQUIDITY_MANAGER: Option<
Arc<
dyn ALiquidityManager<
- EntropySource = dyn EntropySource,
- ES = &dyn EntropySource,
- NodeSigner = dyn lightning::sign::NodeSigner,
- NS = &dyn lightning::sign::NodeSigner,
+ EntropySource = dyn EntropySource + Send + Sync,
+ ES = &(dyn EntropySource + Send + Sync),
+ NodeSigner = dyn lightning::sign::NodeSigner + Send + Sync,
+ NS = &(dyn lightning::sign::NodeSigner + Send + Sync),
AChannelManager = DynChannelManager,
CM = &DynChannelManager,
- Filter = dyn chain::Filter,
- C = &dyn chain::Filter,
- KVStore = dyn lightning::util::persist::KVStore,
- K = &dyn lightning::util::persist::KVStore,
- TimeProvider = dyn lightning_liquidity::utils::time::TimeProvider,
- TP = &dyn lightning_liquidity::utils::time::TimeProvider,
- BroadcasterInterface = dyn lightning::chain::chaininterface::BroadcasterInterface,
- T = &dyn BroadcasterInterface,
+ Filter = dyn chain::Filter + Send + Sync,
+ C = &(dyn chain::Filter + Send + Sync),
+ KVStore = dyn lightning::util::persist::KVStore + Send + Sync,
+ K = &(dyn lightning::util::persist::KVStore + Send + Sync),
+ TimeProvider = dyn lightning_liquidity::utils::time::TimeProvider + Send + Sync,
+ TP = &(dyn lightning_liquidity::utils::time::TimeProvider + Send + Sync),
+ BroadcasterInterface = dyn lightning::chain::chaininterface::BroadcasterInterface
+ + Send
+ + Sync,
+ T = &(dyn BroadcasterInterface + Send + Sync),
> + Send
+ Sync,
>,
@@ -439,20 +449,22 @@ pub const NO_LIQUIDITY_MANAGER: Option<
pub const NO_LIQUIDITY_MANAGER_SYNC: Option<
Arc<
dyn ALiquidityManagerSync<
- EntropySource = dyn EntropySource,
- ES = &dyn EntropySource,
- NodeSigner = dyn lightning::sign::NodeSigner,
- NS = &dyn lightning::sign::NodeSigner,
+ EntropySource = dyn EntropySource + Send + Sync,
+ ES = &(dyn EntropySource + Send + Sync),
+ NodeSigner = dyn lightning::sign::NodeSigner + Send + Sync,
+ NS = &(dyn lightning::sign::NodeSigner + Send + Sync),
AChannelManager = DynChannelManager,
CM = &DynChannelManager,
- Filter = dyn chain::Filter,
- C = &dyn chain::Filter,
- KVStoreSync = dyn lightning::util::persist::KVStoreSync,
- KS = &dyn lightning::util::persist::KVStoreSync,
- TimeProvider = dyn lightning_liquidity::utils::time::TimeProvider,
- TP = &dyn lightning_liquidity::utils::time::TimeProvider,
- BroadcasterInterface = dyn lightning::chain::chaininterface::BroadcasterInterface,
- T = &dyn BroadcasterInterface,
+ Filter = dyn chain::Filter + Send + Sync,
+ C = &(dyn chain::Filter + Send + Sync),
+ KVStoreSync = dyn lightning::util::persist::KVStoreSync + Send + Sync,
+ KS = &(dyn lightning::util::persist::KVStoreSync + Send + Sync),
+ TimeProvider = dyn lightning_liquidity::utils::time::TimeProvider + Send + Sync,
+ TP = &(dyn lightning_liquidity::utils::time::TimeProvider + Send + Sync),
+ BroadcasterInterface = dyn lightning::chain::chaininterface::BroadcasterInterface
+ + Send
+ + Sync,
+ T = &(dyn BroadcasterInterface + Send + Sync),
> + Send
+ Sync,
>,
@@ -3582,15 +3594,13 @@ mod tests {
r2.unwrap()
}
- #[test]
+ #[tokio::test]
#[cfg(not(c_bindings))]
- fn test_no_consts() {
+ async fn test_no_consts() {
// Compile-test the NO_* constants can be used.
let (_, nodes) = create_nodes(1, "test_no_consts");
- let data_dir = nodes[0].kv_store.get_data_dir();
- let persister = Arc::new(Persister::new(data_dir));
let bg_processor = BackgroundProcessor::start(
- persister,
+ Arc::clone(&nodes[0].kv_store),
move |_: Event| Ok(()),
Arc::clone(&nodes[0].chain_monitor),
Arc::clone(&nodes[0].node),
@@ -3606,5 +3616,39 @@ mod tests {
if !std::thread::panicking() {
bg_processor.stop().unwrap();
}
+
+ let kv_store = KVStoreSyncWrapper(Arc::clone(&nodes[0].kv_store));
+ let (exit_sender, exit_receiver) = tokio::sync::watch::channel(());
+ let sweeper_async: &'static OutputSweeper<_, _, _, _, _, _, _> = unsafe {
+ &*(nodes[0].sweeper.sweeper_async() as *const OutputSweeper<_, _, _, _, _, _, _>)
+ as &'static OutputSweeper<_, _, _, _, _, _, _>
+ };
+ let bp_future = super::process_events_async(
+ kv_store,
+ move |_: Event| async move { Ok(()) },
+ Arc::clone(&nodes[0].chain_monitor),
+ Arc::clone(&nodes[0].node),
+ crate::NO_ONION_MESSENGER,
+ nodes[0].no_gossip_sync(),
+ Arc::clone(&nodes[0].peer_manager),
+ crate::NO_LIQUIDITY_MANAGER,
+ Some(sweeper_async),
+ Arc::clone(&nodes[0].logger),
+ Some(Arc::clone(&nodes[0].scorer)),
+ move |dur: Duration| {
+ let mut exit_receiver = exit_receiver.clone();
+ Box::pin(async move {
+ tokio::select! {
+ _ = tokio::time::sleep(dur) => false,
+ _ = exit_receiver.changed() => true,
+ }
+ })
+ },
+ false,
+ || Some(Duration::ZERO),
+ );
+ let t1 = tokio::spawn(bp_future);
+ exit_sender.send(()).unwrap();
+ t1.await.unwrap().unwrap();
}
}
Why this scored 25/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.