Skip to content

Commit 640cec5

Browse files
committed
feat(tests): refund EIP-8037 account creation for existing create targets
1 parent af161dc commit 640cec5

1 file changed

Lines changed: 106 additions & 4 deletions

File tree

tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,11 +1210,20 @@ def test_code_deposit_halt_discards_initcode_state_gas(
12101210
)
12111211

12121212

1213+
@pytest.mark.parametrize(
1214+
"target",
1215+
[
1216+
pytest.param("new", id="new_account"),
1217+
pytest.param("existing", id="existing_account"),
1218+
],
1219+
)
1220+
@pytest.mark.pre_alloc_mutable()
12131221
@pytest.mark.valid_from("EIP8037")
12141222
def test_create_tx_header_gas_used(
12151223
blockchain_test: BlockchainTestFiller,
12161224
pre: Alloc,
12171225
fork: Fork,
1226+
target: str,
12181227
) -> None:
12191228
"""
12201229
Verify block header gas_used for a successful CREATE transaction.
@@ -1223,22 +1232,45 @@ def test_create_tx_header_gas_used(
12231232
exact gas_used from first principles and verify against the block
12241233
header. Catches bugs where clients report gas_limit instead of
12251234
actual consumed gas.
1235+
1236+
For a fresh target the NEW_ACCOUNT state gas is charged and
1237+
dominates the regular gas, so gas_used == NEW_ACCOUNT. For a
1238+
pre-existing balance-only leaf the NEW_ACCOUNT charge is refunded,
1239+
so net state gas is zero and the regular intrinsic gas dominates.
1240+
The expected value subtracts NEW_ACCOUNT and so fails if the
1241+
refund regresses.
12261242
"""
12271243
gas_costs = fork.gas_costs()
12281244
initcode = Op.STOP
12291245
create_state_gas = fork.create_state_gas(code_size=1)
12301246

1247+
if target == "existing":
1248+
sender = pre.fund_eoa(nonce=0)
1249+
contract_address = compute_create_address(address=sender, nonce=0)
1250+
# Balance-only leaf: alive and deployable, so the creation
1251+
# succeeds and the intrinsic NEW_ACCOUNT charge is refunded.
1252+
pre.fund_address(contract_address, amount=1)
1253+
else:
1254+
sender = pre.fund_eoa()
1255+
12311256
tx = Transaction(
12321257
to=None,
12331258
data=initcode,
12341259
state_gas_reservoir=create_state_gas,
1235-
sender=pre.fund_eoa(),
1260+
sender=sender,
12361261
)
12371262

12381263
# block_gas_used = max(block_regular, block_state)
1239-
# For a minimal CREATE tx deploying Op.STOP (1 byte),
1240-
# state gas (new account) dominates regular gas.
1241-
expected_gas_used = gas_costs.NEW_ACCOUNT
1264+
if target == "existing":
1265+
intrinsic_cost = fork.transaction_intrinsic_cost_calculator()
1266+
intrinsic_total = intrinsic_cost(
1267+
calldata=bytes(initcode), contract_creation=True
1268+
)
1269+
expected_gas_used = intrinsic_total - gas_costs.NEW_ACCOUNT
1270+
else:
1271+
# For a minimal CREATE tx deploying Op.STOP (1 byte),
1272+
# state gas (new account) dominates regular gas.
1273+
expected_gas_used = gas_costs.NEW_ACCOUNT
12421274

12431275
blockchain_test(
12441276
pre=pre,
@@ -2568,3 +2600,73 @@ def test_create_collision_burned_gas_counted_in_block_regular(
25682600
],
25692601
post={},
25702602
)
2603+
2604+
2605+
@pytest.mark.parametrize(
2606+
"target",
2607+
[
2608+
pytest.param("new", id="new_account"),
2609+
pytest.param("existing", id="existing_account"),
2610+
],
2611+
)
2612+
@pytest.mark.with_all_create_opcodes()
2613+
@pytest.mark.valid_from("EIP8037")
2614+
def test_create_account_creation_charge(
2615+
blockchain_test: BlockchainTestFiller,
2616+
pre: Alloc,
2617+
fork: Fork,
2618+
create_opcode: Op,
2619+
target: str,
2620+
) -> None:
2621+
"""
2622+
Verify NEW_ACCOUNT is charged for a new account and refunded for a
2623+
pre-existing balance-only leaf.
2624+
2625+
Empty init code means zero code deposit, so NEW_ACCOUNT is the only
2626+
create state cost. A fresh target is charged it; a pre-existing
2627+
balance-only target (balance, no code, zero nonce) refunds it on
2628+
success. The probe SSTORE both confirms the create succeeded and
2629+
makes state gas dominate, so gas_used drops by exactly NEW_ACCOUNT
2630+
when refunded.
2631+
"""
2632+
new_account = fork.gas_costs().NEW_ACCOUNT
2633+
sstore_state_gas = Op.SSTORE(new_value=1).state_cost(fork)
2634+
mstore_value, size = init_code_at_high_bytes(Op.STOP)
2635+
create_call = (
2636+
create_opcode(value=0, offset=0, size=size, salt=0)
2637+
if create_opcode == Op.CREATE2
2638+
else create_opcode(value=0, offset=0, size=size)
2639+
)
2640+
2641+
storage = Storage()
2642+
factory = pre.deploy_contract(
2643+
code=Op.MSTORE(0, mstore_value)
2644+
+ Op.SSTORE(
2645+
storage.store_next(1, "create_succeeds"), Op.GT(create_call, 0)
2646+
)
2647+
)
2648+
2649+
# Factory deployed via deploy_contract starts at nonce 1.
2650+
if create_opcode == Op.CREATE2:
2651+
create_address = compute_create2_address(
2652+
address=factory, salt=0, initcode=bytes(Op.STOP)
2653+
)
2654+
else:
2655+
create_address = compute_create_address(address=factory, nonce=1)
2656+
if target == "existing":
2657+
pre.fund_address(create_address, amount=1)
2658+
2659+
tx = Transaction(
2660+
to=factory,
2661+
state_gas_reservoir=new_account + sstore_state_gas,
2662+
sender=pre.fund_eoa(),
2663+
)
2664+
2665+
# State gas dominates regular: a new account adds NEW_ACCOUNT on top
2666+
# of the probe SSTORE, a pre-existing target refunds it.
2667+
expected = sstore_state_gas + (new_account if target == "new" else 0)
2668+
blockchain_test(
2669+
pre=pre,
2670+
blocks=[Block(txs=[tx], header_verify=Header(gas_used=expected))],
2671+
post={factory: Account(storage=storage)},
2672+
)

0 commit comments

Comments
 (0)