Skip to content

feat(spec-specs, tests): repay spilled state gas from the reservoir when a child merges - #3478

Open
spencer-tb wants to merge 3 commits into
ethereum:forks/amsterdamfrom
spencer-tb:eips/amsterdam/eip-8037-merge-state-gas-return
Open

feat(spec-specs, tests): repay spilled state gas from the reservoir when a child merges#3478
spencer-tb wants to merge 3 commits into
ethereum:forks/amsterdamfrom
spencer-tb:eips/amsterdam/eip-8037-merge-state-gas-return

Conversation

@spencer-tb

@spencer-tb spencer-tb commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Description

Implement the merge time repayment specified in ethereum/EIPs#12265. A state charge spilled from gas_left can be refunded in a different frame, where the credit lands in the state_gas_reservoir. When a successful child merges, the reservoir now repays the merged frame's outstanding spill, capped at the spill (repay_state_gas_spill).

Flips the placement pins of test_state_gas_cross_frame_refund.py (#3490, #3498, #3499, #3506) to the repaid behaviour, adds an inner-frame probe and pins the receipt of the repaid execution. The repayment moves gas between the two pools settlement sums, so no other fixture changes and the settlement pins hold unchanged.

Related Issues or PRs

Requires ethereum/EIPs#12256 & ethereum/EIPs#12265

Checklist

  • Ran fast static checks to avoid CI fails, see Code Standards & Verifying Changes: just static
  • PR title has the form <type>(<area>): <title>, where <type> and <area> come from an appropriate C-<type>, respectively A-<area>, label. The title should match the target squash commit message.

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.01%. Comparing base (b462ba2) to head (94ee8b5).
⚠️ Report is 1 commits behind head on forks/amsterdam.

Additional details and impacted files
@@               Coverage Diff                @@
##           forks/amsterdam    #3478   +/-   ##
================================================
  Coverage            94.01%   94.01%           
================================================
  Files                  624      624           
  Lines                36905    36912    +7     
  Branches              3326     3326           
================================================
+ Hits                 34695    34702    +7     
  Misses                1533     1533           
  Partials               677      677           
Flag Coverage Δ
unittests 94.01% <100.00%> (+<0.01%) ⬆️

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

☔ View full report in Codecov by Harness.
📢 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 changed the title feat(spec-specs,test-tests): repay spilled state gas from the reservoir when a child merges feat(spec-specs, tests): repay spilled state gas from the reservoir when a child merges Aug 31, 2026
@spencer-tb
spencer-tb force-pushed the eips/amsterdam/eip-8037-merge-state-gas-return branch from aab8895 to e28e730 Compare August 31, 2026 18:43
spencer-tb pushed a commit to chfast/execution-specs that referenced this pull request Sep 2, 2026
…voir

Add a test where the sender pays for a reservoir that covers none, one
or both of the parent's two fresh sets and the rest spill. A delegated
child clears both slots and the refunded state gas lands in the
reservoir. The call window, a later set and the receipt are the same
in every case: the refund stays in the reservoir and the spill is not
repaid.

With one set covered, the refund merged from the child is larger than
the outstanding spill. The existing tests in this file all start with
an empty reservoir, so the merged refund never exceeds the spill there.
Covering both sets and covering none are the boundaries.

The expectations pin current placement. With the `src/` change of
sets and `SLOT_PROBE_RESULT` rises by one state cost for
`reservoir_slots=0` only. The receipt does not change.

Follow-up to ethereum#3490. Coverage for ethereum/EIPs#12265 and ethereum#3478.
@spencer-tb
spencer-tb force-pushed the eips/amsterdam/eip-8037-merge-state-gas-return branch from e28e730 to df8077e Compare September 2, 2026 13:10
chfast added a commit to chfast/execution-specs that referenced this pull request Sep 2, 2026
Add two tests pinning where a merged state gas refund lands, swept
over `with_all_call_opcodes` and `with_all_create_opcodes`. A holder
contract owns the cleared slot, so a create's initcode reaches it the
same way a call's child does, and the parent's spill comes from
setting the slot through the holder.

Each test measures a clearing window against a no-op window of
identical code, so neither needs gas arithmetic. Today the refund
stays in the reservoir: the two windows cost the same at a call, and
at a create the second window's account creation charge draws on the
parked refund, so the clearing window costs one slot's state gas more.
With the `src/` change of ethereum#3478 applied, every arm flips to minus one
slot's state cost.

Before this, `CREATE` and `CREATE2` never merged a refunding child
into a parent holding a spill: a repayment applied only at
`generic_call` passed the whole suite. `CALL`, `CALLCODE` and depth 2
did reach the path, in `test_state_gas_sstore` and
`test_state_gas_set_code`, but nothing there discriminates. Both
tests reach the holder through an intermediate frame, so the sweep
pins depth 2 as well.

`STATICCALL` is excluded: a static child cannot write, so it can
never refund.

`test_cross_frame_refund_parks_in_reservoir` builds the same probe,
so move that shape into `clearing_probe_code` and take the setting
code as a parameter. Its fixtures are unchanged.

Follow-up to ethereum#3490 and ethereum#3499. Coverage for ethereum/EIPs#12265 and
ethereum#3478.
@spencer-tb
spencer-tb force-pushed the eips/amsterdam/eip-8037-merge-state-gas-return branch from d14e716 to 5db355e Compare September 2, 2026 15:17
chfast added a commit to chfast/execution-specs that referenced this pull request Sep 3, 2026
Four tests each asserted a gas window measuring where a merged refund
sits and a receipt measuring what the sender pays. The window changes
under the merge-time repayment of ethereum/EIPs#12265, the receipt
must not. Asserted together, the receipt could only be read from a
test that change makes fail.

Split each in two. The tests keeping the receipt drop the `GAS` stamps
and the result slot, so one fewer slot survives and their sums stop
carrying the probe overhead. With the `src/` change of ethereum#3478 applied,
the failing tests are exactly the ones measuring a window, so the
module docstring's claim that the two are independent is now checked.

Extract what the halves share as `clearing_child_code`,
`clearing_child_storage` and `delegation_to`, and name the budget
leaving a storing child more than the call stipend
`budget_above_sstore_stipend`. It was open-coded ten times. The two
`SSTORE` metadata forms spelled out eleven times become `FRESH_SET`
and `WARM_CLEAR`, and `window_cost_excess` loses its metadata
parameter now that no receipt sum reads a measured frame. Fixtures for
every pre-existing test are unchanged.
@spencer-tb
spencer-tb force-pushed the eips/amsterdam/eip-8037-merge-state-gas-return branch from 5db355e to 94ee8b5 Compare September 3, 2026 09:23
@spencer-tb
spencer-tb marked this pull request as ready for review September 3, 2026 14:13
spencer-tb added a commit that referenced this pull request Sep 3, 2026
ilitteri pushed a commit to lambdaclass/execution-specs that referenced this pull request Sep 3, 2026
…es branch

Ports execution-specs#3478 onto devnets/frames/0, which was frozen before the rule and so
diverged from tests-glamsterdam-devnet@v8.1.4 on the cross-frame-refund fixtures: the two
releases pinned contradictory post-states for the same tests, and no client could satisfy
both.

The frames branch keeps the state gas reservoir as a separate GasMeter.reservoir component
rather than flat meter fields, so the repayment operates on the reservoir and runs inside
the reservoir branch of incorporate_child, next to the spill and reservoir merge it
completes. A frame transaction's meters carry no reservoir, so the repayment never runs for
them: their state gas lives on the frame context and is never drawn from gas_left, leaving
nothing to repay and nothing to repay from. The test rewrite is unchanged from ethereum#3478.

Verification: the rewritten test fills 75/75 at Amsterdam and 75/75 at Bogota, and every
common case matches tests-glamsterdam-devnet@v8.1.4 exactly (post-state and block hashes);
the EIP-8141 suite fills unchanged (721 passed at Bogota).

(cherry picked from commit 7341820, adapted)
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.

1 participant