Repository navigation
Conversation
…ut tests A pre-run's gas_limit could only be reached from below: bumpGasLimit skipped whenever the head was already at or above it. Below the head it now ramps down, sending payload attribute targetGasLimit (the CL's gas limit target since Amsterdam) on each block. Before Amsterdam there is no such attribute and the filler keeps rising toward its miner ceiling, so a ramp down fails on the first block that does not fall rather than after gas_bump_max_blocks. A pre-run with no tests is now valid and skips the EEST checkout and the fill: a pure gas ramp (plus funding, if any), recorded as a bundle like any other pre-run. Together these let a released pre-run's 1T head be walked down to a devnet's limit and replayed by every client. Amsterdam slot numbers continue the parent's when it has one. Every existing flow starts from a pre-Amsterdam snapshot, where that equals the old per-client counter, so their bundles are unchanged; a pre-run on top of an Amsterdam chain no longer restarts at slot 1.
…fill: false (review) - The non-predeploy pre-run builds its funding block after the bump. Clearing targetGasLimit when the ramp returned sent that block back toward the filler's 1T miner ceiling, ~1/1024 above the target. A ramp down now leaves the target set for every later block this client builds. - An empty tests list cannot opt out of the fill: ResolveTarget re-inherits config.tests. A pre-run that only ramps the gas limit is now explicit, fill: false, hoisted like the other fields; tests stays required otherwise.
…hain An in-place (schelk) target restores the baseline before it boots, so a second target cannot continue the first one's scratch. base_bundle replays a recorded bundle (e.g. a release's pre-run) before the gas bump; the target's own bundle holds only the blocks it built, replayed after the base.
This comment has been minimized.
This comment has been minimized.
A release's pre-run bundle is 49.7 GB, which readRequestLines held in memory several times over. Replays now read line by line. A replay target can take base_bundle as well (base, then replay_from), so the other clients continue a release + ramp without a concatenated copy of the release.
There was a problem hiding this comment.
Summary
This PR makes the builder's gas bump bidirectional (via the Amsterdam targetGasLimit payload attribute), adds fill: false for a ramp-only pre-run, streams replay bundles instead of loading them whole, and lets replay targets take a base_bundle. The streaming and fill: false work look sound and the prior two findings (tests required, fill undocumented) are fixed; I found one functional gap in the ramp-down check for predeploy targets and one documentation omission.
Issues
- 🟡
pkg/builder/engine.go:673— ramp-down aborts on a predeploy target's first pre-fork bump block — see the thread on that line - 🟢
docs/configuration.md— new base_bundle option is undocumented in the configuration reference —base_bundleis a new per-target option (pkg/config/pre_runs.go:188) used by both the fill and replay paths, but thebuilder.pre_runs.targets[]table in docs/configuration.md still lists onlyreplay_from; onlyconfig.example.yamlmentions it. A row describing the pre-run it continues and the replay order (base, then the target's own bundle /replay_from) belongs next toreplay_from.
Reviewed @ 2a66455c
"The most dangerous phrase in the language is 'we've always done it this way.'" — Grace Hopper
| return built, fmt.Errorf("building gas-bump block %d: %w", built+1, buildErr) | ||
| } | ||
|
|
||
| if down && built == 0 && gl >= gasLimit { |
There was a problem hiding this comment.
🟡 ramp-down aborts on a predeploy target's first pre-fork bump block
With predeploy, the funding/deploy blocks are built pre-fork and the gas bump is what crosses into Amsterdam (forkAt returns preFork until activationTS). If the head is above gas_limit and the first bump block is still pre-fork, buildBlock sends no targetGasLimit, the limit rises, and this check returns an error on block 1 — even though the later Amsterdam blocks would ramp it down. The pre-fork bump is the expected case when activation is more than one block after the deploy block, so this combination cannot use ramp-down.
…filler fails the target The 15-minute stop window was granted only to schelk_options.promote targets, and the target still passed when the filler was SIGKILLed. A pre-run's datadir is always its product (promoted, exported or read as output_dir): on the release-snapshot e2e, geth v1.17.7 wrote its ramp journal in 32 ms and was then killed at 30 s while waiting on its history indexer, with pebble unclosed, and the target was reported OK.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…ed cleanly
nethermind (dotnet) returns 130 and besu (java) 143 after the signal a
container stop sends, having logged a complete shutdown ("All DBs closed").
Reading graceful as exit 0 logged every nethermind pre-run as not stopped
cleanly, and with the previous commit would have failed it.
SummaryThis PR adds a gas-limit ramp-down (via the Amsterdam Issues
Reviewed @ |
Builder changes (plus review fixes) so a released pre-run can be walked back down to a devnet's gas limit and replayed by every client. The motivation is a mainnet-shadowfork snapshot (glamsterdam msf-2) built from the jochemnet stateful release, whose pre-run ends at a 1T gas limit.
1. Gas bump in both directions
gas_limitbelow the head's limit used to be a silent no-op (bumpGasLimitskipped whenhead >= target). It now ramps down. Each block carries payload attributetargetGasLimit, the CL's gas limit target since Amsterdam, which geth'stesting_buildBlockV1honours over its miner ceiling (miner/worker.go,IsAmsterdam && targetGasLimit != nil).gas_bump_max_blocks.2.
fill: false: a pre-run that only ramps the gas limitA pre-run is gas bump → funding block → fill (EEST setup tests), every block recorded into the bundle. With
fill: false(inherited like the other fields) the fill is skipped, along with the EEST checkout. The pre-run is then just the gas ramp (plus funding, if any), recorded as a bundle like any other.testsstays required whenever the fill runs. An emptytestscouldn't express this, since it inherits the shared default.After a ramp down,
targetGasLimitstays set for every later block this client builds. Otherwise the funding block (built after the bump on the non-predeploy path) would climb back toward the 1T ceiling.Slot numbers
An Amsterdam block's
slotNumbernow continues the parent's. Every existing flow starts from a pre-Amsterdam snapshot, where this equals the old per-client counter, so the bundles are unchanged. A pre-run on top of an Amsterdam chain (e.g. a replayed release) would otherwise restart at slot 1.Verified
gas_ramp_test.go: a fake filler that applies geth'sCalcGasLimit.targetGasLimit, and slots continue from the head;fill: falsevalidates without tests, overrides a sharedtests, and is inherited from the shared config.--miner.gaslimit=1Tas the filler runs: from a 2,000,000,000 parent,testing_buildBlockV1gives 2,001,953,124 withouttargetGasLimitand 1,998,046,876 withtargetGasLimit=1G.go test ./pkg/config ./pkg/builderpasses. golangci-lint v2.3.0 (go 1.24) reports nothing in the changed files.3.
base_bundle: continue a recorded chainAn in-place (schelk) target restores the baseline before it boots, so a second target can't continue where the first one stopped on the scratch.
base_bundle(an absolute path to a.requestfile or apre_run_bundledir) is replayed onto the restored snapshot first:replay_from(base, then tail), so the other clients need no concatenated copy of the release.4. Replays stream
A release's pre-run bundle is 49.7 GB (jochemnet stateful e269bb44: 82,186 lines).
readRequestLinesdidos.ReadFileand then split it, which needs several times that in RAM. Replays now read line by line. Checked against that real bundle with a VALID-everything engine stub: all 82,186 lines replayed with max RSS ~235 MB.5. Every pre-run's filler gets the flush window; a killed filler fails the target
The 15-minute stop window was granted only to
schelk_options.promotetargets, and a SIGKILLed filler still left the targetOK. A pre-run's datadir is always its product — promoted, exported, or read asoutput_dir— so both now apply to every target. Found on the release-snapshot e2e: geth v1.17.7 wrote its ramp journal in 32 ms, then sat in "Waiting background history index initer to exit" past 30 s and was killed with pebble unclosed, and the build reportedOK geth-ramp. The bundle is written before the stop and is unaffected."Stopped cleanly" is exit 0, 130 or 143: nethermind (dotnet) and besu (java) return 128 + the signal our stop sends after a complete shutdown (
All DBs closed … Nethermind is shut down, then 130, measured). 137 (SIGKILL after the grace period), an OOM kill or any other code fails the target.Example: walk a release down to 200M, then replay it on another client
Used by ethpandaops/benchmarkoor-tests#44 (
configs/snapshots/jochemnet/v1/builder.yaml).