Skip to content

cluster-tool: pass --slots-in-an-epoch to the run-time anvil at spawn - #111

Open
qhool wants to merge 1 commit into
masterfrom
feature/eth_opp_calldata_delivery
Open

qhool wants to merge 1 commit into
masterfrom
feature/eth_opp_calldata_delivery

Conversation

@qhool

@qhool qhool commented Oct 1, 2026 •

Copy link
Copy Markdown

Summary

One harness fix found while running the whole-envelope delivery branches end to end: pass --slots-in-an-epoch to the run-time anvil at spawn.

AnvilProcessSteps.runStart spawned anvil with only host/port/chain-id/state and switched interval mining on afterwards over RPC. anvil has no RPC for the epoch slot count, so every create and flow cluster ran on the default 32-slot epoch: finalized sat 64 blocks behind latest, and the batch operators — which read the outpost's outbound envelope at finalized — only saw each Ethereum envelope a minute after the tip that emitted it. Every depot epoch stretched to about two minutes waiting on that one hop.

AnvilProcess.SlotsInAnEpoch now rides the spawn (finality two blocks behind), exactly as the rendered start.sh already did for the run path; --block-time stays off the spawn so the Hardhat deploy keeps instamine and planEnableIntervalMining still turns mining on afterwards. Measured on flow-operator-collateral-deposit: tip-to-read 74s → 15s, flow 842s → 563s.

Runner tests added in AnvilProcessSteps.test.ts; repo gate (lint, build, jest 2268 tests) green.

Cross-linked below: the wire-ethereum contracts and wire-sysio cranker this was run against.

Companion PRs

E2E gate

e2e-tests run 36780734850 — success, full platform build plus every discovered flow, dispatched with BRANCH_WIRE_SYSIO, BRANCH_WIRE_ETHEREUM, BRANCH_WIRE_TOOLS_TS and BRANCH_WIRE_LIBRARIES_TS = feature/eth_opp_calldata_delivery (wire-solana and wire-cdt on their manifest branches).

Flow Result Duration
batch-operator-slashing ✅ 407s
batch-operator-termination ✅ 676s
emissions-soak ✅ 2150s
node-owner-nft ✅ 433s
operator-collateral-deposit ✅ 553s
producer-registration ✅ 810s
reserve-lifecycle ✅ 629s
swap-from-wire ✅ 512s
swap-non-native-tokens ✅ 1291s
swap-private-reserves ✅ 1126s
swap-to-wire ✅ 477s
swap-variance-revert ✅ 394s
swap-with-underwriting ✅ 700s
underwriter-slashing ✅ 599s
yield-distribution ✅ 582s

All 15 flows passed.

🤖 Generated with Claude Code

`AnvilProcessSteps.runStart` spawned anvil with only host/port/chain-id/state
and switched interval mining on afterward over RPC. anvil has no RPC for the
epoch slot count, so every `create` and flow cluster ran on anvil's default
32-slot epoch: `finalized` sat 64 blocks behind `latest`, and the batch
operators — which read the outpost's outbound envelope at `finalized` — only
saw each ETH envelope a minute after the tip that emitted it. Every depot
epoch stretched to about two minutes waiting on that one hop.

Ride `AnvilProcess.SlotsInAnEpoch` on the spawn (finality two blocks behind),
exactly as the rendered start.sh already does for the `run` path, and keep
`--block-time` off the spawn so the Hardhat deploy keeps its instamine.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@brianjohnson5972 brianjohnson5972 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.

Peer review alongside wire-ethereum#216 and wire-sysio#660. Looks good to merge. It is independent of the other two PRs and has no ordering constraint. The fix is correct and well tested. Inline: one FYI about production ETH finality latency, plus two test nits.

AnvilProcess.StateFilename
)
),
slotsInAnEpoch: AnvilProcess.SlotsInAnEpoch

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.

This looks right. anvil has no RPC to change --slots-in-an-epoch, so it has to be set at spawn. --block-time correctly stays off the spawn, so the Hardhat deploy keeps instamine, and this now matches the rendered start.sh on the run path.

FYI, not a change request for this PR: the measurement here (tip-to-read 74 s → 15 s, flow 842 s → 563 s) shows how much of each epoch is the single ETH→depot hop, read at finalized. On mainnet finalized trails latest by ~12.8 minutes (two epochs), so in production every depot epoch waits at least one ETH finality period, whatever epoch_duration_sec is set to. Worth flagging to whoever sizes production epochs and the depot's path-2 boundary.

// ProcessManager is a set-once singleton: one sandbox for the whole block.
beforeAll(() => {
dir = Fs.mkdtempSync(Path.join(Os.tmpdir(), "anvilsteps-"))
ProcessManager.setClusterPath(dir)

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.

Nit: ProcessManager.setClusterPath is set-once per process. That's fine here because jest gives each test file its own module registry, but a one-line note that this file must keep a single sandbox for the whole describe would stop someone adding a second describe with its own beforeAll from silently inheriting the first path.

it("spawns anvil with the finality window and without interval mining", async () => {
const ctx = fixtureContext({ clusterPath: dir, dataPath: Path.join(dir, "data") }),
start = jest.fn(),
anvil = { start } as Partial<AnvilProcess> as AnvilProcess,

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.

Nit: { start } as Partial<AnvilProcess> as AnvilProcess (here and at line 76) is fine in a test. If jest.mocked / a typed stub helper already exists in the repo's test utilities, prefer it so the double cast doesn't spread.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants