Problem
L1WatcherHandle::revert_to_l1_block enqueues a ResetToBlock command and immediately drops the old notification receiver (crates/watcher/src/handle/mod.rs). If the watcher is mid-step() sending on the old channel at that moment, the send fails with SendError, which the run() loop treats as terminal — it stops the watcher without draining the queued reset.
Result: a dead watcher and a silently stalled node. No further L1 notifications (batches, messages, finalizations) are ever delivered, with only a single warn line as evidence.
This is pre-existing on feat/tsuki-hardfork-2 and independent of the signer-refresh work — any component that calls revert_to_l1_block under load can trigger it.
Reference fix (mine for parts)
PR #7 (closed unmerged) fixed this in commit 7496ee8 via recover_or_stop_on_closed_channel(): on any closed-channel send, drain the command queue for a pending reset and recover onto the fresh channels; stop only if no reset is queued. The command channel is unbounded, so the recovery cannot deadlock behind a full notification channel. The fix is small and extractable on its own, and the PR branch contains a regression test reproducing the race at the Synced send point.
Found during the PR #7 review cycle (test-coverage analysis, confirmed by differential review).
Problem
L1WatcherHandle::revert_to_l1_blockenqueues aResetToBlockcommand and immediately drops the old notification receiver (crates/watcher/src/handle/mod.rs). If the watcher is mid-step()sending on the old channel at that moment, the send fails withSendError, which therun()loop treats as terminal — it stops the watcher without draining the queued reset.Result: a dead watcher and a silently stalled node. No further L1 notifications (batches, messages, finalizations) are ever delivered, with only a single warn line as evidence.
This is pre-existing on
feat/tsuki-hardfork-2and independent of the signer-refresh work — any component that callsrevert_to_l1_blockunder load can trigger it.Reference fix (mine for parts)
PR #7 (closed unmerged) fixed this in commit
7496ee8viarecover_or_stop_on_closed_channel(): on any closed-channel send, drain the command queue for a pending reset and recover onto the fresh channels; stop only if no reset is queued. The command channel is unbounded, so the recovery cannot deadlock behind a full notification channel. The fix is small and extractable on its own, and the PR branch contains a regression test reproducing the race at theSyncedsend point.Found during the PR #7 review cycle (test-coverage analysis, confirmed by differential review).