Skip to content

test_call_forwarded_gas_63_64()

Documentation for tests/amsterdam/eip8038_state_access_gas_cost_increase/test_call_gas.py::test_call_forwarded_gas_63_64@343274cc.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8038_state_access_gas_cost_increase/test_call_gas.py::test_call_forwarded_gas_63_64 --fork Amsterdam

Verify the 63/64 forwarding budget is computed after the repriced cold access charge.

A wrapper performs a cold, zero-value CALL requesting maximum gas. The spec charges the repriced COLD_ACCOUNT_ACCESS up front and only then forwards floor(63/64 * gas_left) to the child. The wrapper is handed an exact budget so that, net of the access charge, gas_left equals child_execution * 64 // 63; forwarding then yields exactly the child's execution need (child_execution) and its cold SSTORE takes effect. With one gas less the floor drops below child_execution and the child OOGs, so the slot stays zero. This pins that the floor is taken over gas_left already net of the post-8038 cold access cost (not before it, and not double-charging it).

Source code in tests/amsterdam/eip8038_state_access_gas_cost_increase/test_call_gas.py
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
@EIPChecklist.GasCostChanges.Test.GasUpdatesMeasurement()
@pytest.mark.parametrize(
    "sufficient_gas", [True, False], ids=["sufficient", "insufficient"]
)
def test_call_forwarded_gas_63_64(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
    sufficient_gas: bool,
) -> None:
    """
    Verify the 63/64 forwarding budget is computed after the repriced
    cold access charge.

    A wrapper performs a cold, zero-value ``CALL`` requesting maximum
    gas. The spec charges the repriced ``COLD_ACCOUNT_ACCESS``
    up front and only then forwards ``floor(63/64 * gas_left)`` to the
    child. The wrapper is handed an exact budget so that, net of the
    access charge, ``gas_left`` equals ``child_execution * 64 // 63``;
    forwarding then yields exactly the child's execution need
    (``child_execution``) and its cold ``SSTORE`` takes effect. With one
    gas less the floor drops below ``child_execution`` and the child OOGs,
    so the slot stays zero. This pins that the floor is taken over
    ``gas_left`` already net of the post-8038 cold access cost (not
    before it, and not double-charging it).
    """
    sstore_state_gas = Op.SSTORE(new_value=1).state_cost(fork)

    # Child: a single cold zero-to-nonzero SSTORE as proof of execution.
    # Its execution need is the two operand pushes plus the cold storage
    # write (the state portion is funded separately via the reservoir,
    # which is passed to the child in full with no 63/64 rule).
    child_code = Op.SSTORE(0, 1)
    child = pre.deploy_contract(child_code)
    child_execution = child_code.execution_cost(fork)

    # Smallest budget whose 63/64 floor still reaches `child_execution`.
    forward_budget = child_execution * 64 // 63
    if not sufficient_gas:
        forward_budget -= 1

    # Wrapper: cold zero-value CALL requesting max gas (so the forwarded
    # amount is bound by `gas_left`, not by the request). ret_size=0
    # avoids any memory-expansion term.
    wrapper_call = Op.CALL(
        gas=0xFFFFFFFF,
        address=child,
        value=0,
        args_offset=0,
        args_size=0,
        ret_offset=0,
        ret_size=0,
    )
    wrapper = pre.deploy_contract(wrapper_call)

    # At the wrapper's CALL the cold access charge is deducted first
    # (folded with the operand pushes into its execution cost), leaving
    # exactly `forward_budget` as `gas_left` for the 63/64 floor.
    wrapper_gas = wrapper_call.execution_cost(fork) + forward_budget

    # Outer caller hands the wrapper exactly `wrapper_gas`.
    caller = pre.deploy_contract(
        Op.POP(Op.CALL(gas=wrapper_gas, address=wrapper))
    )

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

    # Child SSTORE lands only when the forwarded floor reaches its need.
    post = {child: Account(storage={0: 1 if sufficient_gas else 0})}
    state_test(pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 2 parametrized test cases across 1 fork.