Skip to content

perf(e2e): overlap and batch setup txs in e2e harnesses#24569

Open
spalladino wants to merge 3 commits into
spl/e2e-genesis-fund-sponsored-fpcfrom
spl/e2e-overlap-setup-txs
Open

perf(e2e): overlap and batch setup txs in e2e harnesses#24569
spalladino wants to merge 3 commits into
spl/e2e-genesis-fund-sponsored-fpcfrom
spl/e2e-overlap-setup-txs

Conversation

@spalladino

@spalladino spalladino commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Stacked on #24564; retarget to merge-train/spartan-v5 after it merges. (Unifies former #24584 into this PR — the two touched the same mint path in fees_test.ts, so they now land together.)

Restructures the serial setup-tx chains in the fees and cross-chain e2e harnesses so independent setup transactions stop paying one production slot each: independent txs run concurrently, and same-sender calls are batched into a single tx (one proof, one tx, one slot). Part of the round-4/5 e2e speedup effort.

Commits

  • perf(e2e): overlap fees harness setup txs — after FeesTest.setup deploys BananaCoin, the BananaFPC deploy, the SponsoredFPC registration (a PXE registration, no L2 tx), and Alice's banana mints each depend only on the token, so the fees suites (account_init, gas_estimation, private_payments) now run them under a Promise.all. The cross-chain harness (deployAndInitializeTokenAndBridgeContracts) deploys the L2 token and its bridge concurrently: the bridge takes the token address as a constructor arg, but that address is deterministic before the deploy tx mines (the deploy is now constructed with an explicit deployer, same address derivation as the previous send-time lock), and the bridge constructor only stores it without calling into the token.
  • perf(e2e): parallelize dual token mints + perf(e2e): batch same-sender setup txs into single txsapplyFundAliceWithBananas sent Alice's private and public banana mints back-to-back (measured 100% serial: tx:mint busyMs == totalMs on the base). The first commit made them concurrent; the second replaces the concurrency with a single BatchCall([mint_to_private, mint_to_public]) tx, because measurement showed concurrent same-sender txs still pay consecutive slots: the PXE serializes simulateTx/proveTx on a single queue, so the second tx is only proven after the first and misses the first's slot. Overlapping cannot remove whole slots — only batching can. Both mints are Alice sends on BananaCoin touching disjoint balances; the private-balance before/after assertion is preserved, and the public mint's effect is checked by the downstream tests that spend Alice's public bananas. Precedent for the pattern already lives in this tree (mintNotes in fixtures/token_utils.ts).

Dependency analysis

  • FPC deploy needs the token address (constructor arg) → runs after the token deploy, concurrent with the mint batch.
  • Mints need the token → concurrent with the FPC deploy, and the private/public mints are mutually independent (disjoint balances), so they share one tx.
  • SponsoredFPC setup is a wallet registration only → free to join the Promise.all.
  • TokenBridge's initializer stores {token, portal} in its own storage and never calls the token, so the bridge deploy does not need the token to be mined first.
  • perf(e2e): seed BananaFPC fee juice at genesis instead of bridging #24564 invariants preserved: BANANA_COIN_SALT/BANANA_FPC_SALT + deployer: alice are untouched, and the genesis-funded-address assertion in applyFPCSetup still runs (and passed locally).

Same-sender concurrency

