Skip to content

Commit 79be78c

Browse files
joostjagerclaude
andcommitted
Move ChannelMonitorUpdateStatus mode check from ChannelManager to ChainMonitor
When `ChannelMonitor::update_monitor` fails (e.g. a counterparty `commitment_signed` arrives after funding was spent), `ChainMonitor` persists the full monitor successfully but overrides the return value to `InProgress`. If the user's `Persist` impl only returns `Completed`, this causes `ChannelManager::handle_monitor_update_res` to panic on the mode mismatch. The root cause is that the mode check lived in `ChannelManager`, which sees the *post-override* status. By moving the check to `ChainMonitor` and running it against the *raw* `Persist` result (before the `update_res.is_err()` override), we validate only the user's persister behavior. ChainMonitor's internal override to `InProgress` for failed monitor updates no longer triggers a false mode violation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2d2151a commit 79be78c

2 files changed

Lines changed: 34 additions & 24 deletions

File tree

lightning/src/chain/chainmonitor.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ pub struct ChainMonitor<
369369
/// Messages to send to the peer. This is currently used to distribute PeerStorage to channel partners.
370370
pending_send_only_events: Mutex<Vec<MessageSendEvent>>,
371371

372+
/// We only support using one of [`ChannelMonitorUpdateStatus::InProgress`] and
373+
/// [`ChannelMonitorUpdateStatus::Completed`] without restarting. We enforce this in non-test
374+
/// builds by storing which one is in use (0 = unset, 1 = InProgress, 2 = Completed).
375+
#[cfg(not(any(test, feature = "_externalize_tests")))]
376+
monitor_update_type: AtomicUsize,
377+
372378
#[cfg(peer_storage)]
373379
our_peerstorage_encryption_key: PeerStorageKey,
374380
}
@@ -412,6 +418,8 @@ where
412418
event_notifier: Arc::clone(&event_notifier),
413419
persister: AsyncPersister { persister, event_notifier },
414420
pending_send_only_events: Mutex::new(Vec::new()),
421+
#[cfg(not(any(test, feature = "_externalize_tests")))]
422+
monitor_update_type: AtomicUsize::new(0),
415423
#[cfg(peer_storage)]
416424
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
417425
}
@@ -617,11 +625,32 @@ where
617625
highest_chain_height: AtomicUsize::new(0),
618626
event_notifier: Arc::new(Notifier::new()),
619627
pending_send_only_events: Mutex::new(Vec::new()),
628+
#[cfg(not(any(test, feature = "_externalize_tests")))]
629+
monitor_update_type: AtomicUsize::new(0),
620630
#[cfg(peer_storage)]
621631
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
622632
}
623633
}
624634

