Skip to content

ci(l1,l2): make the cargo cache restorable, and cut duplicate builds in the L2 dev test - #7029

Merged
edg-l merged 8 commits into
mainfrom
ci/speed-up-l2-dev-integration-test
Aug 3, 2026
Merged

edg-l merged 8 commits into
mainfrom
ci/speed-up-l2-dev-integration-test

Conversation

@edg-l

@edg-l edg-l commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Nothing in this repo restores a cargo cache. Every job logs No cache found, including on main, so every CI leg builds from scratch. The Test - ubuntu-22.04 leg of the last completed L1 run spent 29 minutes on that, and the L2 Dev integration test 44 minutes, of which ~36 was rustc (21 min building ethrex, 15.5 min building the test binary, 6.5 min actually testing).

Description

The cache is never readable. An entry saved on a refs/pull/N/merge ref can only be restored by that same PR; ?ref=refs/heads/main returns count=0, so there is nothing on a ref that other branches may read. setup-rust now saves only on main.

Nothing fits in the budget either. Counting matrix legs, 32 setup-rust calls run on main across six workflows, and rust-cache keys per job, so that is 32 writers at 1.2 to 2.75 GB each. The default budget is 10 GB with LRU eviction (admin-configurable, billed above that); actions/cache/usage currently reports 17 entries totalling 31.37 GiB. Either way 32 writers do not coexist, so main would thrash exactly as PR refs do now.

setup-rust gains cache-pool, forwarded to rust-cache's shared-key, which replaces the per-job segment of the key. Four pools, each with exactly one writer, picked as the widest dependency set in it:

pool writer serves
l1 pr-main_l1::test root workspace at dev, plus tooling
l1-reorg pr-main_l1::reorg-tests root workspace at release-fast
l2 pr-main_l2::integration-test -F l2,l2-sql at release
prover main_prover::test the SP1 host stack

Everything else restores only, so a lean job cannot overwrite an entry with a subset and clippy-only target dirs never claim a slot. reorg-tests gets its own pool because it is the only job on main building the root workspace at release-fast, which the dev-profile l1 entry cannot serve. That is 6 entries (the l1 writer is a 3-platform matrix) instead of 32.

The second workspace was never cached. rust-cache's workspaces defaults to the repo root, and tooling is a separate workspace with its own target dir, so the ef_tests, reorgs, loc and hive_report builds were all cold regardless of anything above. That includes a step inside the l1 writer's own job, since pr-main_l1::test runs make -C tooling/ef_tests/blockchain test. setup-rust now lists both workspaces. Entries get bigger for it; the count does not change.

Writing is opt-in: save-cache defaults to 'false' and only the four jobs above pass 'true'. It has to be opt-in rather than opt-out, because schedule and workflow_dispatch have no head ref of their own and fire on the default branch, so github.ref is refs/heads/main and save-if evaluates true. daily_loc_report and daily_hive_report were writing the l1 entry nightly on that path, and deep_reorg, pr_lint_readme, pr_perf_levm and pr_perf_build_block_bench could. Cache keys are immutable, so the first of those to save wins and the pool owner's richer save is skipped, not merged.

The remaining PR-only workflows (pr_loc, pr_check_l2_genesis, pr_perf_blocks_exec) get a pool name too. They restore only; naming a pool is what makes main's entry readable for them, at no storage cost.

The L2 test binary was a near total duplicate of the node build. cargo fingerprints include the resolved feature set, and the two invocations diverged on 18 shared packages, among them sha3, tokio, hyper and idna, which sit low enough in the graph that the second build restarted from libc. Most of it came from ethrex-test's l2 feature being an empty marker while the crate dev-depends on ethrex with default features, so cmd/ethrex and the whole L2 stack compiled twice in different configurations. Forwarding l2 to ethrex/l2 and ethrex/l2-sql brings the divergence down to 6 packages and makes cmd/ethrex resolve identically in both builds. What still rebuilds is the three crates ethrex-test turns testing/test-utils on for, plus their dependents.

