@@ -635,24 +635,27 @@ def test_parent_state_gas_after_child_failure(
635635 """
636636 Test parent state-gas pools after CREATE child failure.
637637
638- A factory invokes CREATE whose initcode performs an SSTORE
639- (charging state gas) then either REVERTs or hits INVALID. The
640- factory's own SSTORE after the failed CREATE acts as the
641- discriminator that the parent's state-gas accounting (reservoir
642- and gas_left) is in the expected state.
638+ A factory runs CREATE whose initcode does an SSTORE, then either
639+ REVERTs or hits INVALID. The factory's own SSTORE after the failed
640+ CREATE checks the parent's reservoir and gas_left are correct.
641+
642+ Under EIP-11807 state-gas refunds are LIFO. Gas spilled from
643+ gas_left refunds to gas_left, only the reservoir-funded portion
644+ returns to the reservoir.
643645
644646 Four scenarios cover the gas-pool state space:
645647
646- - `with_reservoir x revert`: child state gas (new account +
647- initcode SSTORE) is fully refunded to the parent reservoir on
648- REVERT.
649- - `with_reservoir x halt`: HALT resets the child frame to
650- `(0, R0_child)`; only the reservoir-portion entering the
651- initcode is returned, any spilled gas stays burned.
652- - `no_reservoir x revert`: child state gas refunded forms a
653- fresh reservoir even though `R0_parent` started at 0.
654- - `no_reservoir x halt`: no phantom reservoir may form; the
655- factory's post-CREATE SSTORE must spill from gas_left.
648+ - `with_reservoir x revert`: child state gas refills LIFO. The
649+ reservoir-funded portion returns to the parent reservoir, any
650+ spill to the parent gas_left.
651+ - `with_reservoir x halt`: halt refills the child frame LIFO then
652+ burns its gas_left. Only the child's start reservoir survives.
653+ - `no_reservoir x revert`: child state gas spilled wholly from
654+ gas_left, so the LIFO refill returns it there. No phantom
655+ reservoir forms.
656+ - `no_reservoir x halt`: no phantom reservoir forms. The spilled
657+ child state gas is burned with the child gas_left and the
658+ factory's post-CREATE SSTORE spills from gas_left.
656659 """
657660 gas_limit_cap = fork .transaction_gas_limit_cap ()
658661 assert gas_limit_cap is not None
@@ -728,21 +731,21 @@ def test_parent_state_gas_after_child_failure(
728731 initcode_regular_revert = initcode .gas_cost (fork ) - sstore_state_gas
729732
730733 if failure_op == Op .INVALID :
731- # Simulate runtime gas accounting for HALT using fork helpers :
732- # 1. Initial regular pool capped by transaction_gas_limit_cap;
734+ # Simulate runtime gas for HALT under EIP-11807 LIFO refills :
735+ # 1. Regular pool capped by transaction_gas_limit_cap. The
733736 # remainder forms the state reservoir.
734- # 2. CREATE op charges new_account state gas (from reservoir
735- # first, spilled to gas_left otherwise) .
736- # 3. 63/64 retention rule : parent retains gas_left // 64.
737- # 4. INVALID burns all forwarded regular gas in the child.
738- # Per the updated EIP , child halt preserves its state-gas
739- # counters and `incorporate_child_on_error` refunds the
740- # full child charge — including any spilled portion — to
741- # the parent 's reservoir.
742- # 5 . CREATE failure refunds new_account state gas to the
743- # parent's state pool (account creation rolled back) .
744- # 6 . Factory's post-CREATE SSTORE charges sstore_state_gas
745- # (state pool first, spilled to gas_left otherwise) .
737+ # 2. CREATE charges new_account state gas, reservoir first
738+ # then spilled to gas_left and tracked .
739+ # 3. 63/64 retention: parent keeps gas_left // 64. The
740+ # reservoir is forwarded to the child frame .
741+ # 4. Child initcode SSTORE charges sstore_state_gas , child
742+ # reservoir first then spilled to child gas_left.
743+ # 5. INVALID refills the child frame LIFO then burns its
744+ # gas_left. Only the child 's start reservoir survives .
745+ # 6 . CREATE failure refills new_account LIFO: the spill to
746+ # parent gas_left, the rest to the parent reservoir .
747+ # 7 . Factory post-CREATE SSTORE charges sstore_state_gas,
748+ # reservoir first then spilled to gas_left.
746749 execution_gas = gas_limit - intrinsic_cost
747750 regular_budget = gas_limit_cap - intrinsic_cost
748751 sim_gas_left = min (regular_budget , execution_gas )
@@ -751,26 +754,31 @@ def test_parent_state_gas_after_child_failure(
751754 sim_gas_left -= factory_pre_create_regular
752755 sim_gas_left -= gas_costs .OPCODE_CREATE_BASE + init_code_word_cost
753756
754- if sim_state_gas_left >= new_account_state_gas :
755- sim_state_gas_left -= new_account_state_gas
756- else :
757- sim_gas_left -= new_account_state_gas - sim_state_gas_left
758- sim_state_gas_left = 0
757+ # CREATE new_account state gas: reservoir first, spill tracked.
758+ new_account_from_reservoir = min (
759+ sim_state_gas_left , new_account_state_gas
760+ )
761+ new_account_spill = new_account_state_gas - new_account_from_reservoir
762+ sim_state_gas_left -= new_account_from_reservoir
763+ sim_gas_left -= new_account_spill
759764
760- # `child_reservoir` is what the parent forwards to the child.
761- # Under Policy A halt, incorporate refunds child.state_gas_used
762- # + child.state_gas_left = max(sstore, child_reservoir) back to
763- # the parent. The simulator already implicitly retains
764- # `child_reservoir` in `sim_state_gas_left`, so the additional
765- # Policy A refund versus the Policy B "burn the spill" rule is
766- # `max(0, sstore_state_gas - child_reservoir)`.
765+ # 63/64 retention: parent keeps gas_left // 64. The reservoir
766+ # is forwarded to the child frame and survives on halt.
767767 child_reservoir = sim_state_gas_left
768768 sim_gas_left = sim_gas_left // 64
769- sim_state_gas_left += max (0 , sstore_state_gas - child_reservoir )
770- sim_state_gas_left += new_account_state_gas
769+
770+ # INVALID burns child gas_left, including any spilled SSTORE
771+ # state gas. Only the forwarded reservoir survives.
772+ sim_state_gas_left = child_reservoir
773+
774+ # CREATE failure refills new_account LIFO: spilled portion to
775+ # gas_left, reservoir-funded portion to the reservoir.
776+ sim_gas_left += new_account_spill
777+ sim_state_gas_left += new_account_from_reservoir
771778
772779 sim_gas_left -= factory_post_create_regular
773780
781+ # Factory post-CREATE SSTORE: reservoir first, spill otherwise.
774782 if sim_state_gas_left >= sstore_state_gas :
775783 sim_state_gas_left -= sstore_state_gas
776784 else :
0 commit comments