Skip to content

test_calldatacopy_word_copy_oog()

Documentation for tests/frontier/opcodes/test_data_copy_oog.py::test_calldatacopy_word_copy_oog@b47f0253.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/frontier/opcodes/test_data_copy_oog.py::test_calldatacopy_word_copy_oog --fork Amsterdam

Test that CALLDATACOPY properly consumes gas for word copy cost.

Uses a sub-call with controlled gas to isolate the test from intrinsic gas costs that vary across forks.

Source code in tests/frontier/opcodes/test_data_copy_oog.py
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
@pytest.mark.parametrize(
    "subcall_gas,expect_success",
    [
        pytest.param(
            5000,
            True,
            id="sufficient_gas",
        ),
        pytest.param(
            # Enough for: MSTORE + memory expansion + static CALLDATACOPY
            # But NOT enough for word copy cost (3 gas per 32-byte word)
            150,
            False,
            id="insufficient_gas_for_word_copy_cost",
        ),
    ],
)
def test_calldatacopy_word_copy_oog(
    state_test: StateTestFiller,
    pre: Alloc,
    subcall_gas: int,
    expect_success: bool,
) -> None:
    """
    Test that CALLDATACOPY properly consumes gas for word copy cost.

    Uses a sub-call with controlled gas to isolate the test from intrinsic
    gas costs that vary across forks.
    """
    storage = Storage()
    storage_key = storage.store_next(1 if expect_success else 0)

    # Inner contract: performs CALLDATACOPY and stores success marker
    inner_code = (
        # Pre-expand memory to cover COPY_SIZE
        Op.MSTORE(COPY_SIZE - 0x20, 0)
        # CALLDATACOPY - should consume word copy gas
        + Op.CALLDATACOPY(dest_offset=0, offset=0, size=COPY_SIZE)
        # If we reach here, sufficient gas was available
        + Op.MSTORE8(0, 1)
        + Op.RETURN(0, 1)
    )
    inner_address = pre.deploy_contract(inner_code)

    # Outer contract: calls inner with controlled gas, stores call success
    # CALL pushes 1 on success, 0 on OOG/revert
    # Stack after CALL: [success]
    # PUSH key, then SSTORE pops [key, value] -> storage[key] = value
    outer_code = (
        Op.CALL(
            gas=subcall_gas,
            address=inner_address,
            value=0,
            args_offset=0,
            args_size=0,
            ret_offset=0,
            ret_size=1,
        )
        # Stack: [success (0 or 1)]
        + Op.PUSH1[storage_key]
        # Stack: [storage_key, success]
        + Op.SSTORE
        # Stores storage[storage_key] = success
        + Op.STOP
    )
    outer_address = pre.deploy_contract(outer_code)

    sender = pre.fund_eoa()

    tx = Transaction(
        to=outer_address,
        sender=sender,
        gas_limit=500_000,  # Plenty of gas for outer call
    )

    post = {outer_address: Account(storage=storage)}

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

Parametrized Test Cases

This test generates 2 parametrized test cases across 12 forks.