Skip to content

test_extcodehash_empty_account()

Documentation for tests/amsterdam/eip8038_state_access_gas_cost_increase/test_ext_code_opcodes_gas.py::test_extcodehash_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_extcodehash_empty_account --fork Amsterdam

Verify EXTCODEHASH of an empty account is priced without surcharge.

EXTCODEHASH reads only the account leaf, so EIP-8038 adds no code-read surcharge: the cost is exactly COLD_ACCOUNT_ACCESS (cold) or WARM_ACCESS (warm) regardless of the target being empty. The returned hash of an empty/non-existent account is 0.

Source code in tests/amsterdam/eip8038_state_access_gas_cost_increase/test_ext_code_opcodes_gas.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
@EIPChecklist.GasCostChanges.Test.GasUpdatesMeasurement()
@pytest.mark.parametrize("warm", [False, True], ids=["cold", "warm"])
def test_extcodehash_empty_account(
    state_test: StateTestFiller,
    env: Environment,
    pre: Alloc,
    fork: Fork,
    warm: bool,
) -> None:
    """
    Verify ``EXTCODEHASH`` of an empty account is priced without surcharge.

    ``EXTCODEHASH`` reads only the account leaf, so EIP-8038 adds no
    code-read surcharge: the cost is exactly ``COLD_ACCOUNT_ACCESS`` (cold)
    or ``WARM_ACCESS`` (warm) regardless of the target being empty. The
    returned hash of an empty/non-existent account is ``0``.
    """
    # A non-existent (empty) target: never deployed, no balance, no code.
    empty_addr = Address(0xDEAD)

    # EXTCODEHASH reads only the account leaf (no code-read surcharge), so
    # its bare cost is the plain account access.
    expected_gas = Op.EXTCODEHASH(address_warm=warm).gas_cost(fork)

    # Measure the access cost, then store the returned hash so the
    # empty-account 0 result is asserted alongside the pricing. The
    # measured opcode carries the runtime warmth so the overhead reduces
    # to the address PUSH alone.
    #
    # The empty-account hash is 0, which is also the default of an
    # unwritten storage slot: a stranded hash store would leave slot 1 at
    # 0 and pass vacuously (the original defect). Slot 1 is poisoned with
    # a non-zero sentinel before the measured region, so the real store
    # must overwrite it back to 0. If that store is ever stranded, slot 1
    # keeps the sentinel and the assertion fails instead of silently
    # passing.
    # The poison precedes the measured region and the hash store follows
    # it, so neither touches 0xDEAD before the measured access nor
    # perturbs the cold-case gas measurement.
    storage = Storage()
    measured_code = Op.EXTCODEHASH.with_metadata(address_warm=warm)(empty_addr)
    gas_slot = storage.store_next(expected_gas, "extcodehash_empty_gas")
    hash_slot = storage.store_next(0, "extcodehash_empty_hash")
    hash_slot_sentinel = 0xBADC0FFEE
    code = (
        Op.SSTORE(hash_slot, hash_slot_sentinel)
        + CodeGasMeasure(
            code=measured_code,
            overhead_cost=measured_code.gas_cost(fork)
            - Op.EXTCODEHASH(address_warm=warm).gas_cost(fork),
            extra_stack_items=1,
            sstore_key=gas_slot,
        )
        + Op.SSTORE(hash_slot, Op.EXTCODEHASH(empty_addr))
    )
    measure_address = pre.deploy_contract(code=code)

    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=storage)}
    state_test(env=env, pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 2 parametrized test cases across 1 fork.