Skip to content

Commit 7546b83

Browse files
authored
feat(tests): EIP-8037 create OOG state gas boundary coverage (#2847)
1 parent b7860ca commit 7546b83

1 file changed

Lines changed: 120 additions & 14 deletions

File tree

tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_ordering.py

Lines changed: 120 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
from execution_testing import (
1818
Account,
1919
Alloc,
20+
Block,
21+
BlockchainTestFiller,
2022
Fork,
23+
Header,
2124
Initcode,
2225
Op,
2326
StateTestFiller,
@@ -30,6 +33,8 @@
3033
REFERENCE_SPEC_GIT_PATH = ref_spec_8037.git_path
3134
REFERENCE_SPEC_VERSION = ref_spec_8037.version
3235

36+
WORD_SIZE = 32
37+
3338

3439
def _single_sstore_probe_gas(fork: Fork) -> int:
3540
"""
@@ -253,36 +258,61 @@ def test_selfdestruct_oog_reservoir_inflation_detection(
253258
state_test(pre=pre, tx=tx, post=post)
254259

255260

261+
@pytest.mark.parametrize(
262+
"oog_step",
263+
[
264+
pytest.param("create_base", id="oog_on_create_base"),
265+
pytest.param("init_code_word_cost", id="oog_on_init_code_word_cost"),
266+
],
267+
)
256268
@pytest.mark.with_all_create_opcodes()
257269
@pytest.mark.valid_from("EIP8037")
258270
def test_create_oog_reservoir_inflation_detection(
259271
state_test: StateTestFiller,
260272
pre: Alloc,
261273
fork: Fork,
262274
create_opcode: Op,
275+
oog_step: str,
263276
) -> None:
264277
"""
265-
Detect CREATE/CREATE2 state gas ordering via reservoir inflation.
266-
267-
A child does CREATE (or CREATE2) with size=0 and gas tuned so the
268-
regular gas charge OOGs by 1. CREATE/CREATE2 already have the
269-
correct ordering (regular before state), so this is a regression
270-
test ensuring it stays that way.
271-
272-
Single-SSTORE probe detects potential inflation.
278+
Detect CREATE/CREATE2 state-gas ordering via parent-reservoir
279+
inflation. Two OOG boundaries are exercised: `oog_on_create_base`
280+
(empty initcode) and `oog_on_init_code_word_cost` (32-byte
281+
initcode).
273282
"""
274283
gas_costs = fork.gas_costs()
275284
new_account_state_gas = gas_costs.NEW_ACCOUNT
276285

286+
if oog_step == "create_base":
287+
initcode_size = 0
288+
setup_gas = 0
289+
init_code_word_cost = 0
290+
else:
291+
initcode_size = WORD_SIZE
292+
setup_gas = (
293+
Op.MSTORE.popped_stack_items * gas_costs.VERY_LOW
294+
+ gas_costs.OPCODE_MSTORE_BASE
295+
+ gas_costs.MEMORY_PER_WORD
296+
)
297+
init_code_word_cost = gas_costs.CODE_INIT_PER_WORD
298+
277299
if create_opcode == Op.CREATE:
278-
child_code = create_opcode(value=0, offset=0, size=0)
279-
pushes_gas = 3 * gas_costs.VERY_LOW
300+
create_op = create_opcode(value=0, offset=0, size=initcode_size)
280301
else:
281-
child_code = create_opcode(value=0, offset=0, size=0, salt=0)
282-
pushes_gas = 4 * gas_costs.VERY_LOW
302+
create_op = create_opcode(
303+
value=0, offset=0, size=initcode_size, salt=0
304+
)
305+
pushes_gas = create_opcode.popped_stack_items * gas_costs.VERY_LOW
283306

284-
create_regular_gas = gas_costs.OPCODE_CREATE_BASE
285-
child_gas = pushes_gas + create_regular_gas + new_account_state_gas - 1
307+
if oog_step == "create_base":
308+
child_code = create_op
309+
else:
310+
child_code = Op.MSTORE(0, 0) + create_op
311+
312+
create_regular_gas = gas_costs.OPCODE_CREATE_BASE + init_code_word_cost
313+
child_gas = (
314+
setup_gas + pushes_gas + create_regular_gas + new_account_state_gas - 1
315+
)
286316
child = pre.deploy_contract(child_code)
287317

288318
probe = pre.deploy_contract(Op.SSTORE(0, 1))
@@ -306,3 +336,79 @@ def test_create_oog_reservoir_inflation_detection(
306336

307337
post = {caller: Account(storage=caller_storage)}
308338
state_test(pre=pre, tx=tx, post=post)
339+
340+
341+
@pytest.mark.parametrize(
342+
"oog_step",
343+
[
344+
pytest.param("create_base", id="oog_on_create_base"),
345+
pytest.param("init_code_word_cost", id="oog_on_init_code_word_cost"),
346+
],
347+
)
348+
@pytest.mark.with_all_create_opcodes()
349+
@pytest.mark.valid_from("EIP8037")
350+
def test_create_oog_full_burn_no_state_credit(
351+
blockchain_test: BlockchainTestFiller,
352+
pre: Alloc,
353+
fork: Fork,
354+
create_opcode: Op,
355+
oog_step: str,
356+
) -> None:
357+
"""
358+
Verify a CREATE OOG inside a non-creation tx burns the whole
359+
tx gas_limit — no state-gas leftover is credited at tx-end.
360+
"""
361+
gas_costs = fork.gas_costs()
362+
new_account_state_gas = gas_costs.NEW_ACCOUNT
363+
364+
if oog_step == "create_base":
365+
initcode_size = 0
366+
setup_gas = 0
367+
init_code_word_cost = 0
368+
else:
369+
initcode_size = WORD_SIZE
370+
setup_gas = (
371+
2 * gas_costs.VERY_LOW
372+
+ gas_costs.OPCODE_MSTORE_BASE
373+
+ gas_costs.MEMORY_PER_WORD
374+
)
375+
init_code_word_cost = gas_costs.CODE_INIT_PER_WORD
376+
377+
if create_opcode == Op.CREATE:
378+
create_op = create_opcode(value=0, offset=0, size=initcode_size)
379+
else:
380+
create_op = create_opcode(
381+
value=0, offset=0, size=initcode_size, salt=0
382+
)
383+
pushes_gas = create_opcode.popped_stack_items * gas_costs.VERY_LOW
384+
385+
if oog_step == "create_base":
386+
factory_code = create_op
387+
else:
388+
factory_code = Op.MSTORE(0, 0) + create_op
389+
factory = pre.deploy_contract(factory_code)
390+
391+
create_regular_gas = gas_costs.OPCODE_CREATE_BASE + init_code_word_cost
392+
body_gas = (
393+
setup_gas + pushes_gas + create_regular_gas + new_account_state_gas - 1
394+
)
395+
396+
intrinsic_calc = fork.transaction_intrinsic_cost_calculator()
397+
tx_gas_limit = intrinsic_calc() + body_gas
398+
399+
tx = Transaction(
400+
sender=pre.fund_eoa(),
401+
to=factory,
402+
gas_limit=tx_gas_limit,
403+
)
404+
405+
blockchain_test(
406+
pre=pre,
407+
blocks=[
408+
Block(
409+
txs=[tx],
410+
header_verify=Header(gas_used=tx_gas_limit),
411+
),
412+
],
413+
post={},
414+
)

0 commit comments

Comments
 (0)