Skip to content

Commit 165ba69

Browse files
committed
feat(tests): EIP-8037 drop double refund on incorporate_child_on_error
1 parent 3f82bbb commit 165ba69

2 files changed

Lines changed: 32 additions & 49 deletions

File tree

tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_reservoir.py

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,9 +1328,7 @@ def test_nested_failure_resets_to_tx_reservoir(
13281328
- `cumulative_gas_used` (receipt) pins `tx.gas - gas_left -
13291329
state_gas_left`, catching bugs in the leftover split.
13301330
- `header.gas_used` pins `max(block_regular, block_state)` via
1331-
the block accumulators. They differ from the receipt by
1332-
exactly `non_top_burns` (inline refunds the user pays for but
1333-
the block doesn't track in either accumulator).
1331+
the block accumulators.
13341332
"""
13351333
gas_limit_cap = fork.transaction_gas_limit_cap()
13361334
assert gas_limit_cap is not None
@@ -1359,41 +1357,25 @@ def test_nested_failure_resets_to_tx_reservoir(
13591357
else:
13601358
top, frame_codes = _build_create_chain(pre, frame_bodies, terminator)
13611359

1362-
# Non-top inline state-gas refunds (body SSTORE x→0 plus CREATE
1363-
# pre-charge credits on child failure) accumulate in each frame's
1364-
# `state_gas_refund` and get subtracted at the parent's
1365-
# `incorporate_child_on_error` boundary so the inflation does not
1366-
# leak across the rolled-back state change. The top frame's
1367-
# refund is preserved by the tx-level error handler.
1368-
non_top_body_refund_burn = sum(
1369-
b.state_refund(fork) for b in frame_bodies[1:]
1370-
)
1371-
non_top_create_credit_burn = max(0, n_creates - 1) * new_account_state_gas
1372-
non_top_burns = non_top_body_refund_burn + non_top_create_credit_burn
1373-
13741360
sum_regular = sum(code.regular_cost(fork) for code in frame_codes)
13751361
spill = max(0, total_state_charges - reservoir)
13761362
if failure_mode == "halt":
13771363
# Policy A (updated EIP): all state-gas — body charges, spilled
13781364
# portions, and CREATE pre-charges (returned via credit) — folds
13791365
# into state_gas_left at tx end. gas_left is zeroed by halt.
1380-
# `non_top_burns` is the inline refund burn at incorporate
1381-
# boundaries that does not return to the user's reservoir.
1382-
state_gas_at_end = max(reservoir, total_state_charges) - non_top_burns
1366+
state_gas_at_end = max(reservoir, total_state_charges)
13831367
expected_cumulative = tx_gas - state_gas_at_end
13841368
# Header: block_regular = gas_limit_cap - spill (spilled
13851369
# state-gas drained gas_left but is no longer reclassified to
13861370
# regular under Policy A); block_state ≈ 0 for plain CALLs.
13871371
expected_header_gas_used = gas_limit_cap - spill
13881372
elif failure_mode == "revert":
13891373
# Revert preserves gas_left; full state-gas refund.
1390-
# User pays only regular costs + intrinsic + non-top burns.
1391-
expected_cumulative = intrinsic_cost + sum_regular + non_top_burns
1374+
# User pays only regular costs + intrinsic.
1375+
expected_cumulative = intrinsic_cost + sum_regular
13921376
# Header reflects the regular-vs-state attribution directly:
13931377
# state_gas_used is zeroed by the tx error handler, so only
1394-
# regular gas usage shows up. The refund burn lives in the
1395-
# `state_gas_left` shortfall (visible in cumulative), not
1396-
# the regular accumulator.
1378+
# regular gas usage shows up.
13971379
expected_header_gas_used = intrinsic_cost + sum_regular
13981380
else:
13991381
raise ValueError("Invariant, unreachable code.")

tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_sstore.py

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -828,12 +828,15 @@ def test_sstore_restoration_sub_frame_revert(
828828
call_opcode: Op,
829829
) -> None:
830830
"""
831-
Verify 0 to x to 0 reservoir refund unwinds on sub-frame REVERT.
832-
833-
The sub-call performs 0 to x to 0 then REVERTs. If the reservoir
834-
refund is not rolled back with the reverted frame, the reservoir
835-
stays inflated by `sstore_state_gas`. A single-SSTORE probe sized
836-
to OOG by 1 would then succeed; the test asserts it OOGs.
831+
Verify 0 to x to 0 reservoir refund returns to the caller on
832+
sub-frame REVERT.
833+
834+
The sub-call performs 0 to x to 0 then REVERTs. Since both the
835+
set-charge and its refund roll back together, the
836+
`state_gas_used + state_gas_left` sum reflects the unconsumed
837+
reservoir and is returned to the caller via
838+
`incorporate_child_on_error`. A single-SSTORE probe sized to OOG
839+
by 1 succeeds, confirming the caller's reservoir was replenished.
837840
"""
838841
gas_costs = fork.gas_costs()
839842
# Probe SSTORE(0, 1): 2 pushes + cold storage write + state gas - 1,
@@ -853,7 +856,7 @@ def test_sstore_restoration_sub_frame_revert(
853856
# and REVERT without a hard-coded budget.
854857
caller_storage = Storage()
855858
caller_code = Op.POP(call_opcode(gas=Op.GAS, address=child)) + Op.SSTORE(
856-
caller_storage.store_next(0, "probe_must_fail"),
859+
caller_storage.store_next(1, "probe_must_succeed"),
857860
Op.CALL(gas=probe_gas, address=probe),
858861
)
859862
caller = pre.deploy_contract(code=caller_code)
@@ -876,16 +879,15 @@ def test_sstore_restoration_ancestor_revert(
876879
fork: Fork,
877880
) -> None:
878881
"""
879-
Verify the SSTORE 0 to x to 0 refund unwinds when an ancestor frame
880-
(not the applying frame itself) reverts.
882+
Verify the SSTORE 0 to x to 0 refund returns to the caller when an
883+
ancestor frame (not the applying frame itself) reverts.
881884
882885
Inner frame applies the refund and returns successfully; its
883-
refund propagates to middle via `incorporate_child_on_success`.
884-
Middle then REVERTs; its refund must be dropped by the caller's
885-
`incorporate_child_on_error`, rather than propagating up. This
886-
exercises the recursive scope that single-frame revert tests do
887-
not: a bug in the success propagation of `state_gas_refund` would
888-
leak the refund into the caller's reservoir.
886+
`state_gas_left` (inflated by the refund) propagates to middle
887+
via `incorporate_child_on_success`. Middle then REVERTs; the
888+
refunded reservoir flows back to the caller via
889+
`incorporate_child_on_error`, so the caller's reservoir is
890+
replenished by `sstore_state_gas`.
889891
"""
890892
gas_costs = fork.gas_costs()
891893
# Probe SSTORE(0, 1): 2 pushes + cold storage write + state gas - 1,
@@ -910,7 +912,7 @@ def test_sstore_restoration_ancestor_revert(
910912
code=(
911913
Op.POP(Op.CALL(gas=Op.GAS, address=middle))
912914
+ Op.SSTORE(
913-
caller_storage.store_next(0, "probe_must_fail"),
915+
caller_storage.store_next(1, "probe_must_succeed"),
914916
Op.CALL(gas=probe_gas, address=probe),
915917
)
916918
),
@@ -936,16 +938,17 @@ def test_sstore_restoration_create_init_revert(
936938
create_opcode: Op,
937939
) -> None:
938940
"""
939-
Verify reservoir refunds unwind when CREATE init code REVERTs
940-
inside a sub-frame that also REVERTs.
941+
Verify reservoir refunds return to the caller when CREATE init
942+
code REVERTs inside a sub-frame that also REVERTs.
941943
942944
Wrapping the CREATE in an outer reverting frame isolates the
943945
rollback concern from the legitimate CREATE silent-failure refund
944946
(`create_account_state_gas` credited to the frame executing the
945-
CREATE opcode). When the outer frame reverts, every refund that
946-
occurred inside it must unwind, leaving the caller's reservoir at
947-
its pre-call value. A single-SSTORE probe sized to OOG by 1
948-
detects any leaked refund.
947+
CREATE opcode). When the outer frame reverts, the refunded
948+
reservoir flows back to the caller via
949+
`incorporate_child_on_error`, replenishing the caller's
950+
reservoir by at least `sstore_state_gas`. A single-SSTORE probe
951+
sized to OOG by 1 succeeds, confirming the propagation.
949952
"""
950953
gas_costs = fork.gas_costs()
951954
# Probe SSTORE(0, 1): 2 pushes + cold storage write + state gas - 1,
@@ -965,9 +968,7 @@ def test_sstore_restoration_create_init_revert(
965968
else:
966969
create_call = Op.CREATE2(0, 0, len(init_code), 0)
967970

968-
# Inner contract performs the CREATE then REVERTs, so any refunds
969-
# (SSTORE restoration or CREATE silent-failure) applied during its
970-
# execution must unwind with the frame.
971+
# Inner contract performs the CREATE then REVERTs.
971972
inner = pre.deploy_contract(
972973
code=(
973974
Op.MSTORE(
@@ -985,7 +986,7 @@ def test_sstore_restoration_create_init_revert(
985986
code=(
986987
Op.POP(Op.CALL(gas=Op.GAS, address=inner))
987988
+ Op.SSTORE(
988-
caller_storage.store_next(0, "probe_must_fail"),
989+
caller_storage.store_next(1, "probe_must_succeed"),
989990
Op.CALL(gas=probe_gas, address=probe),
990991
)
991992
),

0 commit comments

Comments
 (0)