Skip to content

feat(tests, spec-specs): eip8037 sstore/collision clear dynamics#2863

Merged
spencer-tb merged 3 commits into
ethereum:devnets/bal/7from
fselmo:fix/sstore-clear-dynamics
May 18, 2026
Merged

feat(tests, spec-specs): eip8037 sstore/collision clear dynamics#2863
spencer-tb merged 3 commits into
ethereum:devnets/bal/7from
fselmo:fix/sstore-clear-dynamics

Conversation

@fselmo

@fselmo fselmo commented May 15, 2026

Copy link
Copy Markdown
Contributor

🗒️ Description

Spec fix: EIP-8037 inline state_gas refunds (SSTORE clear, CREATE collision/revert) now credit the local frame's reservoir immediately, instead of being clamped to local state_gas_used and deferred.

Tests added

  • Added a nested refund-propagation test in test_state_gas_reservoir.py modelled on test_nested_failure_resets_to_tx_reservoir but exercising the success path. Parametrizes for refund scenarios × depth × consume-point.
  • A local-consumption SSTORE-restoration test and a depth_10 case on existing ancestor-charge test

🔗 Related Issues or PRs

N/A.

✅ Checklist

  • All: Ran fast static checks to avoid unnecessary CI fails, see also Code Standards and Enabling Pre-commit Checks:
    just static
  • All: PR title adheres to the repo standard - it will be used as the squash commit message and should start type(scope):.
  • All: Considered updating the online docs in the ./docs/ directory.
  • All: Set appropriate labels for the changes (only maintainers can apply labels).

@fselmo fselmo requested a review from spencer-tb May 15, 2026 22:24
@codecov

codecov Bot commented May 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (devnets/bal/7@73083f0). Learn more about missing BASE report.

Additional details and impacted files
@@               Coverage Diff                @@
##             devnets/bal/7    #2863   +/-   ##
================================================
  Coverage                 ?   87.39%           
================================================
  Files                    ?      586           
  Lines                    ?    35942           
  Branches                 ?     3381           
================================================
  Hits                     ?    31410           
  Misses                   ?     3911           
  Partials                 ?      621           
Flag Coverage Δ
unittests 87.39% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@spencer-tb spencer-tb marked this pull request as ready for review May 18, 2026 12:12
@spencer-tb spencer-tb changed the title Fix/sstore clear dynamics feat(tests, spec-specs): eip8037 sstore/collision clear dynamics May 18, 2026
@spencer-tb spencer-tb added A-spec-specs Area: Specification—The Ethereum specification itself (eg. `src/ethereum/*`) C-feat Category: an improvement or new feature P-urgent A-tests Area: Consensus tests. labels May 18, 2026

@spencer-tb spencer-tb 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.

Thanks! LGTM

@spencer-tb spencer-tb force-pushed the fix/sstore-clear-dynamics branch from 6bd98e3 to 85b10d8 Compare May 18, 2026 13:34
@spencer-tb spencer-tb merged commit 8eb54b4 into ethereum:devnets/bal/7 May 18, 2026
22 checks passed
Sahil-4555 pushed a commit to Sahil-4555/erigon that referenced this pull request May 20, 2026
## Summary

Bumps the EEST devnet fixtures from `tests-bal@v7.1.1` to
`tests-bal@v7.2.0` and adapts Erigon to the EIP-8037 spec change that
ships with it.

### What changed in EEST v7.2.0

The only behavioural change in the new fixtures is execution-specs PR
[erigontech#2863](ethereum/execution-specs#2863) —
"EIP-8037 SSTORE/collision clear dynamics":

- Inline `state_gas` refunds (`SSTORE` original-zero reset, `CREATE`
collision/revert) now credit the local frame's `state_gas_left`
**immediately**, instead of being clamped to a per-frame
`state_gas_used` counter and bubbled up as a pending credit.
- `Evm.state_gas_used` becomes a **signed** counter — it goes negative
when refunds in a frame outweigh the frame's own charges (typical for
`DELEGATECALL`/`CALLCODE` callees that clear a slot an ancestor set).
- `incorporate_child_on_success` no longer propagates a pending refund;
`incorporate_child_on_error` restores the parent's reservoir via the
`state_gas_left + state_gas_used` invariant.
- `process_transaction` computes block state gas as `Uint(max(0,
intrinsic_state + state_gas_used − state_refund))`.

