Skip to content

test_create_execution_gas()

Documentation for tests/amsterdam/eip8038_state_access_gas_cost_increase/test_create_gas.py::test_create_execution_gas@8acae1b0.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8038_state_access_gas_cost_increase/test_create_gas.py::test_create_execution_gas --fork Amsterdam

Measure the execution gas of CREATE/CREATE2 and assert the schedule.

The EIP-8038 execution dimension is CREATE_ACCESS plus the EIP-3860 init code word cost plus, for CREATE2 only, an additional keccak word cost. The EIP-8037 account-creation state gas is excluded by subtracting create_state_gas(0).

Source code in tests/amsterdam/eip8038_state_access_gas_cost_increase/test_create_gas.py
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
@EIPChecklist.GasCostChanges.Test.GasUpdatesMeasurement()
@pytest.mark.with_all_create_opcodes()
@pytest.mark.parametrize(
    "init_code_size",
    [
        pytest.param(0, id="empty"),
        pytest.param(32, id="one_word"),
        pytest.param(33, id="two_words"),
        pytest.param(96, id="three_words"),
    ],
)
def test_create_execution_gas(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
    create_opcode: Op,
    init_code_size: int,
) -> None:
    """
    Measure the execution gas of CREATE/CREATE2 and assert the schedule.

    The EIP-8038 *execution* dimension is ``CREATE_ACCESS`` plus the
    EIP-3860 init code word cost plus, for ``CREATE2`` only, an
    additional keccak word cost. The EIP-8037
    account-creation state gas is excluded by subtracting
    ``create_state_gas(0)``.
    """
    # Isolate the execution dimension: opcode total minus its account
    # creation state gas (the only state component carried by the CREATE
    # opcode itself; code deposit is charged on RETURN inside initcode).
    create_meta = create_opcode(init_code_size=init_code_size)
    execution_gas = create_meta.gas_cost(fork) - fork.create_state_gas(
        code_size=0
    )
    # Equivalent isolation via the execution_cost helper.
    assert execution_gas == create_meta.execution_cost(fork)

    # Runtime confirmation via CodeGasMeasure: a factory whose CREATE
    # deploys empty code, so no code-deposit state gas is charged and the
    # only state component is the account-creation gas funded from the
    # reservoir. The initcode is brought into memory BEFORE the measured
    # window, so the memory-expansion charge is excluded; the measured
    # value is the CREATE opcode's execution cost exactly. The overhead
    # subtracts the create-call argument pushes (the create leaves one
    # stack item, its result).
    #
    # The initcode is all-zero bytes (`STOP`), so the child frame halts
    # immediately consuming zero gas and deposits empty code. This keeps
    # the measured value the CREATE opcode's own execution cost, with no
    # child-execution gas folded in. `init_code_size` still drives the
    # opcode's per-init-word charge.
    padded_init = b"\x00" * init_code_size

    create_call = (
        Op.CREATE2(value=0, offset=0, size=init_code_size, salt=0)
        if create_opcode == Op.CREATE2
        else Op.CREATE(value=0, offset=0, size=init_code_size)
    )
    push_cost = Op.PUSH1(0).execution_cost(fork)
    arg_pushes = (4 if create_opcode == Op.CREATE2 else 3) * push_cost

    memory_setup = (
        Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE, new_memory_size=init_code_size)
        if init_code_size
        else Bytecode()
    )
    storage = Storage()
    measure = CodeGasMeasure(
        code=create_call,
        overhead_cost=arg_pushes,
        extra_stack_items=1,
        sstore_key=storage.store_next(execution_gas, "create_execution_gas"),
    )
    factory = pre.deploy_contract(code=memory_setup + measure)

    tx = Transaction(
        to=factory,
        data=padded_init,
        # Reservoir funds the account-creation state gas; leaving
        # gas_limit unset keeps `Op.GAS` honest about gas_left.
        state_gas_reservoir=fork.create_state_gas(code_size=0),
        sender=pre.fund_eoa(),
    )

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

Parametrized Test Cases

This test generates 8 parametrized test cases across 1 fork.