Skip to content

test_nested_create_fail_parent_revert_state_gas()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py::test_nested_create_fail_parent_revert_state_gas@87aba1a3.

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_nested_create_fail_parent_revert_state_gas --fork Amsterdam

Verify factory nonce is rolled back when the factory reverts after a failed inner CREATE, and preserved when the factory returns.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
@pytest.mark.parametrize(
    "parent_reverts",
    [
        pytest.param(True, id="parent_reverts"),
        pytest.param(False, id="parent_succeeds"),
    ],
)
@pytest.mark.parametrize(
    "child_failure",
    [
        pytest.param("revert", id="child_revert"),
        pytest.param("halt", id="child_halt"),
    ],
)
@pytest.mark.with_all_create_opcodes()
@pytest.mark.valid_from("EIP8037")
def test_nested_create_fail_parent_revert_state_gas(
    blockchain_test: BlockchainTestFiller,
    pre: Alloc,
    fork: Fork,
    parent_reverts: bool,
    child_failure: str,
    create_opcode: Op,
) -> None:
    """
    Verify factory nonce is rolled back when the factory reverts after
    a failed inner CREATE, and preserved when the factory returns.
    """
    gas_costs = fork.gas_costs()
    create_state_gas = gas_costs.NEW_ACCOUNT

    if child_failure == "revert":
        init_code = Op.REVERT(0, 0)
    else:
        init_code = Op.INVALID

    create_call = (
        create_opcode(value=0, offset=0, size=len(init_code), salt=0)
        if create_opcode == Op.CREATE2
        else create_opcode(value=0, offset=0, size=len(init_code))
    )

    factory = pre.deploy_contract(
        code=(
            Op.MSTORE(
                0,
                int.from_bytes(bytes(init_code), "big")
                << (256 - 8 * len(init_code)),
            )
            + Op.POP(create_call)
            + (Op.REVERT(0, 0) if parent_reverts else Op.STOP)
        ),
    )

    # Nested CALL required so the child-error path has a parent
    # frame to receive the restored state gas.
    caller = pre.deploy_contract(
        code=Op.POP(Op.CALL(gas=500_000, address=factory)),
    )

    tx = Transaction(
        to=caller,
        state_gas_reservoir=create_state_gas,
        sender=pre.fund_eoa(),
    )

    inner_address = compute_create_address(
        address=factory,
        nonce=1,
        salt=0,
        initcode=bytes(init_code),
        opcode=create_opcode,
    )

    if parent_reverts:
        post = {
            factory: Account(nonce=1),
            inner_address: Account.NONEXISTENT,
        }
    else:
        post = {
            factory: Account(nonce=2),
            inner_address: Account.NONEXISTENT,
        }

    blockchain_test(
        pre=pre,
        blocks=[Block(txs=[tx])],
        post=post,
    )

Parametrized Test Cases

This test generates 8 parametrized test cases across 1 fork.