Skip to content

fix(sequencer): prevent unhandled rejections from in-flight L1 requests during shutdown (v5 line) - #24560

Merged
spalladino merged 2 commits into
merge-train/spartan-v5from
cb/fix-flake-sentinel-status-slash-timestamp-v5
Jul 6, 2026
Merged

fix(sequencer): prevent unhandled rejections from in-flight L1 requests during shutdown (v5 line)#24560
spalladino merged 2 commits into
merge-train/spartan-v5from
cb/fix-flake-sentinel-status-slash-timestamp-v5

Conversation

@AztecBot

@AztecBot AztecBot commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Ports the remaining part of the sentinel_status_slash flake fix to the v5 line. The flake crashed the jest process after all tests passed: an in-flight eth_getBlockByNumber against the torn-down anvil surfaced as an unhandled L1RpcError (ECONNREFUSED). CI log: http://ci.aztec-labs.com/9bb09d2f904e290a

This complements #24551, which (after retargeting to merge-train/spartan-v5) now carries only the two files that cherry-picked cleanly from nextl1_tx_utils.ts (guard the loop-top getL1Timestamp() in monitorTransaction) and sequencer-publisher.ts (.catch on the fire-and-forget backupDroppedInSim). The other two files had diverged on this branch and are addressed here; #24551's title/body should be updated to reflect that it covers 2 of the original 4 files, with this PR covering the rest.

yarn-project/ethereum/src/publisher_manager.ts — fixed

This branch's stop() (which, unlike next, also clears the started flag and supports restart via start()) interrupted all publishers and the funder but returned immediately, so an in-flight tx monitor loop could still be issuing L1 RPC calls after test teardown. Applied the equivalent of the next fix, fitted to this branch's structure: stop() now awaits waitMonitoringStopped() on every publisher and the funder before returning. L1TxUtils.waitMonitoringStopped(timeoutSeconds = 10) already exists on this branch, is self-bounded, and swallows its own timeout with a warning, so shutdown cannot hang.

yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts — no change needed

On next, the votes-only path assigned a bare pendingL1Submission = publisher.sendRequestsAt(...) with no rejection handler, and only the last job's submission was awaited at shutdown, so an orphaned job's rejection could crash the process. On this branch that field no longer exists: both fire-and-forget paths go through this.pendingRequests.trackRequest(promise, () => this.interrupt()), backed by the sequencer's shared RequestsTracker (requests_tracker.ts). trackRequest attaches a rejection handler synchronously at track time (promise.then(delete, delete)), so a tracked promise can never surface as an unhandled rejection regardless of whether shutdown awaits it. Additionally, Sequencer.stop() calls pendingRequests.interruptRequests() followed by awaitRequests(), so all tracked requests — not just the last one — are interrupted and drained at shutdown. The next bug does not exist here, so no change was forced. (Minor observability note, not a bug: a rejection on the votes-only path is swallowed silently by the tracker rather than logged.)

Verified publisher_manager.ts parses cleanly; the full workspace type-check runs in CI (this container lacks the bootstrapped bb.js/noir/l1-artifacts toolchain).

No tracked issue.


Created by claudebox · group: slackbot

…ts during shutdown (v5 line)

PublisherManager.stop() interrupted publishers and the funder but returned
without waiting for their in-flight tx monitor loops to observe the
interrupt, so a monitor loop could still issue L1 RPC calls after test
teardown, surfacing as an unhandled L1RpcError (ECONNREFUSED) that crashed
the process after the sentinel_status_slash e2e had already passed.

Make stop() await waitMonitoringStopped() (bounded, ~10s) on all publishers
and the funder before returning.
@AztecBot AztecBot added ci-draft Run CI on draft PRs. ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure claudebox Owned by claudebox. it can push to this PR. labels Jul 6, 2026
AztecBot added a commit that referenced this pull request Jul 6, 2026
…during shutdown

Guards L1TxUtils.monitorTransaction's loop-top getL1Timestamp() call to
break quietly when interrupted, and adds a .catch to sequencer-publisher's
fire-and-forget backupDroppedInSim call.

Scoped to the 2 files that apply cleanly to merge-train/spartan-v5;
publisher_manager.ts and checkpoint_proposal_job.ts are covered separately
in #24560 since this branch's code has diverged there.
@spalladino
spalladino marked this pull request as ready for review July 6, 2026 21:17
@spalladino
spalladino enabled auto-merge (squash) July 6, 2026 21:19
… test mock

TestL1TxUtils in publisher_manager.test.ts is a lightweight mock cast via
'as TestL1TxUtils & L1TxUtils', not a real L1TxUtils subclass, so it never
had waitMonitoringStopped and TS didn't catch the gap. PublisherManager.stop()
now calls it on every publisher and the funder, so every test using it failed
with 'pub.waitMonitoringStopped is not a function'.
spalladino pushed a commit that referenced this pull request Jul 6, 2026
…during shutdown (v5 line, partial) (#24551)

## Flake

`multi-node/slashing/sentinel_status_slash.parallel.test.ts` ("slashes
an attestor that gets stopped after the network is running") flaked on
PR #24507 (unrelated change). CI log:
http://ci.aztec-labs.com/9bb09d2f904e290a

## What actually failed

The test itself **passed**. Immediately after, the node process crashed
with an **unhandled rejection** (`ECONNREFUSED` on anvil after teardown)
— something was still polling L1 after shutdown. Root cause:
`L1TxUtils.monitorTransaction`'s loop calls `getL1Timestamp()` at the
top of its loop, outside the loop's own try/catch, and nothing waited
for in-flight monitor iterations to actually stop during shutdown.

## Scope of this PR (rebased onto `merge-train/spartan-v5`)

This PR was originally written against `next` and touched 4 files. After
rebasing onto `merge-train/spartan-v5`, 2 of those files cherry-picked
cleanly and are included here:

- `yarn-project/ethereum/src/l1_tx_utils/l1_tx_utils.ts` — guards the
loop-top `getL1Timestamp()` call to break quietly when interrupted.
- `yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts` —
adds `.catch` to a bare fire-and-forget `backupDroppedInSim(...)` call.

The other 2 files (`publisher_manager.ts`, `checkpoint_proposal_job.ts`)
have diverged in structure on this branch and needed their own
investigation rather than a blind cherry-pick — that's covered
separately in
[#24560](#24560),
which found `publisher_manager.ts` had the same defect (fixed there) and
`checkpoint_proposal_job.ts` did not (this branch already routes
fire-and-forget L1 requests through a `RequestsTracker` that attaches
its own rejection handler).

No tracked issue exists for this flake; reference only.

---
*Created by
[claudebox](https://claudebox.work/v2/sessions/0959caa9f03f59fa) ·
group: `slackbot`*
@spalladino
spalladino merged commit e1711d3 into merge-train/spartan-v5 Jul 6, 2026
12 checks passed
@spalladino
spalladino deleted the cb/fix-flake-sentinel-status-slash-timestamp-v5 branch July 6, 2026 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-draft Run CI on draft PRs. ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure claudebox Owned by claudebox. it can push to this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants