fix(evm): align EIP-8037 spill-refund accounting with the EELS reference - #12419
Conversation
- Make advanced state-gas refund revocation exact: track how much spill refund each advance marked (VmState.StateGasSpillRefundAdvanced) and undo it on revert; the removal drains the reservoir only to zero, then recorded usage, and restores any net-spill debt the credit had filled. - Collapse StateGasRefundPending into StateGasRefundAdvanced (they were provably lockstep at every site). - Set the OutOfGas flag when ConsumeStateGas fails on the spill path. - Saturate the EIP-7825 cap subtraction in CreateAvailableFromIntrinsic (replacing the debug assert) so an over-cap intrinsic moves the excess to the reservoir; regression tests for all three. - Extract the shared destroy/recreate logic into DestroyAccount on the non-generic TransactionProcessorBase. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Differential-tested against EELS amsterdam at execution-specs d0338f561 (the tests-glamsterdam-devnet@v6.1.1 commit), settling the two open questions from the PR #12369 review: - Advanced (cross-frame) state-gas refunds carry no spill marks: the discharge in IncorporateChildStateGasRefunds no longer re-marks the parent's StateGasSpillRefunded (the double-marking), and the Min/Max combinator question dissolves - marks travel only via the ordinary child-gas merge. - The advanced portion of a refund continues the source-based LIFO refill (gas_left up to the frame's unrefunded spill, remainder to the reservoir) instead of parking everything in the reservoir; revocation claws the full advance back from the reservoir without unmarking. - Reverted and halted children no longer merge their gross StateGasSpill/StateGasSpillRefunded into the parent, so a reverted descendant's spill cannot dock the parent reservoir at a later halt (sender was overcharged) and advance money can no longer survive a halt in the reservoir (sender was undercharged). - A top-level exceptional halt keeps the full post-refund intrinsic state gas in the block state dimension; burned spill stays regular (removes the StateGasSpillBurned reattribution and the now-dead StateGasSpillReclassified field). Adds Eip8037EelsReferenceTests: 13 scenarios whose expected (spentGas, blockRegularGas, blockStateGas) triples were produced by the EELS reference; all 812 v6.1.1 EIP-8037/8038 fixture tests stay green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @Marchhill's task in 4m 48s —— View job Review: EIP-8037 spill-refund accounting alignment with EELSI reviewed the full diff against
Verdict: ✅ No blocking issues foundThis is a clean, net-removing simplification (−209 lines, three fields deleted) that also fixes real consensus divergences. The design is exactly what the repo rules ask for: it removes state ( Correctness — the algebra checks out. I verified the key invariants by hand:
Signature consistency. All call sites of the three re-signed methods and both deleted-field accessors were updated ( Tests. Minor / non-blocking observations
I did not independently run the build or test suite in this review (relied on static analysis + the PR's reported 13/13 reference, 49/49 policy, 812 fixture, and full Evm/Blockchain green runs). The signature and semantic analysis gives me high confidence the change is sound. |
EVM Opcode Benchmark DiffAggregated runs: base=1, pr=1 No significant regressions or improvements detected. |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
One coverage gap in |
The base branch was changed.
Adds the reservoir paths reachable when tx.gas exceeds the EIP-7825 cap: reservoir-funded charges, the reservoir/gas_left boundary spill, and the initial-reservoir restore across nested exceptional halts. Also drops an unused using directive. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Good catch — added in 50561a6 as three scenarios with reference-derived expectations: |
…-refund-accounting # Conflicts: # src/Nethermind/Nethermind.Evm.Test/Eip8037Tests.cs # src/Nethermind/Nethermind.Evm/GasPolicy/EthereumGasPolicy.cs # src/Nethermind/Nethermind.Evm/GasPolicy/IGasPolicy.cs # src/Nethermind/Nethermind.Evm/VirtualMachine.cs
Follow-up to the PR #12369 review discussion (@LukaszRozmej, @AnkushinDaniil). Stacked on
fix/devnet6-review-findings; will retarget tomasterwhen #12369 merges.Changes
Every open question was settled by differential testing against the EELS amsterdam reference at execution-specs
d0338f561— the exact commit thetests-glamsterdam-devnet@v6.1.1fixtures are built from: identical bytecode scenarios were executed through both implementations and the(spentGas, blockRegularGas, blockStateGas)triples compared (readingblock_output.block_gas_used/block_state_gas_useddirectly on the EELS side).Answers to the review questions:
Math.Min(childState.StateGasSpillRefundAdvanced, unappliedRefund)combinator (Q1): neitherMinnorMax(0, S - applied)is correct — under EELS's source-based refunds, advanced refunds carry no spill marks at all. Marks are applied once, at credit time, in the frame doing the LIFO refill, and reach ancestors via the ordinary child-gas merge.VmState.StateGasSpillRefundAdvancedand the combinator are removed.DiscardStateGasno longer re-marks the parent'sStateGasSpillRefundedwhen discharging a child's advance.Further divergences the differential surfaced (all consensus-relevant, all fixed):
StateGasSpill/StateGasSpillRefundedinto the parent (EELSrefill_frame_state_gassemantics). Previously a reverted grandchild's spill riding through a child halt docked the parent reservoir and overcharged the sender by the spill amount (95k+ gas in the test scenarios).tx_state_gas = intrinsic_state - state_refund). TheStateGasSpillBurnedreattribution shiftedheader.GasUsedby the burned spill in halt-with-authorization scenarios.StateGasSpillBurnedand the deadStateGasSpillReclassifiedfield are removed.Regression tests:
Eip8037GasAccountingTestspins 13 scenarios (nested revert-with-spill → cross-frame refund → outer halts, incl. both review traces, top-level-halt-with-auth block splits, create-refund shapes) to EELS-derived gas triples. On the pre-fix branch, 6 of 13 scenarios diverged from EELS in both directions; all 13 now match exactly.Types of changes
What types of changes does your code introduce?
Testing
Requires testing
If yes, did you write tests?
Notes on testing
Eip8037GasAccountingTests: 13/13 pass (6 failed pre-fix).Eip8037Testspolicy unit tests updated to the corrected semantics (49/49).Nethermind.Evm.Test(4898) andNethermind.Blockchain.Test(1505): green.tests-glamsterdam-devnet@v6.1.1EIP-8037/EIP-8038 fixture files (812 tests) via nethtest: 0 failures.Documentation
Requires documentation update
Requires explanation in Release Notes
🤖 Generated with Claude Code