Skip to content

test_repeated_create_same_code_charges_each_account()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py::test_repeated_create_same_code_charges_each_account@c74f1a67.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py::test_repeated_create_same_code_charges_each_account --fork Amsterdam

Test code deposit is charged per-account, not per code hash.

Two CREATEs with identical init code deploy identical bytecode and so share a single code_hash. The factory snapshots gas_left around each CREATE via Op.GAS and stores (g0 - g1) - (g1 - g2) in slot 0. Identical work must cost the same — so the difference must be zero.

Runtime measurement is required: the bug manifests as a child-frame state-gas spillover into gas_left (a runtime quantity), which static helpers like bytecode.gas_cost() do not model.

A non-zero result indicates compute_state_byte_diff is keying code-deposit accounting by hash via code_writes, silently dropping the second CREATE's len(code) × CPSB charge.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
@pytest.mark.valid_from("EIP8037")
def test_repeated_create_same_code_charges_each_account(
    state_test: StateTestFiller,
    pre: Alloc,
) -> None:
    """
    Test code deposit is charged per-account, not per code hash.

    Two CREATEs with identical init code deploy identical bytecode
    and so share a single ``code_hash``. The factory snapshots
    ``gas_left`` around each CREATE via ``Op.GAS`` and stores
    ``(g0 - g1) - (g1 - g2)`` in slot 0. Identical work must cost
    the same — so the difference must be zero.

    Runtime measurement is required: the bug manifests as a
    child-frame state-gas spillover into ``gas_left`` (a runtime
    quantity), which static helpers like ``bytecode.gas_cost()``
    do not model.

    A non-zero result indicates ``compute_state_byte_diff`` is
    keying code-deposit accounting by hash via ``code_writes``,
    silently dropping the second CREATE's ``len(code) × CPSB``
    charge.
    """
    # Y init code returns memory[0:1] = 0x00 to deploy a 1-byte STOP.
    y_init = Op.PUSH1(1) + Op.PUSH1(0) + Op.RETURN
    y_size = len(bytes(y_init))

    # Memory layout:
    #   [ 0: 32) — Y init code (right-aligned PUSH32 padding)
    #   [32: 64) — g0 (gas before first CREATE)
    #   [64: 96) — g1 (gas between the two CREATEs)
    #   [96:128) — g2 (gas after second CREATE)
    factory_code = (
        Op.MSTORE(0, Op.PUSH32(bytes(y_init)))
        + Op.MSTORE(32, Op.GAS)
        + Op.POP(Op.CREATE(value=0, offset=32 - y_size, size=y_size))
        + Op.MSTORE(64, Op.GAS)
        + Op.POP(Op.CREATE(value=0, offset=32 - y_size, size=y_size))
        + Op.MSTORE(96, Op.GAS)
        + Op.SSTORE(
            0,
            Op.SUB(
                Op.SUB(Op.MLOAD(32), Op.MLOAD(64)),  # cost of CREATE 1
                Op.SUB(Op.MLOAD(64), Op.MLOAD(96)),  # cost of CREATE 2
            ),
        )
        + Op.STOP
    )

    factory_storage = Storage()
    factory_storage[0] = 0
    factory = pre.deploy_contract(code=factory_code, storage=factory_storage)

    tx = Transaction(
        to=factory,
        sender=pre.fund_eoa(),
        gas_limit=2_000_000,
    )

    state_test(
        pre=pre,
        post={factory: Account(storage=factory_storage)},
        tx=tx,
    )

Parametrized Test Cases

This test generates 1 parametrized test case across 1 fork.