Skip to content

test_selfdestruct_created()

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

Generate fixtures for these test cases for Bogota with:

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

Benchmark SELFDESTRUCT instruction for contracts created in same tx.

Source code in tests/benchmark/compute/instruction/test_system.py
@pytest.mark.parametrize("value_bearing", [True, False])
def test_selfdestruct_created(
    benchmark_test: BenchmarkTestFiller,
    pre: Alloc,
    value_bearing: bool,
    fork: Fork,
    gas_benchmark_value: int,
) -> None:
    """Benchmark SELFDESTRUCT instruction for contracts created in same tx."""
    selfdestructable_contract = Op.SELFDESTRUCT(Op.CALLER, address_warm=True)

    # Initcode
    initcode = (
        Op.MSTORE8(
            0,
            Op.CALLER.int(),
            # gas accounting
            old_memory_size=0,
            new_memory_size=2,
        )
        + Op.MSTORE8(1, Op.SELFDESTRUCT.int())
        + Op.RETURN(0, 2, code_deposit_size=2)
    )

    # CALLDATA[0:32] = iteration_count
    setup = (
        Op.MSTORE(
            0,
            initcode.hex(),
            # Gas accounting
            old_memory_size=0,
            new_memory_size=32,
        )
        + Op.CALLDATALOAD(0)
        + Op.PUSH0
    )

    loop = While(
        body=Op.POP(
            Op.CALL(
                address=Op.CREATE(
                    value=1 if value_bearing else 0,
                    offset=32 - len(initcode),
                    size=len(initcode),
                    init_code_size=len(initcode),
                ),
                address_warm=True,
            )
        ),
        condition=Op.PUSH1(1) + Op.ADD + Op.DUP1 + Op.DUP3 + Op.GT,
    )

    attack_code = IteratingBytecode(
        setup=setup,
        iterating=loop,
        iterating_subcall=initcode + selfdestructable_contract,
        cleanup=Op.STOP,
    )

    def calldata(iteration_count: int, start_iteration: int) -> bytes:
        del start_iteration
        return Hash(iteration_count)

    iteration_counts = list(
        attack_code.tx_iterations_by_gas_limit(
            fork=fork,
            gas_limit=gas_benchmark_value,
            calldata=calldata,
        )
    )
    num_iterations = sum(iteration_counts)

    attack_code_address = pre.deploy_contract(
        code=attack_code,
        balance=num_iterations if value_bearing else 0,
    )

    with TestPhaseManager.execution():
        sender = pre.fund_eoa()
        exec_txs = list(
            attack_code.transactions_by_gas_limit(
                fork=fork,
                gas_limit=gas_benchmark_value,
                sender=sender,
                to=attack_code_address,
                calldata=calldata,
            )
        )

    total_gas_cost = sum(tx.gas_cost for tx in exec_txs)

    post = {
        attack_code_address: Account(
            balance=num_iterations if value_bearing else 0
        )
    }

    benchmark_test(
        post=post,
        target_opcode=Op.SELFDESTRUCT,
        blocks=[
            Block(txs=exec_txs),
        ],
        expected_benchmark_gas_used=total_gas_cost,
    )

Parametrized Test Cases

This test generates 2 parametrized test cases across 4 forks.