Concurrent .send() calls from the same sender through the same TestWallet are safe: the PXE serializes simulateTx/proveTx through a SerialQueue (#putInJobQueue), so simulations never interleave and only the block-inclusion waits overlap. The setup txs pay fees from Alice's public fee-juice balance (PREEXISTING_FEE_JUICE) with random tx nonces, so there are no nullifier or note conflicts between the concurrent txs. Verified locally with full Promise.all of the send+wait calls; no NO_WAIT fallback or cross-sender split was needed.

Surveyed and skipped

  • Batching token class+instance publication: not applicable on this base — DeployMethod.request() already merges class publication, instance publication, and the constructor call into a single execution payload sent as one tx. Measured locally, deploy:token is a single ~12s (1 slot) occurrence; the ~24s occurrences in CI span data are inclusion latency at CI cadence, not a second tx.
  • applyFPCSetup: a single deploy:fpc tx — no same-sender pair inside the phase. Folding the FPC deploy into the mint batch (one class publication + app calls is within the class-log limit) would remove the remaining consecutive slot, but it merges two harness phases that four test files compose differently via Promise.all, and deploy-inside-BatchCall is not a proven pattern here — a restructure, not a mechanical merge. Left as a possible follow-up.
  • applySponsoredFPCSetup: no on-chain tx — it only calls wallet.registerContract locally; the sponsored FPC is genesis-funded and its class preloaded. Nothing to batch.
  • Two different contract deploys cannot share a tx (MAX_CONTRACT_CLASS_LOGS_PER_TX = 1), which rules out any two-deploy batch (e.g. cross-chain token+bridge) — those stay concurrent rather than batched.

Local verification (12s slots; CI runs at its own cadence)

Overlap commits (fees + cross-chain):

  • Base (account_init, spans on): 5/5 passed; beforeAll 83.5s = setup:auth-registry 32.0s + deploy:token 12.0s + tx:mint 23.9s (busyMs == totalMs, fully serial) + deploy:fpc 11.8s.
  • After overlap: 5/5 passed; beforeAll 72.2s (−11.3s); deploy:fpc fully overlapped with the mint window.
  • gas_estimation: 3/3 passed; private_payments: 8/8 passed; token_bridge (cross-chain overlap): 7/7 passed — deploy:token 12.1s and deploy:bridge 23.7s ran concurrently (union ≈ 23.7s vs ~48s serial on the CI baseline); l2_to_l1: 6/6 passed (regression check on the untouched path).

Batch commit (account_init, 5/5 green on all runs):

run tx:mint count tx:mint busyMs deploy:fpc busyMs beforeHooksMs
before batch 2 23618 23713 71607
run 1 (after) 1 11834 24065 72372
run 2 (after) 1 11807 23743 71678

The mint phase drops from two consecutive slots (~23.6s) to one (~11.8s), one proof and one tx fewer per shard. No intermittent failures across any run.

Measured impact

Measured on CI in two steps (each commit group had its own PR run before unification).

Overlap (PR run 1783394912998666 vs #24564's run 1783375878950264): −162s summed across shards. Per-suite beforeHooks: private_payments.parallel −16.0s (×8), gas_estimation.parallel −15.5s (×3), token_bridge −12.0s, fee_juice_payments −12.0s, account_init −11.5s. The overlap is real but partial: tx:mint busyMs/totalMs = 0.68 run-wide — concurrent txs land in consecutive slots because the PXE serializes proving (the finding that motivated the batch commit). Cross-chain token_bridge collapses its deploy phase from serial 35.9s to concurrent ~23.4s (union ≈ max, as designed).

Batch (PR run 1783432285938536 vs 1783394912998666): tx:mint 42 → 29 occurrences (one merged per fees shard); busyMs 523s → 431s (−92s of tx/proving work; busyMs == totalMs in the PR run — no overlapping mint txs left to union). Wall-clock lands where the mint is the setup critical path: failures −12.0s, fee_juice_payments −11.5s (≈ one 12s slot each). Suites where the mint overlaps applyFPCSetup are flat (account_init −0.4s, private_payments.parallel +2s, gas_estimation.parallel +3s): deploy:fpc is unchanged (13 occurrences, ~2 slots each) and remains their critical path, so removing a slot from the mint branch of the Promise.all doesn't move the union. Run-wide sum over common suites: −8s; noise envelope on untouched suites ±20–37s.

Net vs #24564's baseline: roughly −185s summed across shards, plus 13 fewer txs/proofs per run (less proving contention through the sequencer). The remaining ~12s/shard lever on the overlapped fees suites is folding the FPC deploy into the same batch, noted above as a follow-up.

Fixes A-1406
Fixes A-1409

The BananaFPC deploy, SponsoredFPC registration, and Alice's banana mints
each depend only on the BananaCoin deployed during FeesTest.setup, so the
fees suites now run them concurrently instead of paying one production slot
per step. Likewise the cross-chain harness deploys the L2 token and its
bridge concurrently: the bridge takes the token address as a constructor
arg, but that address is deterministic before the deploy tx mines, and the
bridge constructor does not call into the token.
applyFundAliceWithBananas sent Alice's private and public banana mints
back-to-back, paying one production slot each. The two mints touch disjoint
balances, so they are now sent concurrently and share a slot. Each mint
keeps its own tx:mint span, so the overlap shows up as busyMs < totalMs in
the timing data.
@spalladino spalladino added ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure wip Work in progress labels Jul 7, 2026
@spalladino spalladino removed the wip Work in progress label Jul 7, 2026
@spalladino spalladino changed the title perf(e2e): overlap and batch e2e setup transactions perf(e2e): overlap and batch setup txs in e2e harnesses Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant