Skip to content

test_creates_collisions()

Documentation for tests/benchmark/compute/instruction/test_system.py::test_creates_collisions@ffe48572.

Generate fixtures for these test cases for Bogota with:

fill -v tests/benchmark/compute/instruction/test_system.py::test_creates_collisions --gas-benchmark-values 1

Benchmark CREATE and CREATE2 instructions with collisions.

Source code in tests/benchmark/compute/instruction/test_system.py
@pytest.mark.parametrize(
    "opcode",
    [
        Op.CREATE,
        Op.CREATE2,
    ],
)
@pytest.mark.pre_alloc_mutable
def test_creates_collisions(
    benchmark_test: BenchmarkTestFiller,
    pre: Alloc,
    fork: Fork,
    opcode: Op,
    gas_benchmark_value: int,
    fixed_opcode_count: float | None,
) -> None:
    """Benchmark CREATE and CREATE2 instructions with collisions."""
    # We deploy a "proxy contract" which is the contract that will be called in
    # a loop using all the gas in the block. This "proxy contract" is the one
    # executing CREATE2 failing with a collision. The reason why we need a
    # "proxy contract" is that CREATE(2) failing with a collision will consume
    # all the available gas. If we try to execute the CREATE(2) directly
    # without being wrapped **and capped in gas** in a previous CALL, we would
    # run out of gas very fast!
    # The proxy contract calls CREATE(2) with empty initcode. The current call
    # frame gas will be exhausted because of the collision. For this reason the
    # caller will carefully give us the minimal gas necessary to execute the
    # CREATE(2) and not waste any extra gas in the CREATE(2)-failure.
    # Note that these CREATE(2) calls will fail because in (**) below we pre-
    # alloc contracts with the same address as the ones that CREATE(2) will try
    # to create.
    # The collision targets pre-exist (**), so per EIP-8037 the
    # CREATE(2) never charges NEW_ACCOUNT state gas.
    proxy_contract_code = (
        Op.CREATE2(
            value=Op.PUSH0,
            salt=Op.PUSH0,
            offset=Op.PUSH0,
            size=Op.PUSH0,
            # gas accounting
            account_new=False,
        )
        if opcode == Op.CREATE2
        else Op.CREATE(
            value=Op.PUSH0,
            offset=Op.PUSH0,
            size=Op.PUSH0,
            # gas accounting
            account_new=False,
        )
    )
    proxy_contract = pre.deploy_contract(code=proxy_contract_code)

    min_gas_required = proxy_contract_code.execution_cost(
        fork
    ) + proxy_contract_code.state_cost(fork)
    setup = Op.PUSH20(proxy_contract) + Op.PUSH3(min_gas_required)
    attack_block = Op.POP(
        # DUP7 refers to the PUSH3 above.
        # DUP7 refers to the proxy contract address.
        Op.CALL(gas=Op.DUP7, address=Op.DUP7)
    )

    # (**) We deploy the contract that CREATE(2) will attempt to create so any
    # attempt will fail.
    if opcode == Op.CREATE2:
        addr = compute_create2_address(
            address=proxy_contract, salt=0, initcode=[]
        )
        pre.deploy_contract(address=addr, code=Op.INVALID)
    else:
        creation_cost = proxy_contract_code.execution_cost(fork)
        max_contract_count = (
            2 * gas_benchmark_value // creation_cost
            if fixed_opcode_count is None
            else int(fixed_opcode_count * 1000)
        )
        for nonce in range(max_contract_count):
            addr = compute_create_address(address=proxy_contract, nonce=nonce)
            pre.deploy_contract(address=addr, code=Op.INVALID)

    benchmark_test(
        target_opcode=opcode,
        code_generator=JumpLoopGenerator(
            setup=setup, attack_block=attack_block
        ),
    )

Parametrized Test Cases

This test generates 2 parametrized test cases across 4 forks.