Skip to content

test_call_value_to_pre_existing_selfdestructed_account()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py::test_call_value_to_pre_existing_selfdestructed_account@c74f1a67.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py::test_call_value_to_pre_existing_selfdestructed_account --fork Amsterdam

Verify CALL with value to a pre existing contract that ran SELFDESTRUCT charges no new account state gas.

Per EIP-6780 a pre existing contract that executes SELFDESTRUCT is not queued for end of the transaction destruction, so a subsequent CALL sees an existing, code carrying account and the new account creation gate does not fire.

Several cold SSTOREs after the CALLs make block state gas dominate the block regular gas component, so the block header reflects exactly num_probes * sstore_state_gas. A spurious new account charge on the value bearing CALL would push the header up by that charge, breaking the assertion.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
@pytest.mark.parametrize(
    "beneficiary_type",
    [
        pytest.param("eoa", id="eoa_beneficiary"),
        pytest.param("contract", id="contract_beneficiary"),
    ],
)
@pytest.mark.valid_from("EIP8037")
def test_call_value_to_pre_existing_selfdestructed_account(
    blockchain_test: BlockchainTestFiller,
    pre: Alloc,
    fork: Fork,
    beneficiary_type: str,
) -> None:
    """
    Verify CALL with value to a pre existing contract that ran
    SELFDESTRUCT charges no new account state gas.

    Per EIP-6780 a pre existing contract that executes SELFDESTRUCT
    is not queued for end of the transaction destruction, so a
    subsequent CALL sees an existing, code carrying account and the
    new account creation gate does not fire.

    Several cold SSTOREs after the CALLs make block state gas
    dominate the block regular gas component, so the block header
    reflects exactly `num_probes * sstore_state_gas`. A spurious
    new account charge on the value bearing CALL would push the
    header up by that charge, breaking the assertion.
    """
    sstore_state_gas = Op.SSTORE(new_value=1).state_cost(fork)

    # Enough probes that the combined probe state gas dominates the
    # transaction's regular gas component and the header reflects
    # block state gas alone.
    num_probes = 6
    probe_state_gas = num_probes * sstore_state_gas

    # Beneficiary must be alive so the target's SELFDESTRUCT itself
    # does not charge for creating a new beneficiary.
    beneficiary: Address = (
        pre.fund_eoa(amount=1)
        if beneficiary_type == "eoa"
        else pre.deploy_contract(code=Op.STOP)
    )
    target = pre.deploy_contract(
        code=Op.SELFDESTRUCT(beneficiary),
        balance=1,
    )

    probes = Bytecode()
    for slot in range(num_probes):
        probes += Op.SSTORE(slot, 1)
    orchestrator = pre.deploy_contract(
        code=(
            Op.POP(Op.CALL(gas=Op.GAS, address=target))
            + Op.POP(Op.CALL(gas=Op.GAS, address=target, value=1))
            + probes
        ),
        balance=3,
    )

    tx = Transaction(
        to=orchestrator,
        state_gas_reservoir=probe_state_gas,
        sender=pre.fund_eoa(),
    )

    blockchain_test(
        pre=pre,
        blocks=[
            Block(
                txs=[tx],
                header_verify=Header(gas_used=probe_state_gas),
            ),
        ],
        post={},
    )

Parametrized Test Cases

This test generates 2 parametrized test cases across 1 fork.