Bulk of the rest of the v7.2.0 diff is additional BAL test coverage
synchronised from `forks/amsterdam`.

### Erigon changes

- `test-fixtures.json`: bump `eest_devnet` to `tests-bal@v7.2.0` (sha256
`fc1d9ae1…`, 611 743 632 bytes).
- `execution/protocol/mdgas/md_gas.go`: `MdGasUsage` keeps `Regular
uint64` but `State` becomes `int64`. Adds
`MdGasUsage.PlusIntrinsic(MdGas)`, `MdGasUsage.StateClamped()` (=
`uint64(max(0, State))`), and `MdGasUsage.Total() = Regular +
StateClamped()`. `MdGas` (reservoirs / leftover) keeps both fields
`uint64`.
- `execution/vm/interpreter.go`: `CallContext.frameStateUsed` is now
`int64`. `refundCreditPending` is removed. `creditStateGasRefund` drops
the clamp — it does `stateGas += amount; frameStateUsed -=
int64(amount)` directly.
- `execution/vm/instructions.go`:
`op{Call,CallCode,DelegateCall,StaticCall,execCreate}` drop the
`PendingStateGasCredit` absorption; success path is just
`scope.frameStateUsed += childUsed.State` (signed).
- `execution/vm/evm.go`:
- `evm.call` / `evm.create` use a named return `gasRemaining` (renamed
from `leftOverGas`) and `gas` as the parameter; mutations are visible in
the body.
- `handleFrameRevert(gasRemaining *MdGas, …, stateGasUsed int64)` takes
the signed state usage and restores the parent reservoir via
`gasRemaining.State + stateGasUsed`. Includes a panic on the invariant
violation `state_gas_left + state_gas_used < 0` so any future regression
in the `useMdGas`/`creditStateGasRefund` pairing surfaces loudly rather
than silently wrapping the `uint64` cast.
- The depth-0 defers fold the spec's error semantics into
`gasUsed.State`: CALL error resets it to `0`; CREATE error sets it to
`-StateGasNewAccount` (mirrors the spec's `state_refund +=
STATE_BYTES_PER_NEW_ACCOUNT * COST_PER_STATE_BYTE` on CREATE-tx
failure). `TxnExecutor` then reads `gasUsed.State` directly without an
error-path branch.
- Extracts `deriveFrameRegularGasUsed(inputTotal, gasRemainingTotal
uint64, stateGasUsed int64) uint64` — `Regular = (input − leftover) −
state` in signed `int64` so a refund-heavy frame whose
`gasRemaining.State` grew above the input still yields the correct
positive regular-ops count (a guarded `uint64` subtraction would
mis-skip and return 0).
- `execution/protocol/txn_executor.go`: Amsterdam branches collapse the
spec's `tx_state_gas = intrinsic_state + state_gas_used` +
`block_state_gas_used += max(0, tx_state_gas)` into `combined :=
gasUsed.PlusIntrinsic(imdGas); st.blockStateGasUsed =
combined.StateClamped(); st.txnGasUsedB4Refunds = combined.Total()`.

### Tests

- `execution/vm/evm_test.go` (new): `TestDeriveFrameRegularGasUsed` with
four sub-cases — charges-only, with-spillover, refunds-exceed-charges
(the edge case the old guarded subtraction returned `0` for), and
pure-refund. Worked numeric examples are documented inline.

## Test plan

- [x] `make lint` clean.
- [x] `go test ./execution/vm/... ./execution/protocol/...` — unit tests
green.
- [x] EEST shards on the new fixtures, 0 failures:
  - `statetests-devnet` (65 202 tests)
  - `blocktests-devnet` (82 941 tests)
  - `blocktests-devnet-race-amsterdam` (21 368 tests)
- [x] Regression check against stable fixtures, 0 failures:
  - `statetests-stable` (63 556 tests)
  - `blocktests-stable-{sequential,parallel}` (69 256 tests each)
  - `enginextests-stable-{sequential,parallel}` (63 920 tests each)
- [x] The originally failing variants now pass:
`tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_sstore.py::test_sstore_restoration_refund_credits_local_reservoir[fork_Amsterdam-state_test-depth_{1,3,10}-refund_funds_create]`.
jsign pushed a commit to jsign/ethrex that referenced this pull request May 21, 2026
Aligns ethrex with bal-devnet-7 v7.2.0.

Tracking: lambdaclass#6583 (item 10).

## Changes

- Port ethereum/execution-specs#2863: inline `state_gas` refunds (SSTORE
`0→x→0`, CREATE collision/revert, EIP-7702 auth refills) credit the
local frame's reservoir immediately. `state_gas_used` becomes `i64`; the
clamp-and-spill scaffolding (`state_gas_refund_pending`,
`state_gas_refund_absorbed`, `state_gas_spill_outstanding`,
`state_gas_credit_against_drain`, intrinsic-tracking field + frame
snapshots) is removed.
- Bump fixtures and `eels_commit` to `tests-bal@v7.2.0` / `a3e5201a5`.
- Preserve intrinsic state gas on top-level error: split out a dedicated
`vm.intrinsic_state_gas` so the error path refunds only the execution
portion. Without this, EIP-7702 set-code reverts and CREATE-tx reverts
underbilled `block_state_gas_used` by `AUTH_TOTAL × CPSB` / `NEW_ACCOUNT
× CPSB`.
- `default_hook.rs` `state_refund` i64 cast: propagate
`InternalError::Overflow` instead of silently saturating (match
`vm.rs`).
- `engine_newPayloadV{4,5}`: return JSON-RPC `-32602` on fork/field
mismatch (V4 with BAL field or Amsterdam timestamp; V5 missing BAL field
or pre-Amsterdam timestamp). Was returning `PayloadStatus.INVALID`.

## Test plan

- `cargo check`, `clippy`, `fmt`: clean across `ethrex-levm`,
`ethrex-rpc`, `ethrex-vm`, `ethrex-blockchain`, `ethrex-l2`.
- `make -C tooling/ef_tests/blockchain test-levm` against
`tests-bal@v7.2.0`: 8726 passed, 0 failed.
- Hive `eels/consume-engine` Amsterdam: pending re-run; engine-API fix
targets the 2 remaining `test_fork_transition` failures.
spencer-tb added a commit to spencer-tb/execution-specs that referenced this pull request May 22, 2026
benbencik pushed a commit to benbencik/ethrex that referenced this pull request Jun 13, 2026
Aligns ethrex with bal-devnet-7 v7.2.0.

Tracking: lambdaclass#6583 (item 10).

## Changes

- Port ethereum/execution-specs#2863: inline `state_gas` refunds (SSTORE
`0→x→0`, CREATE collision/revert, EIP-7702 auth refills) credit the
local frame's reservoir immediately. `state_gas_used` becomes `i64`; the
clamp-and-spill scaffolding (`state_gas_refund_pending`,
`state_gas_refund_absorbed`, `state_gas_spill_outstanding`,
`state_gas_credit_against_drain`, intrinsic-tracking field + frame
snapshots) is removed.
- Bump fixtures and `eels_commit` to `tests-bal@v7.2.0` / `a3e5201a5`.
- Preserve intrinsic state gas on top-level error: split out a dedicated
`vm.intrinsic_state_gas` so the error path refunds only the execution
portion. Without this, EIP-7702 set-code reverts and CREATE-tx reverts
underbilled `block_state_gas_used` by `AUTH_TOTAL × CPSB` / `NEW_ACCOUNT
× CPSB`.
- `default_hook.rs` `state_refund` i64 cast: propagate
`InternalError::Overflow` instead of silently saturating (match
`vm.rs`).
- `engine_newPayloadV{4,5}`: return JSON-RPC `-32602` on fork/field
mismatch (V4 with BAL field or Amsterdam timestamp; V5 missing BAL field
or pre-Amsterdam timestamp). Was returning `PayloadStatus.INVALID`.

## Test plan

- `cargo check`, `clippy`, `fmt`: clean across `ethrex-levm`,
`ethrex-rpc`, `ethrex-vm`, `ethrex-blockchain`, `ethrex-l2`.
- `make -C tooling/ef_tests/blockchain test-levm` against
`tests-bal@v7.2.0`: 8726 passed, 0 failed.
- Hive `eels/consume-engine` Amsterdam: pending re-run; engine-API fix
targets the 2 remaining `test_fork_transition` failures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-spec-specs Area: Specification—The Ethereum specification itself (eg. `src/ethereum/*`) A-tests Area: Consensus tests. C-feat Category: an improvement or new feature P-urgent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants