@@ -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" )
12141222def 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