Skip to content

test_bal_create_storage_op_then_selfdestruct_same_tx()

Documentation for tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_opcodes.py::test_bal_create_storage_op_then_selfdestruct_same_tx@c74f1a67.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_opcodes.py::test_bal_create_storage_op_then_selfdestruct_same_tx --fork Amsterdam

Same-tx CREATE/CREATE2 + storage_op + SELFDESTRUCT.

The deterministic address A is pre-funded. A single tx deploys a contract at A via the parametrized create opcode; init code performs SLOAD or SSTORE on slot B then SELFDESTRUCTs. Because the contract is destroyed in the same tx, slot B MUST appear in storage_reads and MUST NOT appear in storage_changes (writes demoted to reads per EIP-7928).

Source code in tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_opcodes.py
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
@pytest.mark.with_all_create_opcodes
@pytest.mark.parametrize(
    "storage_op",
    ["read", "write"],
    ids=["sload_then_selfdestruct", "sstore_then_selfdestruct"],
)
def test_bal_create_storage_op_then_selfdestruct_same_tx(
    pre: Alloc,
    blockchain_test: BlockchainTestFiller,
    create_opcode: Op,
    storage_op: str,
) -> None:
    """
    Same-tx CREATE/CREATE2 + storage_op + SELFDESTRUCT.

    The deterministic address A is pre-funded. A single tx deploys a
    contract at A via the parametrized create opcode; init code performs
    SLOAD or SSTORE on slot B then SELFDESTRUCTs. Because the contract
    is destroyed in the same tx, slot B MUST appear in `storage_reads`
    and MUST NOT appear in `storage_changes` (writes demoted to reads
    per EIP-7928).
    """
    alice = pre.fund_eoa()
    beneficiary = pre.fund_eoa(amount=0)
    fund_amount = 100
    slot_b = 0x07

    if storage_op == "read":
        initcode = Op.POP(Op.SLOAD(slot_b)) + Op.SELFDESTRUCT(beneficiary)
    else:
        initcode = Op.SSTORE(slot_b, 0xCAFE) + Op.SELFDESTRUCT(beneficiary)
    initcode_bytes = bytes(initcode)

    salt = 0
    is_create2 = create_opcode == Op.CREATE2
    if is_create2:
        deploy_op = Op.CREATE2(
            value=0, offset=0, size=Op.CALLDATASIZE, salt=salt
        )
    else:
        deploy_op = Op.CREATE(value=0, offset=0, size=Op.CALLDATASIZE)

    factory_code = (
        Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE)
        + Op.SSTORE(0, deploy_op)
        + Op.STOP
    )
    factory = pre.deploy_contract(code=factory_code)
    target_a = compute_create_address(
        address=factory,
        nonce=1,
        salt=salt,
        initcode=initcode_bytes,
        opcode=create_opcode,
    )
    pre.fund_address(target_a, fund_amount)

    tx = Transaction(sender=alice, to=factory, data=initcode_bytes)

    block = Block(
        txs=[tx],
        expected_block_access_list=BlockAccessListExpectation(
            account_expectations={
                target_a: BalAccountExpectation(
                    balance_changes=[
                        BalBalanceChange(block_access_index=1, post_balance=0),
                    ],
                    storage_reads=[slot_b],
                    storage_changes=[],
                    code_changes=[],
                    nonce_changes=[],
                ),
                beneficiary: BalAccountExpectation(
                    balance_changes=[
                        BalBalanceChange(
                            block_access_index=1,
                            post_balance=fund_amount,
                        )
                    ],
                ),
            }
        ),
    )

    blockchain_test(
        pre=pre,
        blocks=[block],
        post={
            target_a: Account.NONEXISTENT,
            beneficiary: Account(balance=fund_amount),
            factory: Account(nonce=2, storage={0: target_a}),
        },
    )

Parametrized Test Cases

This test generates 4 parametrized test cases across 1 fork.