Three other jobs (main_prover, and two in pr-main_l2) prebuilt the test binary without --features l2 and then ran with it. Harmless while the feature was empty, but now it flips the whole L2 stack config, so they get the flag too.

The L2 build was hidden inside the backgrounded cargo run. A compile error did not fail the job, it let the readiness loop poll for 25 min first, and the test binary was compiled while the node and prover were competing for the same 4 cores. Both binaries are now built in their own steps before the node starts, and the job uses release-fast instead of release. The test waits on 15s batch commits and L1 confirmations rather than on EVM throughput, so a slightly slower binary costs very little here.

The deposit test raced the dev pre-funding. Removing the implicit sleep exposed a pre-existing bug. A node started with --dev funds the rich accounts by depositing half of each L1 balance through the bridge, and the deployer awaits only the L1 receipts; the credits reach L2 through the L1 watcher a batch later. The port-3900 readiness gate opens ~20s before that, a gap the test binary's own compile time used to cover. So the suite read a zero initial L2 balance and test_deposit failed by exactly the 5e26 wei pre-funding. l2_integration_test now polls every test account's balance until it is non-zero before asserting any delta, which fixes local make init runs too.

Expect no improvement on this PR itself, since main has to run once to populate the pools.

Not in this PR

Jobs building the root workspace at plain release (the pr_perf_* benchmarks, deep_reorg) have no writer on main, so they restore ~/.cargo but no matching target dir. Same for revm_comparison, which is excluded from the workspace and has a third target dir. Giving them writers costs entries, and the budget is not measurable from outside the org.

aligned-sdk is a non-optional dependency of ethrex-l2 and pulls the whole SP1 host stack (~4 min of the critical path) into every -F l2 build even with no zkVM backend enabled. Gating it behind a feature needs config and release-plumbing changes.

Checklist

  • Updated STORE_SCHEMA_VERSION (crates/storage/lib.rs) if the PR includes breaking changes to the Store requiring a re-sync. N/A: CI and feature wiring only.

@edg-l
edg-l requested a review from a team as a code owner July 27, 2026 12:48
@github-actions github-actions Bot added L1 Ethereum client L2 Rollup client labels Jul 27, 2026
@github-actions

github-actions Bot commented Jul 27, 2026 •

Copy link
Copy Markdown

⚠️ Known Issues — intentionally skipped tests

Source: docs/known_issues.md

Stateless (zkEVM) Amsterdam+ EF tests skipped

Where: tooling/ef_tests/blockchain/test_runner.rs — parse_and_execute skips
fixtures with network >= Fork::Amsterdam when running with a stateless backend.
Affects make test-stateless (the vectors_zkevm/ run); make test-levm is
unaffected.

Why: The stateless run uses the tests-zkevm@v0.5.0 bundle, filled against
glamsterdam-devnet v6.1.0, which predeploys the EIP-8282 builder deposit/exit
contracts at the OLD addresses (0x0000884d…d9008282 / 0x000014574a…0f008282).
This client uses the devnet-7 addresses (0x0000bff4…300d8282 /
0x000064d6…800e8282, matching the live tests-glamsterdam-devnet@v7.2.0 bundle
used by make test-levm). Every Amsterdam+ block runs the end-of-block EIP-8282
builder system call; with the new addresses absent from the v0.5.0 bundle, each
stateless Amsterdam+ block fails with
SystemContractCallFailed("System contract: 0x0000…8282 has no code after deployment").
The skip is by fork rather than by test name, since cross-fork directories such as
for_amsterdam/prague/... still execute at the Amsterdam fork.

Removal: Delete the skip_stateless_amsterdam branch in parse_and_execute
once a tests-zkevm bundle filled with the devnet-7 builder predeploy addresses is
released and .fixtures_url_zkevm is bumped to it.

@greptile-apps

greptile-apps Bot commented Jul 27, 2026

Copy link
Copy Markdown

Greptile Summary

