core, core/vm, params: adopt the EIP-2780 and EIP-8037 spec changes (#35318) - #2455
Conversation
|
@claude review |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Amsterdam runtime creation bypasses required checks, and parts of the restored test harness do not exercise the intended fork behavior.
Review effort: Balanced
Findings: 2
Open (2)
What changed in this PR
Adopts updated EIP-2780/EIP-8037 gas accounting behind the dormant Amsterdam fork.
Changes:
- Moves state-dependent gas charges from intrinsic gas to runtime execution.
- Refactors CREATE/CALL gas accounting and tracing.
- Restores Amsterdam EIP tests and fixture support.
| File | Description |
|---|---|
cmd/evm/internal/t8ntool/transaction.go |
Adapts intrinsic-gas API. |
core/bench_test.go |
Updates benchmark gas calculation. |
core/eip2780_test.go |
Adds EIP-2780 coverage. |
core/eip7997_test.go |
Adds factory-transition tests. |
core/eip8037_test.go |
Adds state-transition gas tests. |
core/eip8038_test.go |
Adds authorization gas tests. |
core/eip_testenv_test.go |
Supplies shared EIP test fixtures. |
core/error.go |
Adds runtime out-of-gas error. |
core/state_transition.go |
Implements runtime gas charging. |
core/state_transition_test.go |
Updates intrinsic-gas expectations. |
core/tracing/hooks.go |
Adds gas-change reasons. |
core/tracing/gen_gas_change_reason_stringer.go |
Regenerates reason strings. |
core/txpool/validation.go |
Adapts transaction validation. |
core/vm/common.go |
Minor formatting cleanup. |
core/vm/eip8037_test.go |
Adds VM gas-accounting tests. |
core/vm/evm.go |
Refactors contract creation. |
core/vm/gas_table.go |
Moves creation charges to runtime. |
core/vm/gascosts.go |
Updates gas-budget operations. |
core/vm/instructions.go |
Charges conditional creation costs. |
core/vm/interface.go |
Minor interface formatting. |
core/vm/interpreter.go |
Uses exported regular-gas charging. |
core/vm/operations_acl.go |
Applies Amsterdam warm-access pricing. |
core/vm/runtime/runtime.go |
Adapts the CREATE return signature. |
docs/upstream-merges/v1.17.5/needs-wiring.md |
Records adoption and remaining blockers. |
eth/tracers/parity.go |
Adapts parity intrinsic-gas tracing. |
params/protocol_params.go |
Updates Amsterdam gas constants. |
tests/gen_sttransaction.go |
Supports arbitrary-precision fixture nonces. |
tests/state_test_util.go |
Validates fixture nonces against EIP-2681. |
tests/transaction_test_util.go |
Adds Osaka and Amsterdam cases. |
Files not reviewed (2)
- core/tracing/gen_gas_change_reason_stringer.go: Generated file
- tests/gen_sttransaction.go: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report❌ Patch coverage is ❌ Your patch check has failed because the patch coverage (80.11%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## upstream-merge-v1.17.4 #2455 +/- ##
==========================================================
+ Coverage 56.25% 56.42% +0.16%
==========================================================
Files 944 944
Lines 174742 174836 +94
==========================================================
+ Hits 98295 98645 +350
+ Misses 70396 70158 -238
+ Partials 6051 6033 -18
... and 25 files with indirect coverage changes
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
lucca30
left a comment
There was a problem hiding this comment.
Hi Pratik, approving this one. I compared the port with upstream v1.17.5: state_transition.go only has our known differences (Madhugiri cap arm, fee burn/tip/log, block based fork checks), and gas_table.go and gascosts.go are identical. Before Amsterdam, IntrinsicGas, the create() prechecks and the gas budget are the same as before. The new check order in preCheck changes only which error comes first, and miner/BlockSTM don't depend on the error type, so no impact there.
One small ask: the 2 claude[bot] points (no tracer frame when the CREATE precheck fails, and runtime.Create skipping the precheck under Amsterdam) are upstream verbatim, ok. But can you add them to POS-3738 / needs-wiring so we check them again when we schedule Amsterdam?
Nit: Forks["Amsterdam"] in tests/init.go has no BPO1/BPO2 blob config like upstream. Fine if it is on purpose (same as our Osaka), right?
Carries #2346's review follow-up (49aa12b, BlockSTM pre-execution through PreExecution) and its #2354 adaptation (ab0921f), via #2455 (c27e0f2). One conflict, in core/parallel_state_processor_fork_parity_test.go: kept this branch's IsEIP158 entry (it follows the params.Rules both paths pass, #35498) and took the new IsVerkle classification (the gate lives only in PreExecution). Adapted outside the conflict: this branch's #35458 removed the EIP-7997 factory insert from PreExecution, so the BlockSTM processors drop it along with serial and TestV2PreExecEIP7997Activation, added on #2354, has nothing left to check. It is removed here, and the v1.17.6 ledger's #35458 row says so. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@lucca30 thanks for the review.
|
…#35318) Ports upstream 1ef0ffb (#35318), deferred out of the v1.17.5 sync, onto bor's diverged state transition. IntrinsicGas returns uint64 and drops costPerStateByte; the gas-pool reservation and the init-code size check move into preCheck; the running budget is set up after the intrinsic charge by initRuntimeGasBudget; the top-level create and call frames move into executeCreate and executeCall. Kept from bor: - the Madhugiri arm of the EIP-7825 per-tx gas cap, which upstream's replacement drops; - block-based one-argument fork checks; - the fee burn to the burnt contract, the tip, and the fee transfer log in execute, with effectiveTip on signed big.Int and the NoBaseFee skip still commented out. Also applies the state_transition.go half of #35396 (tracer frames for transactions that halt before the top frame); its core/tracing half was already in. eth/tracers/parity.go follows the IntrinsicGas signature. Everything #35318 changes is Amsterdam-gated, and Amsterdam is nil on every bor preset. Restores the upstream EIP-8037, EIP-2780, EIP-7997 and EIP-8038 state-transition tests dropped in v1.17.5, on bor's block-based Amsterdam gate. Seven core/vm EIP-8037 tests are skipped and three test files are held back: on a config with bor's forks, Chicago's instruction set takes precedence over Amsterdam's (POS-3738). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rdam fork The EIP-8037 test configs cloned MergedTestChainConfig with its Bor config, so Chicago's instruction set was selected ahead of Amsterdam's: seven opcode tests were skipped and the rest passed without exercising the state-gas charges. Clear Bor in both configs so Amsterdam's pricing is what runs, and drop the skips. The fork-ordering problem on real Bor configs stays tracked under POS-3738. TestValidationFloorCostCap now uses 600,000 bytes of calldata, because Bor's MaxTxGas is 2^25 against upstream's 2^24. Add a block-based Amsterdam entry to tests.Forks so transaction fixtures with an Amsterdam result are validated instead of rejected as unsupported. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Both were raised in AI review and are upstream verbatim, so the port keeps them: a CREATE whose precheck fails under Amsterdam gets no tracer frame, and direct EVM.Create callers (vm/runtime.Create, i.e. cmd/evm) skip the precheck. Block processing is unaffected, since the state transition checks CanTransfer and the nonce ceiling itself. Recorded as needs-wiring rows pointing at POS-3738 so both are rechecked when Amsterdam is scheduled, as asked in PR review. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
754d732 to
ada1fc7
Compare
174b61b
into
upstream-merge-v1.17.4
Brings in #2455's merge into the base (174b61b) so this PR's diff is only v1.17.6 again. GitHub rebased #2455 onto the base when #2354 merged, so its commits (b4a3d86, 89d2fd3, ada1fc7) are new copies of the ones this branch already contains via 754d732. A normal three-way merge re-applies the same #2455 hunks and conflicts wherever v1.17.6 changed them further. This merge keeps this branch's tree unchanged (-s ours), and that is the correct result. The merged base's tree equals ada1fc7, which differs from 754d732 (an ancestor of this branch) only in core/vm/gen_dispatch/main.go and core/vm/interpreter_dispatch.go, where the rebase restored #2346's RegularGas form. This branch needs its own form there (ExecutionGas, mirroring this branch's Run), which it already has. Nothing else in the base is missing here. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Important
Status, 2026-10-01: #2345, #2346 and #2354 have merged, so this PR now targets
upstream-merge-v1.17.4directly. It is no longer part of a GitHub stack: merging #2354 made GitHub rebase this branch onto the base automatically (754d732aa→ada1fc786), so the stack was removed. The content is unchanged except that the switch-dispatch generator went back to #2346'schargeRegular/chargeStateform, which charges gas identically. Merge with a merge commit. Don't use "Update branch" with rebase, or any rebase. The notes below describe the earlier stacked layout.Stacked on #2354 (base
ppatil-upstream-v1.17.5), notdevelop. Part of the go-ethereum upstream sync (POS-2549), which merges intoupstream-merge-v1.17.4and reachesdeveloponce, at the end.Merge with a merge commit. Never squash. Squashing rewrites this branch's SHAs and breaks every PR stacked above it.
Review bottom-up: #2308, #2319, #2325, #2328, #2337, #2340, #2341, #2342 and #2343 have merged into the base. The open stack is #2345 → #2346 → #2354 → #2455 → #2461. Start at #2345.
Review fixes and cascade, 2026-09-29: three findings from Marcello's review were fixed where the code lives and cascaded up. #2346
f2fde2692: the switch-dispatch interpreter (runSwitch, on by default on mainnet BPs) now charges EIP-8037 state gas likeRun, with a new Amsterdam differential test. #23540166dcc9d: the pebble v1 path, which every existing bor database opens through, keeps bor's tuning and metrics (new databases use pebble v2; upgrade command tracked in POS-3746). #2354aac7dd01e: V2'sFinaliseFastsettles EIP-8246 self-destructs like serialFinalise. Cascade: #2354256e10c39→ #2455d96fcc81b→ #2461440ed1ac4; at #2455 and #2461 the generator mirrors that level's renamedRunhelpers. All dormant until Amsterdam except the pebble fix, which restores today's behaviour for existing nodes. Every hop builds and passes the dispatch/V2/parity tests, and the witness regeneration tests pass on the top of the chain.Review follow-up and cascade, 2026-09-30: per Jerry's review on #2346 (
49aa12b54), both BlockSTM processors run their pre-execution system calls through the serialPreExecutioninstead of inline copies (no behaviour change there). Merged up into #2354 (ab0921f62), that closes a dormant gap: #35285 had moved the EIP-7997 factory insert (first Amsterdam block) intoPreExecution, which V1/V2 didn't call, so they would have disagreed with serial on the root at activation;TestV2PreExecEIP7997Activationcovers it. Cascade: #2354ab0921f62→ #2455c27e0f29a→ #2461caeadc89b, where upstream's #35458 drops the insert altogether and the test goes with it. Every hop builds and passes the parity/V2 tests, and the witness regeneration tests pass on the top of the chain.Cascade, 2026-09-28: #2342 merged into the base (
cd9097f32) and was cascaded up the stack: #2343663809636→ #23454aa9ff8f0→ #23468971e231a→ #2354778958987→ #2455f1c5c8571. It brings only #2342's final changes (develop's otel 1.45go.mod/go.sumbump, thenightly-govulncheckworkflow and one eth/70 receipt test). Every hop test-compiles, and the witness regeneration tests pass on the top of the chain.2026-09-29: #2343 merged into the base (
cc07116a3, a two-parent merge whose tree matches663809636), so nothing new cascades from the base. Review fixes since: #235464709a22f(FinaliseerrTerminatedlog filter, override-flag help text), merged up into #2455 asc4922a57b; #2455855630f34(EIP-8037 test configs run without Bor forks, block-basedAmsterdamentry intests.Forks). The v1.17.6 sync, #2461, now sits on top of #2455, so the release covers v1.17.4 + v1.17.5 + v1.17.6.Summary
Adopts upstream #35318 (EIP-2780 and EIP-8037 spec changes, upstream
1ef0ffb98). It was deferred out of the v1.17.5 sync with the recorded decision "take as its own stacked PR". It has to land before the v1.17.6 sync, because v1.17.6's gas changes (#35454, #35457, #35486, #35497) are written against it.This changes nothing on Bor's networks. Everything #35318 touches is gated on Amsterdam, and
AmsterdamBlockis nil on every Bor preset.This is a hand-written port, not a merge. #35318's history is already in the tree from v1.17.5, and its content was reverted there, so the diff was replayed 3-way onto Bor's files.
What upstream changes
IntrinsicGasreturnsuint64and dropscostPerStateByte. State gas is now charged at runtime, not as part of intrinsic gas.buyGasintopreCheck.initRuntimeGasBudgetsets up the running budget after the intrinsic charge.executeCreateandexecuteCall.EVM.CreateandCreate2no longer return acreationflag. The account-creation state charge moves intochargeAccountCreation, called fromopCreateandopCreate2.Kept from Bor (the three items the v1.17.5 ledger required)
!rules.IsAmsterdam && rules.IsOsaka, which would drop the cap Bor has had live since Madhugiri. The port keeps!rules.IsAmsterdam && (rules.IsOsaka || isMadhugiri).execute. That's the burn to the burnt contract, the tip, and the fee transfer log, including its pre-gas-purchase balance snapshots.effectiveTipstays on signedbig.Int, and upstream'sNoBaseFeeskip stays commented out, matching the existingTODO(raneet10).Also:
state_transition.gohalf is applied here. It fell with the deferral; itscore/tracing/hooks.gohalf was already in.eth/tracers/parity.go, which is Bor-only, follows the newIntrinsicGassignature.EpochDuration,ExpByteGas,SloadGasandTierStepGasare removed as upstream did. None had a user in Bor.Where to spend review time
core/state_transition.gopreCheck: the EIP-7825 cap with the Madhugiri arm.execute: Bor's fee block andinput1/input2, which must be captured beforepreCheckbuys gas.core/vm/evm.go,core/vm/instructions.gocreateFramePreCheck/chargeAccountCreation. Before Amsterdam,create()runs the same checks in the same order.core/vm/gas_table.go,core/vm/operations_acl.go*8037function. The PIP-88 twins are untouched.core/txpool/validation.goIntrinsicGaschange; Bor's txpool gates are unchanged.Provenance
Each ported file was diffed against upstream v1.17.5, not just against the #35318 commit. Every remaining difference is an existing Bor divergence:
MaxTxGas(2^25) and base-fee parameters;checkMaxCodeSize(the Ahmedabad cap);No other upstream change was lost with the deferral.
Restored tests
The upstream EIP tests dropped in v1.17.5 are restored from upstream v1.17.5 and converted to Bor's block-based Amsterdam gate (
AmsterdamTimebecomesAmsterdamBlock).core/eip8037_test.gocore/eip2780_test.gocore/eip7997_test.gocore/eip8038_test.gocore/vm/eip8037_test.gocore/eip_testenv_test.goholds the part of upstream'seip7928_test.gotest environment that these tests use. Remove it wheneip7928_test.gois restored (POS-3737).MaxTxGas(2^25 against upstream's 2^24):TestEIP2780RuntimeOOGRevertsDelegations/with-reservoiruses 200 authorizations instead of 100, andTestValidationFloorCostCapuses 600,000 bytes of calldata instead of 300,000.Bor(855630f). With Bor's forks,NewEVMselects Chicago's instruction set ahead of Amsterdam's, so the opcode tests would otherwise skip or pass without exercising EIP-8037. All of them now run and pass.tests.Forkshas a block-basedAmsterdamentry, so transaction fixtures with an Amsterdam result are validated.Held back, and why this matters for enabling Amsterdam (POS-3738):
Bor = nilfor that reason.core/vm/eip8038_test.gois held back for the same reason.core/eip7708_test.gois held back because Bor's legacy transfer log is emitted alongside EIP-7708's.core/eip8246_test.gois held back because it fails at chain creation on Bor.POS-3738 tracks all of these as blockers for scheduling Amsterdam.
docs/upstream-merges/v1.17.5/needs-wiring.mdis updated row by row.Executed tests
go build ./...is clean.go test -run XXXNOMATCHXXX ./...compiles every test package.go vetoncore,core/vmandeth/tracersshows only the known pre-existing lock-copy finding. gofmt is clean.go test -shortpasses oncore,core/vm/...,core/txpool/...,core/tracing/...,eth/tracers/...,internal/ethapi/...,tests,minerandparams.cmd/evmfailsTestT8n,TestEVMTracing,TestEvmRunandTestEvmRunRegExwith the same failure messages as the unmodified core/vm, params, eth: merge geth v1.17.5 (v1.17.5 sync) #2354 tip. That's the known pre-existing set; CI excludescmd/.TestV2BlockSTMAllBlocks(BOR_BLOCKSTM_TEST=1): identical results to the unmodified core/vm, params, eth: merge geth v1.17.5 (v1.17.5 sync) #2354 tip. 238/241 blocks are consistent between serial and BlockSTM V2 execution on both trees. The same three blocks fail on both with "code is not found": fixture data the test fetches from an RPC endpoint (ALCHEMY_URL) when available. None of the failures is a state-root mismatch.TestV2WitnessRegeneration{AllBlocks,PipelinedSRCAllBlocks,PipelinedSRCChained}pass in non-short mode. Chained covers 222/222 consecutive mainnet pairs.Rollout notes
IsAmsterdam, which is nil on every Bor preset. Before Amsterdam, the EIP-7825 cap, the intrinsic gas values and the fee handling are unchanged. The replay above is the empirical check.preCheck. The gas-pool check and the init-code size check now run before the balance check, as upstream has them. A transaction that fails several checks may report a different error first. Gas-tracer events keep the same sequence, because #35396 re-emits the initial-balance event.🤖 Generated with Claude Code