Add deferred bool to ChainMonitor
What changed, and why it matters
This commit adds a new 'deferred' mode to ChainMonitor, a component that watches blockchain events for Lightning payment channels. When deferred=true, the watch methods currently panic with unimplemented!(). All existing callers pass false, so normal behavior is unchanged. This appears to be a partial feature implementation, not a security fix or vulnerability.
No security action required. If reviewing downstream usage, ensure no production code passes `deferred: true` until the feature is fully implemented, since it will panic.
Security signals we found
No security relevance claimed by vendor
No vulnerability pattern in diff
No memory safety, cryptographic, or authorization changes
unimplemented!() is intentional panic for unfinished feature path, not reachable by existing callers
All existing callers explicitly pass false
Evidence from the diff
The commit introduces a boolean deferred field to ChainMonitor and its constructors new and new_async_beta. When deferred is true, the Watch trait implementations of watch_channel and update_channel call unimplemented!() (panic). When false, they delegate to existing internal methods. All existing call sites in tests, fuzz targets, benchmarks, and utilities are updated to pass false, preserving current behavior. The commit message frames this as groundwork for queuing watch operations rather than executing them immediately.
Changed components
lightning/src/chain/chainmonitor.rsfuzz/src/chanmon_consistency.rsfuzz/src/full_stack.rsfuzz/src/lsps_message.rslightning/src/ln/chanmon_update_fail_tests.rslightning/src/ln/channelmanager.rslightning/src/util/test_utils.rsInspect captured patch +24 / −6
diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs
index 53591ad..45e9a68 100644
--- a/fuzz/src/chanmon_consistency.rs
+++ b/fuzz/src/chanmon_consistency.rs
@@ -282,6 +282,7 @@ impl TestChainMonitor {
Arc::clone(&persister),
Arc::clone(&keys),
keys.get_peer_storage_key(),
+ false,
)),
logger,
keys,
diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 5dfa510..47aebf4 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -603,6 +603,7 @@ pub fn do_test(mut data: &[u8], logger: &Arc<dyn Logger + MaybeSend + MaybeSync>
Arc::new(TestPersister { update_ret: Mutex::new(ChannelMonitorUpdateStatus::Completed) }),
Arc::clone(&keys_manager),
keys_manager.get_peer_storage_key(),
+ false,
));
let network = Network::Bitcoin;
diff --git a/fuzz/src/lsps_message.rs b/fuzz/src/lsps_message.rs
index 42feed4..8ff85d0 100644
--- a/fuzz/src/lsps_message.rs
+++ b/fuzz/src/lsps_message.rs
@@ -59,6 +59,7 @@ pub fn do_test(data: &[u8]) {
Arc::clone(&kv_store),
Arc::clone(&keys_manager),
keys_manager.get_peer_storage_key(),
+ false,
));
let best_block = BestBlock::from_network(network);
let params = ChainParameters { network, best_block };
diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs
index 17f7952..99f792f 100644
--- a/lightning/src/chain/chainmonitor.rs
+++ b/lightning/src/chain/chainmonitor.rs
@@ -373,6 +373,9 @@ pub struct ChainMonitor<
#[cfg(peer_storage)]
our_peerstorage_encryption_key: PeerStorageKey,
+
+ /// When `true`, [`chain::Watch`] operations are queued rather than executed immediately.
+ deferred: bool,
}
impl<
@@ -399,7 +402,7 @@ where
pub fn new_async_beta(
chain_source: Option<C>, broadcaster: T, logger: L, feeest: F,
persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, T, F>, _entropy_source: ES,
- _our_peerstorage_encryption_key: PeerStorageKey,
+ _our_peerstorage_encryption_key: PeerStorageKey, deferred: bool,
) -> Self {
let event_notifier = Arc::new(Notifier::new());
Self {
@@ -416,6 +419,7 @@ where
pending_send_only_events: Mutex::new(Vec::new()),
#[cfg(peer_storage)]
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
+ deferred,
}
}
}
@@ -605,7 +609,7 @@ where
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
pub fn new(
chain_source: Option<C>, broadcaster: T, logger: L, feeest: F, persister: P,
- _entropy_source: ES, _our_peerstorage_encryption_key: PeerStorageKey,
+ _entropy_source: ES, _our_peerstorage_encryption_key: PeerStorageKey, deferred: bool,
) -> Self {
Self {
monitors: RwLock::new(new_hash_map()),
@@ -621,6 +625,7 @@ where
pending_send_only_events: Mutex::new(Vec::new()),
#[cfg(peer_storage)]
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
+ deferred,
}
}
@@ -1428,13 +1433,21 @@ where
fn watch_channel(
&self, channel_id: ChannelId, monitor: ChannelMonitor<ChannelSigner>,
) -> Result<ChannelMonitorUpdateStatus, ()> {
- self.watch_channel_internal(channel_id, monitor)
+ if !self.deferred {
+ return self.watch_channel_internal(channel_id, monitor);
+ }
+
+ unimplemented!();
}
fn update_channel(
&self, channel_id: ChannelId, update: &ChannelMonitorUpdate,
) -> ChannelMonitorUpdateStatus {
- self.update_channel_internal(channel_id, update)
+ if !self.deferred {
+ return self.update_channel_internal(channel_id, update);
+ }
+
+ unimplemented!();
}
fn release_pending_monitor_events(
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index 3642825..a92af3e 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -4927,6 +4927,7 @@ fn native_async_persist() {
native_async_persister,
Arc::clone(&keys_manager),
keys_manager.get_peer_storage_key(),
+ false,
);
// Write the initial ChannelMonitor async, testing primarily that the `MonitorEvent::Completed`
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index b823864..70617b2 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -21747,7 +21747,7 @@ pub mod bench {
let seed_a = [1u8; 32];
let keys_manager_a = KeysManager::new(&seed_a, 42, 42, true);
- let chain_monitor_a = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_a, &keys_manager_a, keys_manager_a.get_peer_storage_key());
+ let chain_monitor_a = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_a, &keys_manager_a, keys_manager_a.get_peer_storage_key(), false);
let node_a = ChannelManager::new(&fee_estimator, &chain_monitor_a, &tx_broadcaster, &router, &message_router, &logger_a, &keys_manager_a, &keys_manager_a, &keys_manager_a, config.clone(), ChainParameters {
network,
best_block: BestBlock::from_network(network),
@@ -21757,7 +21757,7 @@ pub mod bench {
let logger_b = test_utils::TestLogger::with_id("node a".to_owned());
let seed_b = [2u8; 32];
let keys_manager_b = KeysManager::new(&seed_b, 42, 42, true);
- let chain_monitor_b = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_b, &keys_manager_b, keys_manager_b.get_peer_storage_key());
+ let chain_monitor_b = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_b, &keys_manager_b, keys_manager_b.get_peer_storage_key(), false);
let node_b = ChannelManager::new(&fee_estimator, &chain_monitor_b, &tx_broadcaster, &router, &message_router, &logger_b, &keys_manager_b, &keys_manager_b, &keys_manager_b, config.clone(), ChainParameters {
network,
best_block: BestBlock::from_network(network),
diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs
index 6c19af5..1009d2a 100644
--- a/lightning/src/util/test_utils.rs
+++ b/lightning/src/util/test_utils.rs
@@ -536,6 +536,7 @@ impl<'a> TestChainMonitor<'a> {
persister,
keys_manager,
keys_manager.get_peer_storage_key(),
+ false,
),
keys_manager,
expect_channel_force_closed: Mutex::new(None),
Why this scored 19/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.