Skip to content

test_extcodehash_call_to_selfdestruct()

Documentation for tests/constantinople/eip1052_extcodehash/test_extcodehash.py::test_extcodehash_call_to_selfdestruct@ffe48572.

Generate fixtures for these test cases for Bogota with:

fill -v tests/constantinople/eip1052_extcodehash/test_extcodehash.py::test_extcodehash_call_to_selfdestruct --fork Bogota

Test EXTCODEHASH after calling a contract that selfdestructs.

Call a contract containing SELFDESTRUCT using each call type, then check EXTCODEHASH. The hash is always returned because the check happens within the same transaction. STATICCALL fails because SELFDESTRUCT modifies state. Pre-Cancun, CALLCODE/DELEGATECALL execute SELFDESTRUCT in the caller's context, destroying the test contract at end of transaction.

Source code in tests/constantinople/eip1052_extcodehash/test_extcodehash.py
@pytest.mark.ported_from(
    [
        "https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stExtCodeHash/callToSuicideThenExtcodehashFiller.json",  # noqa: E501
    ],
    pr=["https://github.com/ethereum/execution-specs/pull/2412"],
)
@pytest.mark.with_all_call_opcodes
def test_extcodehash_call_to_selfdestruct(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
    call_opcode: Opcodes,
) -> None:
    """
    Test EXTCODEHASH after calling a contract that selfdestructs.

    Call a contract containing SELFDESTRUCT using each call type, then
    check EXTCODEHASH. The hash is always returned because the check
    happens within the same transaction. STATICCALL fails because
    SELFDESTRUCT modifies state. Pre-Cancun, CALLCODE/DELEGATECALL
    execute SELFDESTRUCT in the caller's context, destroying the test
    contract at end of transaction.
    """
    storage = Storage()
    beneficiary = pre.nonexistent_account()
    target_code = Op.SELFDESTRUCT(beneficiary)
    target = pre.deploy_contract(target_code, balance=5_555_555_555)

    call_succeeds = call_opcode != Op.STATICCALL

    code = Op.SSTORE(
        storage.store_next(int(call_succeeds)),
        call_opcode(address=target),
    ) + Op.SSTORE(
        storage.store_next(target_code.keccak256()),
        Op.EXTCODEHASH(target),
    )

    code_address = pre.deploy_contract(code, storage=storage.canary())

    tx = Transaction(sender=pre.fund_eoa(), to=code_address)

    # Pre-Cancun, CALLCODE/DELEGATECALL execute SELFDESTRUCT in the
    # caller's context, destroying the test contract at end of tx.
    caller_destroyed = fork < Cancun and call_opcode in (
        Op.CALLCODE,
        Op.DELEGATECALL,
    )

    post: dict[Address, Account | None] = {}
    if caller_destroyed:
        post[code_address] = Account.NONEXISTENT
    else:
        post[code_address] = Account(storage=storage)

    state_test(pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 4 parametrized test cases across 11 forks.