This PR streamlines Rust caching and L2 integration-test builds.

  • Restricts Cargo cache writes to main while retaining cache restores on other refs.
  • Aligns the ethrex-test L2 feature with the node's l2 and l2-sql features.
  • Prebuilds the node and integration test with the release-fast profile before starting services.
  • Updates affected prover and L2 workflows to build tests with the L2 feature enabled.
  • Shortens the L2 readiness polling interval while keeping a ten-minute startup window.

Confidence Score: 5/5

The PR appears safe to merge with no actionable defects identified in the changed build and workflow paths.

The cache policy, feature forwarding, build profiles, binary paths, and test invocations are internally aligned across the changed workflows.

Important Files Changed

Filename Overview
.github/actions/setup-rust/action.yml Restricts Rust cache persistence to the main branch to reduce cache eviction.
.github/workflows/main_prover.yaml Builds the prover integration test with the newly forwarded L2 feature set.
.github/workflows/pr-main_l1_l2_dev.yaml Prebuilds release-fast binaries, starts the prebuilt node, and uses shorter readiness polling.
.github/workflows/pr-main_l2.yaml Adds the L2 feature to prebuilds in the standard and shared-bridge integration jobs.
test/Cargo.toml Forwards the ethrex-test L2 feature to ethrex's l2 and l2-sql features.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Main[main workflow] -->|save and restore| Cache[Cargo cache]
  PR[PR workflow] -->|restore only| Cache
  BuildNode[Build ethrex release-fast] --> StartNode[Start L2 node]
  BuildTest[Build L2 test release-fast] --> RunTest[Run integration test]
  StartNode --> Ready[Wait for proof coordinator]
  Ready --> RunTest
Loading

Reviews (1): Last reviewed commit: "ci(l1,l2): fix cargo cache eviction and ..." | Re-trigger Greptile

@github-actions

Copy link
Copy Markdown

🤖 Kimi Code Review

The PR improves CI efficiency and correctness, but one change introduces potential flakiness.

Critical: Timeout Reduction Risk

  • File: .github/workflows/pr-main_l1_l2_dev.yaml, lines 84-90
  • Issue: The health check loop changed from 100 iterations × 15s sleep (25 min timeout) to 120 × 5s (10 min timeout). While faster feedback is desirable, 10 minutes may be insufficient for CI runners under load or when compiling contracts cold, causing intermittent job failures.
  • Suggestion: Increase to 150 or 180 iterations (12.5–15 min) to retain faster feedback while avoiding flakiness, or make the sleep duration configurable via env var.

