Skip to content

test_ext_code_double_read_empty_account()

Documentation for tests/amsterdam/eip8038_state_access_gas_cost_increase/test_ext_code_opcodes_gas.py::test_ext_code_double_read_empty_account@26332146.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8038_state_access_gas_cost_increase/test_ext_code_opcodes_gas.py::test_ext_code_double_read_empty_account --fork Amsterdam

Charge the EIP-8038 double-read surcharge on an empty target.

EXTCODESIZE/EXTCODECOPY add the second, code-reading database access unconditionally: the surcharge is charged before the account is read, so an empty/non-existent target still costs COLD_ACCOUNT_ACCESS + WARM_ACCESS (cold) or 2 * WARM_ACCESS (warm), i.e. 3100 / 200, exactly as for a code-bearing target. This contrasts with EXTCODEHASH/BALANCE, which read only the account leaf and carry no surcharge (see test_extcodehash_empty_account). A client that skipped the second read for code-less accounts would be caught here.

Source code in tests/amsterdam/eip8038_state_access_gas_cost_increase/test_ext_code_opcodes_gas.py
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
@EIPChecklist.GasCostChanges.Test.GasUpdatesMeasurement()
@pytest.mark.parametrize("warm", [False, True], ids=["cold", "warm"])
@pytest.mark.parametrize("opcode", DOUBLE_READ_OPCODES)
def test_ext_code_double_read_empty_account(
    state_test: StateTestFiller,
    env: Environment,
    pre: Alloc,
    fork: Fork,
    warm: bool,
    opcode: Op,
) -> None:
    """
    Charge the EIP-8038 double-read surcharge on an empty target.

    ``EXTCODESIZE``/``EXTCODECOPY`` add the second, code-reading database
    access unconditionally: the surcharge is charged before the account is
    read, so an empty/non-existent target still costs
    ``COLD_ACCOUNT_ACCESS + WARM_ACCESS`` (cold) or ``2 * WARM_ACCESS``
    (warm), i.e. 3100 / 200, exactly as for a code-bearing target. This
    contrasts with ``EXTCODEHASH``/``BALANCE``, which read only the account
    leaf and carry no surcharge (see ``test_extcodehash_empty_account``). A
    client that skipped the second read for code-less accounts would be
    caught here.
    """
    # Never deployed: no code, no balance, non-existent account.
    empty_addr = pre.nonexistent_account()

    measured_code = opcode(address=empty_addr)
    # Subtract the opcode's OWN cold cost so the CodeGasMeasure overhead is
    # only the operand PUSH wrapper; the surcharge is part of the cold cost.
    overhead_cost = measured_code.gas_cost(fork) - opcode(
        address_warm=False
    ).gas_cost(fork)

    code_gas_measure = CodeGasMeasure(
        code=measured_code,
        overhead_cost=overhead_cost,
        extra_stack_items=opcode.pushed_stack_items,
    )
    measure_address = pre.deploy_contract(code=code_gas_measure)
    expected_gas = opcode(address_warm=warm).gas_cost(fork)

    tx = Transaction(
        to=measure_address,
        sender=pre.fund_eoa(),
        access_list=[AccessList(address=empty_addr, storage_keys=[])]
        if warm
        else None,
    )

    post = {measure_address: Account(storage={0: expected_gas})}

    state_test(env=env, pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 4 parametrized test cases across 1 fork.