feat: merge-train/spartan-v5#24532
Merged
Merged
Conversation
…and instrument setup spans (#24452) ## Motivation Span-instrumented full CI runs (since #24407 landed) now show where the remaining e2e wall-clock goes: a large uninstrumented setup layer, a cluster of proven-checkpoint/epoch waits that burn real time while nothing is being built, and a 1s in-process poll cadence that adds ~0.5-0.9s of dead sleep to nearly every awaited tx. This PR takes the low-risk warp folds and polling wins that are safe today, and adds span instrumentation to the setup layer so the next round can attack the ~5,000s of currently-invisible beforeAll work. Everything multi-node is validated on CI (hence `ci-no-fail-fast`); the last commit is a deliberate slot-cut experiment kept isolated so it can be reverted on its own. ## Changes - **poll in-process nodes at 250ms instead of 1s** — TestWallet now defaults its `send().wait()` poll interval to 0.25s (in-process nodes reach CHECKPOINTED synchronously under automine and cheaply otherwise), with the spartan/worker sites explicitly restored to the 1s default since they talk to remote JSON-RPC nodes. The e2e-local wait helpers (`wait_helpers.ts`, `waitForProvenChain`, gas-portal block advance, L1->L2 message poll) drop from 1s to 0.25s. Removes ~0.5-0.9s of dead sleep per awaited tx across ~600 tests. Production aztec.js/wallet-sdk defaults are untouched. - **warp past the epoch boundary in waitForProvenCheckpoint** — after the multi-node block-production fixture stops its sequencers, warp the L1 clock one epoch forward (forward-only, skipped if already proven) so the epoch closes and the fake prover can prove+submit without waiting the epoch out in real time. Targets ~716s of suite-summed `wait:proven-checkpoint` across proposed_chain (~430s), deploy_and_call_ordering (~143s), cross_chain_messages (~143s), plus blob_promotion. - **warp epoch waits in multi_proof and upload_failed_proof** — replace `waitUntilEpochStarts` with `warpToEpochStart` in these two 12s-slot proving tests (both passed under warp in the round-1 CI sweep). proof_fails is deliberately left untouched. - **register-only TestContract in automine pxe test** — the test only calls the noinitcheck private `emit_nullifier`, so register the contract instead of deploying it, dropping a deployment tx and its checkpoint cycle. - **compute genesis values on an ephemeral world state** — `generateGenesisValues` used a full fsync-on `NativeWorldStateService.tmp` per e2e container just to read one tree root; switch to the fsync-off `ephemeral` backend. Adds a unit test asserting `tmp` and `ephemeral` produce identical archive roots for a funded-accounts genesis with non-zero timestamp (this path is consensus-critical — CLI deploy paths compute the on-chain genesis root through it). - **instrument setup-layer deploys and mints with spans** — wrap the top-offender setup helpers (fees harness token/FPC deploys + mints + fee-juice bridge, cross-chain token/bridge deploys + mints, shared auth-registry publish, gas-portal bridge) in `testSpan` under the #24407 taxonomy (`deploy:*`, `tx:mint`, `setup:bridge`, `setup:auth-registry`). Zero behavior change (testSpan is a passthrough without `TEST_TIMING_FILE`); this is the data source for round 4's attack on the ~5,000s of uninstrumented beforeAll work. - **cut multi_validator_node slots 36s -> 16s** (experiment, last commit) — lower `aztecSlotDuration` 36->16 and `blockDurationMs` 6000->2000 together (eth stays 8). Small expected saving; kept as the final isolated commit so it can be reverted alone if CI shows committee/attestation trouble on this file. One planned item was dropped: an opt-in warp for `ChainMonitor.waitUntilL2Slot`. All three candidate call sites turned out to cover deliberate real-time building (live-sequencer coordination, inactivity accumulation across an epoch, and the proof-boundary critical window), so the opt-in API would have had no safe callers. ## Verification Locally: full `yarn build`, `yarn format --check`, and `yarn lint` on the touched packages all pass. The new world-state genesis-equivalence unit test passes (`tmp` and `ephemeral` roots identical). The `automine/pxe.test.ts` e2e passes as a smoke test for the register-only change and the 250ms polling. Everything multi-node (the warp folds, the timing cut) is validated on CI. Note the final commit is a deliberate slot-cut experiment that can be reverted in isolation. ## Measured impact Full green CI run of this PR (`9b4cc967`, CI `1782961631439529`) vs the base-proxy full run (`d160265b`, CI `1782938936852228` — the branch point plus one unrelated one-file test change). Identical test populations: 2051 rows, all passed, in both runs. Sums are across parallel processes, not wall-clock (methodology in the Linear "Times tracking" doc). | Bucket | Base | This PR | Δ | |---|---|---|---| | Overall | 7h 26m 03s | 6h 36m 20s | **−49m 43s (−11.1%)** | | Setup (before-hooks) | 2h 14m 31s | 1h 45m 44s | −21.4% | | — of which setup.ts | 42m 53s | 34m 13s | −20.2% | | Body | 5h 05m 14s | 4h 45m 03s | −6.6% | | Teardown | 6m 17s | 5m 33s | −11.6% | By mechanism: - **Proven-checkpoint warp**: `wait:proven-checkpoint` 14m 12s → 3m 32s (**−75%**), worst single wait 215s → 79s. Suite deltas match span deltas ~1:1 — proposed_chain −67%, deploy_and_call_ordering −45%, cross_chain_messages −21%. Hard attribution. - **Epoch warps**: multi_proof −52% (wait:epoch 92s → 40s). upload_failed_proof's warp also worked (35s → 20s) but was masked in the suite total by one-shot proving noise in that run. - **250ms polling + ephemeral genesis**: ~30–35 min spread across the whole suite — 141 of 152 suites improved; every `setup:env:*` span dropped ~16–50% with identical counts (same work, less waiting). - **Slot-cut experiment**: multi_validator_node 103s → 84s (−18.6%) and passed CI — the experiment survives. - **Register-only pxe test**: 18.2s → 10.1s. - **New setup spans**: ~44 min/run of previously untagged setup time is now attributable (`setup:auth-registry` 16m, `setup:bridge` 10m, `tx:mint` 8m, `deploy:token` 7m, `deploy:fpc` 2m) — the target list for round 4. 11 suites regressed (~8 min total vs ~58 min of improvements), all in untagged real-time slashing/proving bodies whose tagged waits are unchanged — consistent with run-to-run variance, not PR effects (per-suite noise floor between same-day runs is median ~11s / p90 ~52s).
…24481) L2 slot boundaries must land on L1 slot boundaries, but nothing enforced this at runtime. A check for exactly this relationship already existed (`validateNetworkConsensusConfig` in stdlib), but it was only exercised by a CLI unit test gating the three static mainnet/testnet/devnet config files — never wired into e2e test setup or the L1-contract deployment path itself. ## Approach - Add `validateSlotDurations`/`assertValidSlotDurations` to `ethereum/src/config.ts` and call it from `deployAztecL1Contracts`, the shared choke point used by e2e tests, spartan deployments, and CLI deploys, so a bad pairing now fails fast with a clear error. - Refactor `validateNetworkConsensusConfig` to reuse the shared check instead of duplicating it. - Fix the three e2e configs this caught: all paired `aztecSlotDuration: 36` with `ethereumSlotDuration: 8` (4.5 L1 slots per L2 slot). Corrected to `ethereumSlotDuration: 12`, matching the `36s`/`12s` convention already used elsewhere in the same test suites.
) ## Human-written summary Invalid numeric values in config were silently ignored and replaced with the default. Also, config entries defined with `numberConfigHelper` accepted non-integer values but silently truncated them. ## Context The numeric config helpers mishandled bad input in two ways: - `numberConfigHelper` / `optionalNumberConfigHelper` parsed with `parseInt`, which silently truncated decimals — an integer config set to `0.8` became `0` with no warning. In the worst case (A-1312) that turned a fractional retention window into `0`, i.e. "delete immediately". - `numberConfigHelper`, `floatConfigHelper` and `percentageConfigHelper` swallowed any unparseable value and returned the config default (the JSDoc even documented "...or is invalid"), so a typo'd env var ran silently with an unexpected value. ## Approach The config default is for an **unset** env var only — empty/unset is already resolved to the default before `parseEnv` runs, so the only thing `parseEnv` returning a default achieved was hiding bad input. The helpers now parse strictly and throw on any set-but-invalid value: - `numberConfigHelper` / `optionalNumberConfigHelper`: parse with `parseFloat` and require a safe integer (no more silent truncation). - `floatConfigHelper` / `percentageConfigHelper`: require a finite number; percentage additionally enforces 0–1. `bigint`, `enum` and the secret helpers already threw on invalid input. `booleanConfigHelper` is intentionally unchanged: `parseBooleanEnv` is a total function (unrecognized tokens map to `false`), it never substitutes the configured default. Auditing all ~150 `numberConfigHelper` call sites then surfaced options that legitimately take fractional values and would now wrongly reject them; these were moved to `floatConfigHelper`: - p2p gossipsub tx scoring — topic weight, invalid-message-delivery weight, and decay (default `0.5`), which feed libp2p's float `TopicScoreParams`. - sequencer `perBlockDAAllocationMultiplier` (default `1.5`); its sibling `perBlockAllocationMultiplier` already used `floatConfigHelper`. - L1 tx fee percentages — `gasLimitBufferPercentage`, `priorityFeeBumpPercentage`, `priorityFeeRetryBumpPercentage`. - bot `minFeePadding`, consumed as the fractional overpay factor `1 + padding`; its zod schema no longer pins it to an integer. ## Impact A node whose numeric config env var (or CLI flag) is set to a non-numeric, fractional (for integer options), or out-of-range value now fails at startup with a clear error instead of silently running with a truncated or default value. Operators relying on the old lenient behavior must correct the value. Fixes A-1398
…(A-1401) (#24537) ## Context A-1351 (#24515) fixed the honest-proposer and relay path for non-canonical yParity attestation signatures, but a malicious selected proposer can still hand-craft `propose()` calldata carrying a non-proposer signature slot with `v ∈ {0,1}` (or an all-zero `(r,s,v)` in a bitmap-marked signature slot). L1 accepts this at propose time (it only recovers the proposer's own slot) and stores the attestations hash over the raw bytes. Afterwards, either: - the slot recovers to the correct member, so honest nodes deem the checkpoint valid and nobody invalidates it — but epoch proving reverts at `ECDSA.recover` (which rejects `v ∉ {27,28}`), a silent stall; or - the slot is detected as invalid, but the invalidation repack via `packAttestations` is not byte-faithful, so the recomputed hash diverges from the stored one and `InvalidateLib` reverts — wedging the chain until prune. This is the malicious-proposer sibling of A-1351; it survives that fix. Tracks aztec-claude#650. ## Approach TS-only — no L1 changes, since this targets a fresh deployment. Two parts: - **Detection** — `getAttestationInfoFromPayload` no longer passes `allowYParityAsV: true`, so TS signature validity matches L1's `ECDSA.recover`. A non-canonical slot is now classified as an invalid attestation, so honest nodes detect the bad checkpoint instead of silently accepting it. - **Byte-faithful invalidation** — the raw packed `CommitteeAttestations` tuple from the checkpoint calldata is threaded verbatim (as `verbatimAttestations`) through the negative `ValidateCheckpointResult` into `buildInvalidateCheckpointRequest`, which submits those exact bytes rather than repacking. With the original bytes, `invalidateBadAttestation` reproduces the stored hash and L1's `tryRecover` returns `address(0) ≠ member`, so invalidation succeeds. `verbatimAttestations` is a required field on the negative result and is serialized unconditionally — a repack fallback is intentionally absent, since silently repacking would re-introduce the wedge. The gossip sender-recovery path (`recoverCoordinationSigner`) deliberately stays lenient (`allowYParityAsV`) so an honest node can still attribute a yParity-encoded message and canonicalize it on ingress; only the on-chain checkpoint validation is made strict. A comment documents the split so it is not accidentally unified. ## Tests An e2e regression under the invalidation suite (`invalidate_block.parallel.test.ts`, "proposer invalidates checkpoint with a yParity attestation slot") demonstrates the attack red→green: a malicious proposer lands a checkpoint whose non-proposer attestation slots carry raw yParity bytes, and the honest next proposer invalidates it — which only succeeds when the invalidation submits the verbatim calldata bytes (a repack diverges from the on-chain `attestationsHash` and reverts). Plus unit coverage for the strict attestation-info classification, the byte-faithful passthrough, and the result round-trip. An attester-side variant (a committee member emitting yParity attestations) is not separately tested: the honest proposer's `orderAttestations` normalization (A-1351) canonicalizes the byte before the L1 bundle, so it never reaches L1 and produces the same on-chain outcome as — and is subsumed by — the malicious-proposer case above. Fixes A-1401
## Problem `l1_tx_utils.test.ts` flaked in CI ([log](http://ci.aztec-labs.com/390c881d64e094b6), seen on [#24454](#24454 (comment)), which is unrelated to the failure). The failing test was `monitors all sent txs`: ``` ● L1TxUtils › L1TxUtils with blobs › monitors all sent txs TimeoutError: L1 transaction 0x82cf...aa3a timed out at TestL1TxUtils.monitorTransaction (l1_tx_utils/l1_tx_utils.ts:601:11) ``` ## Root cause The test creates the monitor promise and only attaches its rejection expectation after several intervening awaits: ```ts const monitorPromise = gasUtils.monitorTransaction(state); await sleep(100); await cheatCodes.mineEmptyBlock(); await expect(monitorPromise).rejects.toThrow('timed out'); // handler attached here ``` Anvil timestamps have 1s granularity, so when the wall clock crosses a second boundary between the send and the monitor's timeout check, the monitor (with `txTimeoutMs: 200`, `checkIntervalMs: 100`) can observe the L1 timestamp already 1s past `sentAt` and reject **before** `mineEmptyBlock()` completes. In the failing run the timeout fired at `16:41:20.355`, before the empty block was mined at `.356` and before the `.rejects` handler attached at `.357`. Node emitted `unhandledRejection` in that window and jest 30 reported it as the test failure — which is why the failure stack has no test-file frame and the test's own assertions all passed in the log. The same file already uses the safe idiom in a dozen newer tests: attach the handler synchronously with `.catch(err => err)` and assert via `resolves.toBeInstanceOf(TimeoutError)`. ## Fix Apply that established pattern to the three remaining tests that expect a monitor timeout but attach the handler only after intervening awaits: - `stops trying after timeout once block is mined` - `attempts to cancel timed out transactions` - `monitors all sent txs` (the observed flake) Test-only change; no product code touched. `TimeoutError` is exactly what `monitorTransaction` throws on timeout, so the assertions are equivalent-or-stricter than the previous message matching. Evidence: CI log http://ci.aztec-labs.com/390c881d64e094b6 (search for `monitors all sent txs`). --- *Created by [claudebox](https://claudebox.work/v2/sessions/0959caa9f03f59fa) · group: `slackbot`*
…24546) ## Problem `e2e_ha_full.parallel.test.ts` ("should produce blocks with HA coordination and attestations") failed in CI on an unrelated PR (#24492) before the test even started: Docker Hub returned a transient 502 while `docker compose up` pulled `postgres:16-alpine` for the HA compose stack. CI log: http://ci.aztec-labs.com/cac25b9251c99496 ``` postgres Error unknown: failed to resolve reference "docker.io/library/postgres:16-alpine": unexpected status from HEAD request to https://registry-1.docker.io/v2/library/postgres/manifests/16-alpine: 502 Bad Gateway ``` This can hit any compose-based test (`run_compose_test` is shared by the e2e compose/web3signer/ha suites and docs examples) whenever a required image is not in the local Docker cache and the registry blips. ## Fix Wrap the `docker compose up -d --force-recreate` in `ci3/run_compose_test` with the existing `ci3/retry -p <regex>` helper, retrying only failures that match transient registry/network errors (5xx, HEAD-request failures, TLS/connection timeouts, rate limiting). This reuses the same pattern-gated retry convention already used for network flakes in `l1-contracts/bootstrap.sh`, `barretenberg/sol/bootstrap.sh`, and `noir-projects/aztec-nr/bootstrap.sh`, so genuine failures (e.g. an image tag that doesn't exist) still fail immediately without retrying. Retrying `up -d --force-recreate` is idempotent: a retry recreates any containers from a partially failed attempt. Verified the exact error message from the CI log matches the retry pattern (retried, then succeeds), while a `manifest not found` failure exits immediately without retries. No tracked issue exists for this flake. --- *Created by [claudebox](https://claudebox.work/v2/sessions/0959caa9f03f59fa) · group: `slackbot`* --------- Co-authored-by: Santiago Palladino <santiago@aztec-labs.com>
…uplicate_proposal test (#24557) ## Problem `multi-node/slashing/duplicate_proposal › slashes validator who sends duplicate proposals` (`yarn-project/end-to-end/src/multi-node/slashing/equivocation_offenses.test.ts`) flaked in CI on the unrelated PR #24556 with: ``` Target proposer 0x90f79bf6eb2c4f870365e785982e1f101e93b906 not found in any slot after 20 epoch attempts ``` Evidence: http://ci.aztec-labs.com/eb0b4e658e28440c (the sibling `duplicate_attestation` case in the same file passed in the same run). ## Root cause Pure statistics — no logic bug. `advanceToEpochBeforeProposer` (`yarn-project/end-to-end/src/multi-node/slashing/setup.ts` on this branch) scans upcoming epochs for one where the target validator is the proposer, giving up after `maxAttempts = 20` epochs. The test calls it with the default. This suite uses a 2-slot epoch with `warmupSlots = 1`, so each epoch attempt inspects exactly **one** candidate slot. The proposer for a slot is `keccak256(abi.encode(epoch, slot, seed)) % committeeSize`, and with the suite's 4-member committee (`COMMITTEE_SIZE = NUM_VALIDATORS = 4` in `setup.ts`) each attempt is an independent 1/4 draw. Miss probability over 20 attempts: `(3/4)^20 ≈ 0.32%`, i.e. roughly 1 in 315 runs — matching the observed occasional flake. ## Fix Raise the default `maxAttempts` from 20 to 50, bounding the miss probability at `(3/4)^50 ≈ 5.7e-7` (~1 in 1.8M). Each attempt is only a committee query plus an anvil timestamp warp, so the expected attempt count stays ~4 and the extra headroom costs essentially nothing. The raised default also covers this helper's other callers with the same exposure (`broadcasted_invalid_block_proposal_slash`, `broadcasted_invalid_checkpoint_proposal_slash`), none of which override `maxAttempts`. ## Relationship to PR #24548 This is the same failure mode fixed by #24548 for the `duplicate_attestation` flake, but #24548 does **not** cover this call site: its patch touches `yarn-project/end-to-end/src/e2e_p2p/shared.ts`, a file that does not exist on `merge-train/spartan-v5` (on this branch the helper lives in `multi-node/slashing/setup.ts`, and there is no `e2e_p2p/` directory). #24548's head branch was cut from a `next`-layout tree while targeting `merge-train/spartan-v5`, which is why GitHub reports it as dirty with ~4k changed files — it needs a rebase onto the branch it targets, at which point its fix would land in this same file. This PR applies the fix directly to the `merge-train/spartan-v5` copy of the helper. No tracked issue exists for this flake. --- *Created by [claudebox](https://claudebox.work/v2/sessions/0959caa9f03f59fa) · group: `slackbot`*
…test (#24543) ## Flake `multi-node/block-production/multi_validator_node.parallel.test.ts`, case "should build blocks & attest with multiple validator keys", failed on [#24537](#24537) (unrelated PR) with: ``` TypeError: Cannot read properties of undefined (reading 'checkpoint') at checkpoint (multi-node/block-production/multi_validator_node.parallel.test.ts:92:73) ``` CI log: http://ci.aztec-labs.com/0f321ffb9fa923af ## Root cause `deployContractAndGetAttestedCheckpoint` reads the published checkpoint from the archiver immediately after the deploy tx receipt turns mined: ```ts const [publishedCheckpoint] = await dataStore.getCheckpoints({ from: blockData!.checkpointNumber, limit: 1 }); const payload = ConsensusPayload.fromCheckpoint(publishedCheckpoint.checkpoint, ...); // undefined here ``` The receipt turns mined as soon as the block is built locally, but the archiver only stores the *published* checkpoint (with its attestations, which come from the L1 `propose` calldata) once its L1 sync downloads that tx. In the window between the two, `getCheckpoints` returns `[]` and `publishedCheckpoint` is `undefined`. The CI log shows the race directly: the test node published checkpoint 1 to L1 at `15:17:57.694` (`sequencer:publisher ... Published checkpoint 1 at slot 14`), and its archiver logged `Downloaded checkpoint 1` at `15:17:57.795` — by which point the test had already failed and teardown was running. This is a test-side race, not a production bug: the archiver API is behaving as documented for a not-yet-synced checkpoint. ## Fix Poll for the checkpoint with `retryUntil` instead of reading it once, mirroring the existing pattern in `single-node/cross-chain/cross_chain_messaging_test.ts` (`advanceToEpochProven`), which wraps the same lookup in `retryUntil(..., 'archiver indexes checkpoint N', 120, 0.5)`. Note: the `next`-line variant of this test (`e2e_multi_validator/e2e_multi_validator_node.test.ts`) has the same unguarded lookup in two places and could flake the same way; that file is on a different base branch, so it is not touched here. Based on `merge-train/spartan-v5` because the flaking file only exists on the v5 line (it was consolidated into `multi-node/` there), and the failing run was on a PR targeting that branch. --- *Created by [claudebox](https://claudebox.work/v2/sessions/0959caa9f03f59fa) · group: `slackbot`*
…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`*
…ts during shutdown (v5 line) (#24560) 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 `next` — `l1_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](https://claudebox.work/v2/sessions/0959caa9f03f59fa) · group: `slackbot`*
AztecBot
enabled auto-merge
July 7, 2026 03:02
Collaborator
Author
|
🤖 Auto-merge enabled after 4 hours of inactivity. This PR will be merged automatically once all checks pass. |
Collaborator
Author
|
🤖 Auto-merge enabled after 4 hours of inactivity. This PR will be merged automatically once all checks pass. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
BEGIN_COMMIT_OVERRIDE
perf(e2e): warp proven-checkpoint waits, tighten in-process polling, and instrument setup spans (#24452)
fix: reject aztecSlotDuration not a multiple of ethereumSlotDuration (#24481)
fix(config): fail loudly on invalid or fractional numeric config (#24536)
fix(sequencer): invalidate malicious yParity attestation checkpoints (A-1401) (#24537)
fix(ethereum): deflake monitor timeout tests in l1_tx_utils (#24545)
fix(ci): retry docker compose up on transient registry pull failures (#24546)
fix(e2e): resolve flaky proposer-not-found in equivocation_offenses duplicate_proposal test (#24557)
fix(e2e): resolve flaky checkpoint TypeError in multi_validator_node test (#24543)
fix(ethereum): prevent unhandled L1 rejections from tx monitor loops during shutdown (v5 line, partial) (#24551)
fix(sequencer): prevent unhandled rejections from in-flight L1 requests during shutdown (v5 line) (#24560)
END_COMMIT_OVERRIDE