635+
#[cfg(not(any(test, feature = "_externalize_tests")))]
636+
fn check_monitor_update_type(
637+
monitor_update_type: &AtomicUsize, persist_res: &ChannelMonitorUpdateStatus,
638+
) {
639+
match persist_res {
640+
ChannelMonitorUpdateStatus::InProgress => {
641+
if monitor_update_type.swap(1, Ordering::Relaxed) == 2 {
642+
panic!("Cannot use both ChannelMonitorUpdateStatus modes InProgress and Completed without restart");
643+
}
644+
},
645+
ChannelMonitorUpdateStatus::Completed => {
646+
if monitor_update_type.swap(2, Ordering::Relaxed) == 1 {
647+
panic!("Cannot use both ChannelMonitorUpdateStatus modes InProgress and Completed without restart");
648+
}
649+
},
650+
ChannelMonitorUpdateStatus::UnrecoverableError => {},
651+
}
652+
}
653+
625654
/// Gets the balances in the contained [`ChannelMonitor`]s which are claimable on-chain or
626655
/// claims which are awaiting confirmation.
627656
///
@@ -1285,6 +1314,8 @@ where
12851314
let update_id = monitor.get_latest_update_id();
12861315
let mut pending_monitor_updates = Vec::new();
12871316
let persist_res = self.persister.persist_new_channel(monitor.persistence_key(), &monitor);
1317+
#[cfg(not(any(test, feature = "_externalize_tests")))]
1318+
Self::check_monitor_update_type(&self.monitor_update_type, &persist_res);
12881319
match persist_res {
12891320
ChannelMonitorUpdateStatus::InProgress => {
12901321
log_info!(logger, "Persistence of new ChannelMonitor in progress",);
@@ -1367,6 +1398,8 @@ where
13671398
monitor,
13681399
)
13691400
};
1401+
#[cfg(not(any(test, feature = "_externalize_tests")))]
1402+
Self::check_monitor_update_type(&self.monitor_update_type, &persist_res);
13701403
match persist_res {
13711404
ChannelMonitorUpdateStatus::InProgress => {
13721405
pending_monitor_updates.push(update_id);

lightning/src/ln/channelmanager.rs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2779,13 +2779,6 @@ pub struct ChannelManager<
27792779
#[cfg(any(test, feature = "_test_utils"))]
27802780
pub(super) per_peer_state: FairRwLock<HashMap<PublicKey, Mutex<PeerState<SP>>>>,
27812781

2782-
/// We only support using one of [`ChannelMonitorUpdateStatus::InProgress`] and
2783-
/// [`ChannelMonitorUpdateStatus::Completed`] without restarting. Because the API does not
2784-
/// otherwise directly enforce this, we enforce it in non-test builds here by storing which one
2785-
/// is in use.
2786-
#[cfg(not(any(test, feature = "_externalize_tests")))]
2787-
monitor_update_type: AtomicUsize,
2788-
27892782
/// The set of events which we need to give to the user to handle. In some cases an event may
27902783
/// require some further action after the user handles it (currently only blocking a monitor
27912784
/// update from being handed to the user to ensure the included changes to the channel state
@@ -3527,9 +3520,6 @@ impl<
35273520

35283521
per_peer_state: FairRwLock::new(new_hash_map()),
35293522

3530-
#[cfg(not(any(test, feature = "_externalize_tests")))]
3531-
monitor_update_type: AtomicUsize::new(0),
3532-
35333523
pending_events: Mutex::new(VecDeque::new()),
35343524
pending_events_processor: AtomicBool::new(false),
35353525
pending_htlc_forwards_processor: AtomicBool::new(false),
@@ -10006,23 +9996,13 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
100069996
panic!("{}", err_str);
100079997
},
100089998
ChannelMonitorUpdateStatus::InProgress => {
10009-
#[cfg(not(any(test, feature = "_externalize_tests")))]
10010-
if self.monitor_update_type.swap(1, Ordering::Relaxed) == 2 {
10011-
panic!("Cannot use both ChannelMonitorUpdateStatus modes InProgress and Completed without restart");
10012-
}
100139999
log_debug!(
1001410000
logger,
1001510001
"ChannelMonitor update in flight, holding messages until the update completes.",
1001610002
);
1001710003
false
1001810004
},
10019-
ChannelMonitorUpdateStatus::Completed => {
10020-
#[cfg(not(any(test, feature = "_externalize_tests")))]
10021-
if self.monitor_update_type.swap(2, Ordering::Relaxed) == 1 {
10022-
panic!("Cannot use both ChannelMonitorUpdateStatus modes InProgress and Completed without restart");
10023-
}
10024-
true
10025-
},
10005+
ChannelMonitorUpdateStatus::Completed => true,
1002610006
}
1002710007
}
1002810008

@@ -19550,9 +19530,6 @@ impl<
1955019530

1955119531
per_peer_state: FairRwLock::new(per_peer_state),
1955219532

19553-
#[cfg(not(any(test, feature = "_externalize_tests")))]
19554-
monitor_update_type: AtomicUsize::new(0),
19555-
1955619533
pending_events: Mutex::new(pending_events_read),
1955719534
pending_events_processor: AtomicBool::new(false),
1955819535
pending_htlc_forwards_processor: AtomicBool::new(false),

0 commit comments

Comments
 (0)