Skip to content

test_create_address_collision()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py::test_create_address_collision@8acae1b0.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py::test_create_address_collision --fork Amsterdam

Test CREATE returns zero on address collision.

When CREATE targets an address that already has code or a non-zero nonce (EIP-684), the collision is detected early and returns zero without charging state gas. The existing account is left unchanged.

Requires mutable pre-alloc in order to prepare the collision that normally would require a hash-collision.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
@pytest.mark.pre_alloc_mutable
@pytest.mark.valid_from("EIP8037")
def test_create_address_collision(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
) -> None:
    """
    Test CREATE returns zero on address collision.

    When CREATE targets an address that already has code or a
    non-zero nonce (EIP-684), the collision is detected early and
    returns zero without charging state gas. The existing account is
    left unchanged.

    Requires mutable pre-alloc in order to prepare the collision that normally
    would require a hash-collision.
    """
    gas_limit_cap = fork.transaction_gas_limit_cap()
    assert gas_limit_cap is not None
    init_code = Op.STOP
    mstore_value, size = init_code_at_high_bytes(init_code)

    storage = Storage()
    factory_prefix_code = (
        Op.MSTORE(0, mstore_value, new_memory_size=32)
        # Fill state gas usage just enough to pass the execution gas
        + Op.SSTORE(storage.store_next(1), 1, original_value=0, new_value=1)
        + Op.SSTORE(storage.store_next(1), 1, original_value=0, new_value=1)
    )
    factory_create_code = Op.CREATE(
        0,
        0,
        size,
        # gas accounting
        init_code_size=size,
        account_new=False,
    )
    factory_code = factory_prefix_code + factory_create_code
    contract = pre.deploy_contract(code=factory_code)
    collision_target = compute_create_address(address=contract, nonce=1)
    pre[collision_target] = Account(nonce=1)

    state_gas = factory_prefix_code.state_cost(fork)
    # The collision burns all but a 64th of the factory's execution
    # gas, so half again its own cost keeps the trailing SSTORE alive.
    # Execution gas is at most what the limit has left once the state
    # charge is paid, so the assert keeps that burn under the state gas.
    gas_limit = (
        (
            fork.transaction_intrinsic_cost_calculator()()
            + factory_code.gas_cost(fork)
        )
        * 3
        // 2
    )
    assert factory_code.gas_cost(fork) - state_gas < state_gas, (
        "state gas must dominate"
    )

    tx = Transaction(
        to=contract,
        gas_limit=gas_limit,
        sender=pre.fund_eoa(),
    )

    post = {
        contract: Account(storage=storage),
        collision_target: Account(nonce=1, code=b""),
    }

    state_test(
        pre=pre,
        post=post,
        tx=tx,
        blockchain_test_header_verify=Header(gas_used=state_gas),
    )

Parametrized Test Cases

This test generates 1 parametrized test case across 1 fork.