evm/vm: EIP-8037 v7 fixtures (param bump + spill/auth/top-level-create refunds)#4293
Open
gabrocheleau wants to merge 6 commits into
Open
evm/vm: EIP-8037 v7 fixtures (param bump + spill/auth/top-level-create refunds)#4293gabrocheleau wants to merge 6 commits into
gabrocheleau wants to merge 6 commits into
Conversation
Update the EIP-8037 state-gas constants to match the v7 dev fixtures
(`amsterdam/v700_mixed_with_other_eips`):
costPerStateByte: 1174 -> 1530
stateBytesPerStorageSet: 32 -> 64
stateBytesPerNewAccount: 112 -> 120
stateBytesPerAuthBase: 23 (unchanged)
The v7 spec also drops the block-gas-limit-derived costPerStateByte
formula and treats it as a flat constant again. Replace the parametric
helper with a thin wrapper that reads common.param('costPerStateByte').
Point the dev blockchain test scripts and the consumeBal test at the
new fixture directory v700_mixed_with_other_eips (was
snobal_devnet_6_v110_mixed_with_other_eips). The fixtures submodule is
bumped separately on this branch.
Codecov Report❌ Patch coverage is Additional details and impacted files
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
The frame-revert path was popping the reservoir snapshot, which restores the reservoir-paid portion of state-gas, but the spilled portion (state-gas that overflowed into gas_left via useGas) was not credited back. Per the v7 spec, the FULL state_gas_used on a reverted/halted frame must refund to the parent's reservoir (or to the tx-level reservoir at depth 0). Compute the spilled portion at revert time as usedThisFrame - reservoirPaidThisFrame and add it back to the post-pop reservoir. The parent's useGas of the child's executionGasUsed (which includes the spilled state-gas) is now offset by the reservoir credit, so the net cost to the user is just the regular gas.
📦 Bundle Size Analysis
Values are minified+gzipped bundles of each package entry. Workspace deps are bundled; external deps are excluded. Generated by bundle-size workflow |
Per the v7 spec, the existing-authority auth refund both:
- refunds STATE_BYTES_PER_NEW_ACCOUNT * costPerStateByte to the
state_gas_reservoir, AND
- decreases execution_state_gas_used by the same amount.
Previously we only did the reservoir refund. Now execution_state_gas_used
is initialized to -existingAuthStateGasRefund at the start of the tx so
that subsequent SSTORE / CREATE state-gas charges climb back from a
negative baseline. tx_state_gas = intrinsicState + executionStateGasUsed
ends up reduced by the refund, which makes block.state_gas_used
consistent with the per-tx tx_gas_used accounting.
+36 tests in eip8037 group (317 -> 353).
…ATE failure When a creation tx fails at the top level (initcode revert / OOG / exceptional halt / address collision / oversized code), the intrinsic stateBytesPerNewAccount * costPerStateByte that runTx paid up-front was not refunded. The frame-revert snapshot pop only refunds state-gas CHARGED during the frame; the intrinsic was paid before EVM execution started and so isn't tracked in any snapshot. Capture isCreate at frame entry (before _generateAddress sets message.to) and, in the revert handler at depth=0, add the intrinsic amount back to the reservoir and decrement execution_state_gas_used.
4a0572e to
793445d
Compare
…te SELFDESTRUCT
If a creation tx's initcode SELFDESTRUCTs the freshly-created contract in
the same tx, refund the intrinsic stateBytesPerNewAccount * costPerStateByte
in the state dimension so block_state_gas_used reflects that no new account
persists. Per the v7 spec the sender STILL pays the gross intrinsic on
their balance (the reservoir is NOT credited), so receipt cumulativeGasUsed
and the sender debit remain at the pre-refund value — this is purely a
state-dim accounting adjustment.
Track the intrinsic separately in a new createdAccountIntrinsicStateGas
map (populated at depth=0 CREATE success). The deferred-refund path in
runTx now distinguishes the two pools:
- createdAccountStateGas (frame-exit charges):
credit reservoir + decrement execution_state_gas_used
- createdAccountIntrinsicStateGas (tx-create intrinsic):
accumulate into txCreateIntrinsicStateGasRefund, applied ONLY to
txStateGas at final accounting time (no reservoir credit, no
mid-computation executionStateGasUsed mutation that would skew
execution_regular_gas_used).
+6 tests in eip8037 group (366 -> 372).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Updates EIP-8037 to track the v7 dev fixtures (
amsterdam/v700_mixed_with_other_eips).Param bump to match the v7 spec:
costPerStateByte: 1174 → 1530stateBytesPerStorageSet: 32 → 64stateBytesPerNewAccount: 112 → 120The block-gas-limit-derived
costPerStateByteformula is dropped; v7 treats it as a flat constant. Dev test scripts +consumeBaltest repointed fromsnobal_devnet_6_v110_*tov700_mixed_with_other_eips.Spill refund on frame revert — frame-revert snapshot pop already refunded the reservoir-paid portion of state-gas; v7 also refunds the portion that spilled into
gas_left. Compute spilled amount at revert time and add it on top of the snapshot's reservoir value.Auth refund consistency — v7 spec requires the existing-authority 7702 refund to also decrement
execution_state_gas_used. InitializeexecutionStateGasUsed = -existingAuthStateGasRefundso later charges climb back from a negative baseline.Top-level CREATE intrinsic refund on failure — capture
isCreateat frame entry and, in the revert handler at depth=0 for a creation tx, addstateBytesPerNewAccount * costPerStateByteback to the reservoir.Tx-create SELFDESTRUCT intrinsic refund — if a creation tx's initcode SELFDESTRUCTs the freshly-created contract in the same tx, refund the intrinsic in the state dimension only: block_state_gas_used reflects 0, but the sender still pays the gross intrinsic. Tracked in a new
createdAccountIntrinsicStateGasmap distinct fromcreatedAccountStateGas.Test plan
EIP-8037 group (
amsterdam/v700_mixed_with_other_eips/eip8037_state_creation_gas_cost_increase):Full Amsterdam dev suite (
v700_mixed_with_other_eips):Items not landed in this PR (deep dive)
Isolate SSTORE 0→x→0 refund on same-frame revert
After tracing several affected fixtures (
revert_discards_descendant_storage_clear_credit_through_depth,subcall_revert_does_not_leak_grandchild_storage_clear_credit):executionStateGasUsedat frame entry, naturally discarding child-frame credits when a parent reverts. Debug logs confirmtxStateGas,txRegularGas, andtotalGasSpentall match expected fixture values exactly.generated block level access list correct. Diff shows our impl emits 5 EXTRAstorageReadsentries on reverted-child frames. Currentbal.ts:160-203preserves storage reads across reverts (and converts revertedstorageChangesto reads); the v7 fixture expects these entries discarded.This is an EIP-7928 spec/behavior question, not EIP-8037. The state-gas isolation the task names is already in place.
CREATE address-collision burned regular gas counted in
block_regular_gas_usedLocated the v7 test source at
ethereum/execution-specs:devnets/bal/7/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py. The expected formula:Decoded: under v7 the CREATE opcode pre-charges
new_account = stateBytesPerNewAccount * costPerStateByte(= 183,600) of state-gas at opcode entry (before forwarding to inner frame). On address collision, that pre-charge is refunded (reservoir credit +executionStateGasUseddecrement). The user effectively paysgas_limit - retained - new_account + post_create_static.Our current impl charges CREATE state-gas only at successful frame-exit (
stateGasCreatein_executeCreate), so:new_accountadjustment on collisionexecutionGasUsed: message.gasLimit)Properly fixing this requires restructuring CREATE state-gas to charge at opcode entry instead of at frame-exit success, with mirrored refund paths on collision / revert / halt. That's a non-trivial change touching
opcodes/gas.ts(CREATE dynamic gas),_executeCreate(charge timing), and the snapshot mechanism (the pre-charge needs to be in the per-frame snapshot so reverts properly unwind it). Out of scope for this PR.This affects roughly 80 of the remaining 83 failing fixtures in the
eip8037_state_creation_gas_cost_increasegroup; they all share the pattern "CREATE opcode runs, then halts/reverts/collides and the new_account state-gas should refund".