Correctness & Best Practices

  1. Feature Flag Propagation (Good)

    • File: test/Cargo.toml, lines 12-15
    • The l2 feature now properly unifies with ethrex/l2 and ethrex/l2-sql. This ensures the test binary reuses artifacts from the node build instead of recompiling the L2 stack, saving significant CI time. The explanatory comment is excellent.
  2. Cache Optimization (Good)

    • File: .github/actions/setup-rust/action.yml, lines 26-32
    • Restricting cache writes to main prevents PR jobs from thrashing the 10GB Actions cache budget. This is a correct application of GitHub Actions cache semantics.
  3. Build/Run Separation (Good)

    • File: .github/workflows/pr-main_l1_l2_dev.yaml, lines 64-70
    • Pre-building binaries with COMPILE_CONTRACTS=1 ensures compilation errors surface immediately rather than after node startup timeouts, and prevents resource contention during test execution.
  4. Profile Consistency

    • File: .github/workflows/pr-main_l1_l2_dev.yaml, lines 65, 70, 74, 108, 110
    • Consistent use of --profile release-fast across build, start, and test steps is correct. Verify that release-fast is defined in the workspace Cargo.toml (assumed present

Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt

@github-actions

Copy link
Copy Markdown

🤖 Codex Code Review

No findings.

The patch is limited to CI/workflow behavior and ethrex-test feature forwarding in test/Cargo.toml. The new l2 = ["ethrex/l2", "ethrex/l2-sql"] wiring matches the node build flags used in the updated workflows, so it should improve artifact reuse without changing runtime semantics of the Ethereum client itself. I did not see changes touching EVM execution, gas accounting, consensus, trie/storage, RLP, or transaction/block validation logic.

Residual risk: this was a static review only. I could not run local Cargo verification in this sandbox because rustup tried to write under a read-only home path, so CI is the real validation point for the release-fast and cache behavior changes.


Automated review by OpenAI Codex · gpt-5.4 · custom prompt

Comment thread test/Cargo.toml
- name: Wait for ethrex L2
run: |
for i in {1..100}; do
for i in {1..120}; do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, and flagging it because the opposite change was suggested elsewhere on this PR: this loop is not the flakiness risk it looks like. In the latest run the Wait for ethrex L2 step completed in 5 seconds (15:24:17 → 15:24:22), so 120 × 5 s is roughly 120× the observed startup need. Raising the iteration count would only lengthen the failure path for a genuinely dead node; the 25 min budget it replaces was sized when this step was also absorbing the node's compile time, which now happens in its own step above.

(Relevant to the deposit failure too: if that turns out to be startup-timing related, a longer port poll won't help — port 3900 opens in 5 s either way. The gate would need to be on the actual precondition.)

Comment thread .github/actions/setup-rust/action.yml Outdated
@github-project-automation github-project-automation Bot moved this to Requires Changes in ethrex_l2 Jul 29, 2026
@github-project-automation github-project-automation Bot moved this to In Progress in ethrex_l1 Jul 29, 2026
edg-l added 3 commits August 3, 2026 11:13
Per-job keys put 32 cache writers on main across the six workflows that
trigger there, at 1.2-2.75GB each. Group them into l1/l2/prover pools with
one writer per pool so the budget holds the entries it is given.
These never run on main, so under save-if they never write a key and, with
per-job keys, never read one either. Naming a pool is what makes main's entry
restorable for them.
@edg-l edg-l changed the title ci(l1,l2): fix cargo cache eviction and duplicate builds in L2 dev integration test ci(l1,l2): make the cargo cache restorable, and cut duplicate builds in the L2 dev test Aug 3, 2026
@edg-l
edg-l requested a review from ElFantasma August 3, 2026 09:21
rust-cache's default workspaces covers only the repo root, so tooling/target
was never cached, including the ef_tests build inside the l1 pool writer's own
job. reorg-tests is the only main job building the root workspace at
release-fast, so the dev-profile l1 entry cannot serve it.
@github-actions

github-actions Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

Benchmark Results Comparison

Benchmark Results: MstoreBench

Command Mean [s] Min [s] Max [s] Relative
main_revm_MstoreBench 264.0 ± 7.7 257.4 277.3 1.35 ± 0.04
main_levm_MstoreBench 195.6 ± 1.3 194.0 198.4 1.00
pr_levm_MstoreBench 239.7 ± 138.2 193.6 632.9 1.23 ± 0.71
Detailed Results

Benchmark Results: BubbleSort

Command Mean [s] Min [s] Max [s] Relative
main_revm_BubbleSort 3.073 ± 0.029 3.016 3.117 1.17 ± 0.01
main_levm_BubbleSort 2.629 ± 0.012 2.614 2.652 1.00
pr_revm_BubbleSort 2.981 ± 0.027 2.956 3.050 1.13 ± 0.01
pr_levm_BubbleSort 2.639 ± 0.025 2.608 2.695 1.00 ± 0.01

Benchmark Results: ERC20Approval

Command Mean [s] Min [s] Max [s] Relative
main_revm_ERC20Approval 1.015 ± 0.012 1.003 1.039 1.04 ± 0.02
main_levm_ERC20Approval 0.973 ± 0.013 0.960 1.004 1.00
pr_revm_ERC20Approval 0.983 ± 0.005 0.973 0.989 1.01 ± 0.01
pr_levm_ERC20Approval 0.979 ± 0.009 0.968 0.992 1.01 ± 0.02

Benchmark Results: ERC20Mint

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Mint 136.2 ± 1.1 134.9 137.8 1.03 ± 0.01
main_levm_ERC20Mint 150.5 ± 1.8 148.5 154.0 1.14 ± 0.01
pr_revm_ERC20Mint 132.4 ± 0.6 131.3 133.3 1.00
pr_levm_ERC20Mint 150.1 ± 0.8 149.0 151.6 1.13 ± 0.01

Benchmark Results: ERC20Transfer

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Transfer 236.9 ± 4.5 231.7 248.1 1.02 ± 0.03
main_levm_ERC20Transfer 246.3 ± 1.6 242.6 248.4 1.06 ± 0.03
pr_revm_ERC20Transfer 232.3 ± 5.6 229.1 247.5 1.00
pr_levm_ERC20Transfer 249.0 ± 3.1 245.2 255.5 1.07 ± 0.03

Benchmark Results: Factorial

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Factorial 225.7 ± 3.9 222.1 236.0 1.01 ± 0.02
main_levm_Factorial 258.1 ± 1.1 257.1 260.8 1.16 ± 0.01
pr_revm_Factorial 222.5 ± 0.5 221.9 223.4 1.00
pr_levm_Factorial 262.7 ± 7.8 258.6 283.5 1.18 ± 0.04

Benchmark Results: FactorialRecursive

Command Mean [s] Min [s] Max [s] Relative
main_revm_FactorialRecursive 1.598 ± 0.036 1.557 1.667 1.01 ± 0.04
main_levm_FactorialRecursive 9.315 ± 0.023 9.272 9.345 5.88 ± 0.17
pr_revm_FactorialRecursive 1.585 ± 0.045 1.481 1.651 1.00
pr_levm_FactorialRecursive 9.342 ± 0.087 9.246 9.506 5.90 ± 0.17

Benchmark Results: Fibonacci

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Fibonacci 205.1 ± 1.0 203.7 206.6 1.02 ± 0.01
main_levm_Fibonacci 230.0 ± 14.8 218.6 266.2 1.14 ± 0.07
pr_revm_Fibonacci 200.9 ± 0.8 199.0 202.1 1.00
pr_levm_Fibonacci 224.2 ± 6.2 217.0 238.2 1.12 ± 0.03

Benchmark Results: FibonacciRecursive

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_FibonacciRecursive 837.6 ± 8.6 821.4 849.5 1.15 ± 0.02
main_levm_FibonacciRecursive 734.4 ± 8.9 725.0 754.4 1.01 ± 0.01
pr_revm_FibonacciRecursive 842.6 ± 8.6 831.0 861.0 1.16 ± 0.02
pr_levm_FibonacciRecursive 725.8 ± 5.8 716.1 733.4 1.00

Benchmark Results: ManyHashes

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ManyHashes 8.7 ± 0.1 8.6 8.8 1.03 ± 0.01
main_levm_ManyHashes 9.4 ± 0.1 9.3 9.5 1.11 ± 0.01
pr_revm_ManyHashes 8.5 ± 0.0 8.4 8.5 1.00
pr_levm_ManyHashes 9.4 ± 0.1 9.3 9.6 1.11 ± 0.01

Benchmark Results: MstoreBench

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_MstoreBench 264.0 ± 7.7 257.4 277.3 1.35 ± 0.04
main_levm_MstoreBench 195.6 ± 1.3 194.0 198.4 1.00
pr_revm_MstoreBench 260.2 ± 2.6 258.3 266.7 1.33 ± 0.02
pr_levm_MstoreBench 239.7 ± 138.2 193.6 632.9 1.23 ± 0.71

Benchmark Results: Push

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Push 286.4 ± 1.0 285.3 288.2 1.18 ± 0.01
main_levm_Push 242.3 ± 0.8 241.0 243.5 1.00
pr_revm_Push 287.6 ± 1.1 286.5 289.6 1.19 ± 0.01
pr_levm_Push 243.8 ± 3.1 242.0 252.1 1.01 ± 0.01

Benchmark Results: SstoreBench_no_opt

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_SstoreBench_no_opt 168.4 ± 11.4 162.9 200.5 1.64 ± 0.11
main_levm_SstoreBench_no_opt 102.8 ± 0.4 102.3 103.7 1.00 ± 0.00
pr_revm_SstoreBench_no_opt 167.6 ± 2.5 164.1 173.8 1.63 ± 0.02
pr_levm_SstoreBench_no_opt 102.7 ± 0.3 102.3 103.2 1.00

@ElFantasma ElFantasma left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up on the pooling commits: three main-running workflows are missing save-cache: 'false', so they claim the l1 pool slot the one-writer design reserves.

Comment thread .github/workflows/daily_loc_report.yaml
Comment thread .github/workflows/daily_hive_report.yaml
Comment thread .github/workflows/deep_reorg.yaml
edg-l added 2 commits August 3, 2026 14:24
A job that omitted save-cache became a pool writer, and workflow_dispatch
and schedule runs carry refs/heads/main, so daily_loc_report,
daily_hive_report, deep_reorg, pr_lint_readme, pr_perf_levm and
pr_perf_build_block_bench all claimed the l1 slot with a target dir far
narrower than the pool's owner needs. Cache keys are immutable, so the
first of those to save wins and the owner's later save is skipped.

save-cache now defaults to 'false' and the four pool owners opt in.
A node started with --dev funds the rich accounts by depositing half of
each L1 balance through the bridge, and the deployer awaits only the L1
receipts. The credits reach L2 through the L1 watcher a batch later, so
the suite read a zero initial balance and test_deposit failed by exactly
the 5e26 wei pre-funding.

The port-3900 readiness gate opens ~20s before those credits land, which
the test binary's own compile time used to cover.
@github-project-automation github-project-automation Bot moved this from In Progress to In Review in ethrex_l1 Aug 3, 2026
@edg-l
edg-l enabled auto-merge August 3, 2026 13:36
@edg-l edg-l moved this from Requires Changes to In Review in ethrex_l2 Aug 3, 2026
@edg-l
edg-l added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit 0cc4572 Aug 3, 2026
94 checks passed
@edg-l
edg-l deleted the ci/speed-up-l2-dev-integration-test branch August 3, 2026 14:47
@github-project-automation github-project-automation Bot moved this from In Review to Done in ethrex_l2 Aug 3, 2026
@github-project-automation github-project-automation Bot moved this from In Review to Done in ethrex_l1 Aug 3, 2026
ilitteri added a commit that referenced this pull request Aug 20, 2026
…shared-bridge test (#7151)

**Motivation**

`Integration Test Shared Bridge - Normal` fails intermittently with
`Receiver balance did not increase correctly` at
`test/tests/l2/shared_bridge.rs:409`. It hit `main` on Aug 3
(`0cc4572c5`) and Aug 13 (`f2998963d`), and `release/v24.0.0` on Aug 12
(`8f3d2ca05`), always with the same message hash `0x66867250…5be951` —
so the test is not wrong about what it expects, the message genuinely
never arrives. In the failing runs L2B never commits batch 1, its L2→L2
message never becomes pending on the router, and L2A's watcher logs `L2
message not found in pending messages` 153 times with 0 verified.

Three separate defects combine to produce that:

**1. Both sequencers commit from the same L1 account.**
`crates/l2/docker-compose.yaml` gives `ethrex_l2`
`--committer.l1-private-key 0x385c…` and the shared-bridge override gave
`ethrex_l2_b` the same key (likewise the proof-coordinator key). They
post to one L1 on the same 15 s timer, so their commit loops race for
the same nonce and the loser is rejected — first as an underpriced
replacement, then as a too-low nonce.

**2. The loser cannot recover, which is a bug beyond CI.**
`bump_gas_generic_tx` computed the blob fee as `1 + (bump_percentage /
100) * 10` and then divided by ten. For the only bump either call site
passes, `30`, that evaluates to `fee * 1 / 10`: **every retry lowered
`max_fee_per_blob_gas` by 90%**, so a batch commitment could never
re-price itself — on CI or on a real L1 during a fee spike. Blob
replacements also require the fee to *double*
(`DEFAULT_BLOB_PRICE_BUMP_PERCENT`), and that threshold applies to the
fee cap and the tip as well, so bumping those by 30% could not have
replaced a blob transaction either. Separately, the retry loop never
noticed when the nonce had become unreachable, so one `NonceTooLow`
burned all ten attempts in ~3.5 s and reported "max retries reached",
hiding the cause.

**3. The test slept a fixed 180 s and then asserted**, which made the
result depend on whatever else the job was doing. The first failure in
this job's history is the run of #7029, which aligned the `cargo test
--no-run` prebuild's features with the run step so the test binary is no
longer rebuilt inside it. Job step timestamps show the window between
"Start Sequencer L2B" and the assertion collapsing from **2 m 35 s to 3
s**, moving the assertion onto the sequencers' cold start — where batch
1 carries all 192 privileged deposit mints and the two co-nonced
committers collide from their first commit. Worth noting the direction:
the passing Jul 22 run had *more* nonce collisions (72 underpriced + 76
too-low) than any failing run, because they happened before the test
looked.

**Description**

- **L2B gets its own committer and proof-coordinator accounts, and its
deployment authorizes them.** Changing the keys alone is not enough:
`Timelock.commitBatch` is `onlyRole(SEQUENCER)`, and the role is granted
from the deployer's `--committer.l1-address` /
`--proof-sender.l1-address`, which both default to L2A's accounts.
Without the matching addresses every L2B `commitBatch` reverts at gas
estimation as an unauthorized caller — the same end symptom, reached a
different way. Neither option is forwarded to the deployer container by
`docker-compose.yaml`, so they are set on the override's
`contract_deployer`, leaving L2A's deployment on the defaults.
- **`bump_gas_generic_tx` raises every fee dimension** by a bump that
clears the mempool's replacement threshold for the transaction's
category — `BLOB_FEE_BUMP_PERCENT` (100) for blob-carrying transactions,
`FEE_BUMP_PERCENT` (30) otherwise — using saturating arithmetic so an
absurd starting fee cannot overflow.
- **The retry loop stops spending attempts that cannot succeed:** it
abandons the transaction with `UnreachableNonce` once the chain's nonce
has passed it (checked against the chain, not against the rejection
text, which differs between clients), and reports the reason when the
`maximum_allowed_max_fee_per_gas` / `_blob_gas` clamps leave a bid the
mempool already rejected.
- **Every shared-bridge assertion now waits for its own condition**
instead of sharing one fixed sleep, with a timeout more generous than
the 180 s it replaces. The L1 deposits accounting is waited on
separately rather than assumed settled once the L2B credit lands: the
two are updated by different transactions on different chains and
nothing orders them. The forced-inclusion test's expiry and verification
waits are deliberately left as sleeps — they are waiting for something
*not* to happen.
- **Hardening:** a test pins L2B's private keys to the addresses its
deployment authorizes, so drift between the two blocks of the override
file fails there instead of surfacing at runtime as an ABI-undecodable
`AccessControlUnauthorizedAccount` revert and a chain that silently
stops committing. Unit tests cover both replacement thresholds,
monotonicity across ten retries, saturation, and — so they cannot
silently stop discriminating — that the previous blob formula fails
them.

This is deliberately **not** a revert of #7029. That PR removed an
accidental delay which had been masking defects 1 and 2 since the
shared-bridge test landed; restoring the delay would hide them again.

**Also worth knowing, not changed here:** this job is gated
`github.event_name != 'merge_group'`, so the merge queue never runs it
and a PR can land without it ever executing against the merged result.
Combined with the paths filter, it effectively only runs on pushes to
main — which is why a ~3% flake sat unnoticed for weeks.

**Checklist**

- [x] Updated `STORE_SCHEMA_VERSION` (crates/storage/lib.rs) if the PR
includes breaking changes to the `Store` requiring a re-sync. — n/a, no
`Store` changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L1 Ethereum client L2 Rollup client

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants