feat(sequencer): AutomineSequencer for single-sequencer e2e tests#23354
feat(sequencer): AutomineSequencer for single-sequencer e2e tests#23354spalladino wants to merge 13 commits into
Conversation
A minimal, deterministic, queue-driven sequencer for e2e tests that don't exercise block-building or consensus. Reuses SequencerPublisher, FullNodeCheckpointsBuilder, and GlobalVariableBuilder; skips proposer-turn checks, validator orchestration, attestations, pipelining, P2P gossip, timetable enforcement, and event emission. Uses anvil setAutomine(true) with no interval mining; pre-sets next L1 block timestamp at slot boundaries when needed. Mempool-driven builds and explicit warp/buildEmptyBlock requests share a single serial queue and never interleave. Requires aztecTargetCommitteeSize=0 on the deployed rollup (the e2e default) so empty CommitteeAttestationsAndSigners is accepted by L1.
…chestration Adds the AUTOMINE_E2E_OPTS preset that opts a single-sequencer non-block-building test into the AutomineSequencer path. Adds a useAutomineSequencer flag to SetupOptions for the fixture to branch on. Adds four getters to AztecNodeService (getWorldStateSynchronizer, getL1ToL2MessageSource, getEpochCache, getGlobalVariableBuilder) so the test fixture can construct an AutomineSequencer alongside an otherwise-headless node (disableValidator + dontStartSequencer).
…ture Adds a useAutomineSequencer config flag. When set, AztecNodeService.createAndSync constructs an AutomineSequencer inside the existing validator-enabled branch, reusing the same L1 deps (l1TxUtils, publisher manager, publisher factory, checkpoints builder) instead of going through SequencerClient.new. AztecNodeService.mineBlock routes to AutomineSequencer.buildEmptyBlock when the automine path is wired. CheatCodes.warpL2TimeAtLeastTo delegates to the queue when an AutomineSequencer is wired, so existing test helpers work unchanged. Adds the AutomineSequencer + AutomineSequencerDeps + AutomineSequencerConstants exports from sequencer-client, exposes getPublisherConfigFromSequencerConfig, and adds a USE_AUTOMINE_SEQUENCER env var. Adds e2e_automine_smoke.test.ts exercising sequential txs, parallel txs, warp, and mineBlock under AUTOMINE_E2E_OPTS.
Without waiting, the next mempool-driven build picks up a stale tip and L1 rejects the propose with Rollup__InvalidArchive — the freshly-built header points at the pre-publish lastArchive, but L1's lastArchive has already advanced to the just-published checkpoint. Also cap the smoke-test warp at 24s (2 slots) so it doesn't cross the L1 proof-submission window and trigger a separate prune-related code path.
Mechanical swap from PIPELINING_SETUP_OPTS to AUTOMINE_E2E_OPTS for tests that don't exercise block-building or consensus: - e2e_authwit - e2e_keys - e2e_partial_notes - e2e_orderbook - e2e_double_spend Each tx now lands as soon as the AutomineSequencer picks it up from the mempool rather than waiting for the next 12s slot boundary.
- Drop duplicate re-export of getPublisherConfigFromSequencerConfig from publisher/index.ts; it's already available via sequencer-client's top-level re-export through ./config.js -> ./publisher/config.js. - Drop redundant async keyword from the buildIfPending queue callback (returns runBuild's promise directly, no await needed).
…OPTS Mechanical swap from PIPELINING_SETUP_OPTS to AUTOMINE_E2E_OPTS for 28 more single-sequencer non-block-building tests. Tests with hand-rolled warps (e2e_lending_contract), multi-PXE setups (e2e_2_pxes), or block-building semantics (e2e_block_building, e2e_pruned_blocks, e2e_sequencer_config, e2e_l1_with_wall_time, e2e_expiration_timestamp, e2e_genesis_timestamp) are intentionally held for follow-up.
Pulls the ~50 LOC of inline AutomineSequencer construction out of AztecNodeService.createAndSync into a dedicated factory under the sequencer-client package. Server-side code is back to a single function call; the factory owns publisher-manager wiring, attestor lookup, and EthCheatCodes construction. Pure refactor — no behavior change.
…encer compat
Test-side helper relied on setConfig({ minTxsPerBlock: 0 }) and a polling
wait, expecting the production Sequencer's polling loop to fire an empty
block when no txs were pending. AutomineSequencer never fires unprompted —
it builds on tx arrival or explicit request only, so the wait timed out.
aztecNode.mineBlock() already routes to AutomineSequencer.buildEmptyBlock
when wired, or temporarily drops minTxsPerBlock and triggers the production
sequencer otherwise — works under both presets.
The test does a full L1 reorg via rollbackTo + resumeSync + forceEmptyBlock. AutomineSequencer has no reorg awareness — after a rollback, its archiver- sync wait races against the archiver re-ingesting the original checkpoint from L1, causing either a Rollup__InvalidArchive on re-publish or a deadlock on the sync wait. Reorg semantics are out of scope for AUTOMINE_E2E_OPTS; this test stays on PIPELINING_SETUP_OPTS where it works cleanly.
Implements revertToCheckpoint(targetCheckpoint) on AutomineSequencer, running inside the serial queue so it never interleaves with builds. The sequence: 1. Fetch the target checkpoint's L1 block number from the archiver. 2. Call archiverRollback() to reset the archiver to the target checkpoint boundary (must happen before the L1 reorg so the archiver can still read the target checkpoint's L1 block hash). 3. Call worldState.syncImmediate() to propagate the archiver prune event to world-state before the next build runs. 4. Reorg L1 with reorg(depth) to drop blocks strictly after the target checkpoint publish block, keeping that block as the new tip. 5. Drop all pending L1 txs (anvil_rollback re-queues them) and reset the publisher's cached nonce so the next propose tx uses the correct post-reorg nonce. 6. Reset lastBuiltSlot and sync the date provider. Also adds resetNonce() to L1TxUtils to support step 5, and wires archiverRollback / resetPublisherNonces callbacks through the factory. Smoke test: adds "revertToCheckpoint rolls back L1+L2 state" to e2e_automine_smoke.test.ts, verifying the L2 tip drops and a new tx lands cleanly after the revert. All 5 scenarios pass.
… revertToCheckpoint
Replaces the PIPELINING_SETUP_OPTS + pauseSync/rollbackTo/resumeSync
pattern with AUTOMINE_E2E_OPTS + AutomineSequencer.revertToCheckpoint().
Changes:
- Switch setup preset from PIPELINING_SETUP_OPTS to AUTOMINE_E2E_OPTS.
- forceReorg now captures checkpointBeforeTx (the checkpointed tip
before the transfer tx lands) and calls revertToCheckpoint() with
that number. This atomically rolls back L1, the archiver, and
world-state in one call.
- forceEmptyBlock uses aztecNodeService.mineBlock() instead of
setConfig({ minTxsPerBlock: 0 }), which is the correct path under
AutomineSequencer.
- Remove pauseSync/resumeSync calls (no longer needed; the archiver
never pauses under AutomineSequencer).
- After the reorg, the p2p tx pool restores the rolled-back transfer tx
to pending, so the AutomineSequencer re-mines it automatically.
Three bugs caused the reorg test to fail: 1. After `archiverRollback`, the P2P block stream's `chain-pruned` event was only processed on the next poll cycle (~50ms later), so rolled-back txs weren't restored to pending before the next build ran. Fix: call `syncP2P()` in `runRevert` to force one immediate P2P block stream work cycle. 2. `runRevert` was calling `dateProvider.setTime(newL1Ts * 1000)` to roll the date provider back to the target checkpoint's L1 timestamp. Since the restored tx's `receivedAt` was recorded after that timestamp, `getEligiblePendingTxHashes` filtered it out as "too new". Fix: remove the `setTime` call from `runRevert` so the date provider keeps its current time. 3. `runBuild` called `worldState.fork(syncedToBlockNumber)` using the archiver's tip, but world state syncs from the archiver asynchronously. When the mempool-driven build for the re-queued tx completed and the follow-up empty block build ran immediately after, world state could still be one block behind, causing "Unable to initialize from future block". Fix: call `worldState.syncImmediate(syncedToBlockNumber)` before forking. Also adds a `retryUntil` loop in the test to wait for PXE to process the re-mined block's notes, since PXE syncs asynchronously from the archiver.
Codex review (gpt-5.5, high reasoning)Verdict: needs fixes before merge; the shape is defensible for e2e-only automining, but there are a few concurrency/lifecycle holes that can make the fast path flaky. Findings
Things I checked that look OK
|
Motivation
E2e tests outside of
e2e_p2p,e2e_epochs,e2e_slashing, ande2e_block_buildingdon't exercise block-building or consensus — they just need their tx to land. Running them on the productionSequencer(~1100 LOC) +CheckpointProposalJob(~850 LOC) with 12s slot cadence is pure overhead: every test pays for proposer-turn checks, pipelining bookkeeping, validator attestations, and slot-aligned waits that aren't being tested. This PR explores giving those tests a minimal, deterministic, queue-driven alternative that runs txs effectively as fast as the block builder allows.Approach
Adds an
AutomineSequenceralongside the production one. It reusesSequencerPublisher,FullNodeCheckpointsBuilder, andGlobalVariableBuilder; skips proposer-turn checks, validator orchestration, attestations, pipelining, P2P gossip, timetable enforcement, and event emission. Anvil runs in automine mode with no interval mining; the sequencer pre-sets next L1 block timestamps at slot boundaries only when needed. All test time control (warps, empty-block requests) shares a single serial queue with mempool-driven builds — the three never interleave. RequiresaztecTargetCommitteeSize == 0(the e2e default), so an emptyCommitteeAttestationsAndSignersis accepted by L1 via theverifyProposer/verifyAttestationsearly-return atValidatorSelectionLib.sol:244-249.Changes
AutomineSequencer(~370 LOC) with serial queue + mempool poller,buildIfPending/buildEmptyBlock/warpTo/warpBy. Wait for archiver to surface the published checkpoint before returning to avoidRollup__InvalidArchiveon the next build.useAutomineSequencerconfig flag.AztecNodeService.createAndSyncconstructs the AutomineSequencer inside the existing validator-enabled branch from the same L1 deps (l1TxUtils, publisher manager, publisher factory, checkpoints builder) instead of going throughSequencerClient.new.mineBlockroutes toAutomineSequencer.buildEmptyBlockwhen wired. Adds getters forWorldStateSynchronizer,L1ToL2MessageSource,EpochCache,GlobalVariableBuilder, andAutomineSequencer.CheatCodes.warpL2TimeAtLeastTodelegates to the queue when an AutomineSequencer is wired, so existing test helpers (warpL2TimeAtLeastBy, etc.) work unchanged.AUTOMINE_E2E_OPTSpreset,useAutomineSequencerflag onSetupOptions, ande2e_automine_smoke.test.tsexercising sequential txs, parallel txs, warp, andmineBlock.Plan:
/home/santiago/.claude/plans/i-had-anotehr-agent-snazzy-forest.md(codex-reviewed). Smoke test passes locally (4/4, ~78s wall time). Next steps: migrate the first batch of single-sequencer tests toAUTOMINE_E2E_OPTS.