Skip to content

test_bal_gas_limit_boundary()

Documentation for tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py::test_bal_gas_limit_boundary@622620d8.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py::test_bal_gas_limit_boundary --fork Amsterdam

BAL max-items cap (bal_items <= block_gas_limit // BLOCK_ACCESS_LIST_ITEM) must be enforced on the final BAL — including pre-tx system work (beacon root, history), user txs, and post-tx system work (CL withdrawals, queue processing).

Orthogonal axes: - with_tx: alice → bob transfer adds 3 items (alice + bob + coinbase warmed via EIP-3651). - with_cl_withdrawal: EIP-4895 withdrawal to a recipient adds 1 item, processed between txs and the rest of the post-tx system work. Together they catch clients that validate the cap before process_withdrawals runs.

Source code in tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
@EIPChecklist.BlockLevelConstraint.Test.Boundary.Under()
@EIPChecklist.BlockLevelConstraint.Test.Boundary.Exact()
@EIPChecklist.BlockLevelConstraint.Test.Boundary.Over()
@pytest.mark.parametrize(
    "with_cl_withdrawal",
    [
        pytest.param(False, id="no_cl_withdrawal"),
        pytest.param(True, id="with_cl_withdrawal"),
    ],
)
@pytest.mark.parametrize(
    "with_tx",
    [pytest.param(False, id="no_tx"), pytest.param(True, id="with_tx")],
)
@pytest.mark.parametrize(
    "boundary_offset",
    [
        pytest.param(0, id="at_boundary"),
        pytest.param(
            -1, marks=pytest.mark.exception_test, id="below_boundary"
        ),
    ],
)
def test_bal_gas_limit_boundary(
    blockchain_test: BlockchainTestFiller,
    pre: Alloc,
    fork: Fork,
    boundary_offset: int,
    with_tx: bool,
    with_cl_withdrawal: bool,
) -> None:
    """
    BAL max-items cap (``bal_items <= block_gas_limit //
    BLOCK_ACCESS_LIST_ITEM``) must be enforced on the **final** BAL —
    including pre-tx system work (beacon root, history), user txs, and
    post-tx system work (CL withdrawals, queue processing).

    Orthogonal axes:
    - `with_tx`: alice → bob transfer adds 3 items (alice + bob +
      coinbase warmed via EIP-3651).
    - `with_cl_withdrawal`: EIP-4895 withdrawal to a recipient adds 1
      item, processed between txs and the rest of the post-tx system
      work. Together they catch clients that validate the cap before
      `process_withdrawals` runs.
    """
    # Match framework's DEFAULT_BASE_FEE so gas_price == base_fee
    # cancels the priority fee (no coinbase balance_change to absorb
    # into the expected counts).
    base_fee_per_gas = 7

    extra_items = 0
    txs: list = []
    withdrawals: list = []
    expected_accounts: dict = {}
    post: dict = {}

    if with_tx:
        alice = pre.fund_eoa()
        # Fund bob with 1 wei so the recipient is alive at top-frame
        # check time; this avoids the EIP-2780 ``NEW_ACCOUNT`` state
        # charge that would otherwise inflate the tx's gas needs past
        # the BAL-sized ``block_gas_limit``.
        bob = pre.fund_eoa(amount=1)
        # alice (sender) + bob (recipient) + coinbase (EIP-3651 warm).
        extra_items += 3
        txs.append(
            Transaction(
                sender=alice,
                to=bob,
                value=1,
                gas_price=base_fee_per_gas,
            )
        )
        expected_accounts[alice] = BalAccountExpectation(
            nonce_changes=[BalNonceChange(block_access_index=1, post_nonce=1)],
        )
        expected_accounts[bob] = BalAccountExpectation(
            balance_changes=[
                BalBalanceChange(block_access_index=1, post_balance=2)
            ],
        )
        post[bob] = Account(balance=2)

    if with_cl_withdrawal:
        charlie = pre.fund_eoa(amount=0)
        withdrawal_amount_wei = 10**9  # 1 gwei
        # CL withdrawal recipient adds 1 item; processed at
        # block_access_index = len(txs) + 1 (post-tx).
        extra_items += 1
        withdrawals.append(
            Withdrawal(index=0, validator_index=0, address=charlie, amount=1)
        )
        expected_accounts[charlie] = BalAccountExpectation(
            balance_changes=[
                BalBalanceChange(
                    block_access_index=len(txs) + 1,
                    post_balance=withdrawal_amount_wei,
                )
            ],
        )
        post[charlie] = Account(balance=withdrawal_amount_wei)

    total_items = fork.empty_block_bal_item_count() + extra_items
    gas_limit = (
        total_items * fork.gas_costs().BLOCK_ACCESS_LIST_ITEM + boundary_offset
    )

    at_boundary = boundary_offset == 0
    block = Block(
        txs=txs,
        withdrawals=withdrawals,
        exception=(
            None
            if at_boundary
            else BlockException.BLOCK_ACCESS_LIST_GAS_LIMIT_EXCEEDED
        ),
        expected_block_access_list=(
            BlockAccessListExpectation(account_expectations=expected_accounts)
            if at_boundary and expected_accounts
            else None
        ),
    )

    blockchain_test(
        pre=pre,
        blocks=[block],
        post=post if at_boundary else {},
        genesis_environment=Environment(
            base_fee_per_gas=base_fee_per_gas, gas_limit=gas_limit
        ),
    )

Parametrized Test Cases

This test generates 8 parametrized test cases across 1 fork.