Skip to content

test_bal_lexicographic_address_ordering()

Documentation for tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py::test_bal_lexicographic_address_ordering@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_lexicographic_address_ordering --fork Amsterdam

Test BAL enforces strict lexicographic byte-wise address ordering.

Addresses: addr_low (0x...020000), addr_mid (0x...02000000), addr_high (0x20...00). Endian-trap: addr_endian_low (0x01...02), addr_endian_high (0x02...01). Contract touches them in reverse order to verify sorting.

Expected BAL order: low < mid < high < endian_low < endian_high. Catches endianness bugs in address comparison.

Source code in tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
@pytest.mark.pre_alloc_mutable()
def test_bal_lexicographic_address_ordering(
    pre: Alloc,
    blockchain_test: BlockchainTestFiller,
) -> None:
    """
    Test BAL enforces strict lexicographic byte-wise address ordering.

    Addresses: addr_low (0x...020000), addr_mid (0x...02000000),
    addr_high (0x20...00). Endian-trap: addr_endian_low (0x01...02),
    addr_endian_high (0x02...01). Contract touches them in reverse
    order to verify sorting.

    Expected BAL order: low < mid < high < endian_low < endian_high.
    Catches endianness bugs in address comparison.
    """
    alice = pre.fund_eoa()

    # Create addresses with specific byte patterns for lexicographic testing
    # In lexicographic (byte-wise) order: low < mid < high
    # addr_low:  0x00...020000 (0x02 in third-rightmost byte)
    # addr_mid:  0x00...02000000 (0x02 in fourth-rightmost byte)
    # addr_high: 0x20...00 (leftmost byte = 0x20)
    # Note: Using 0x2xxxx addresses to avoid precompiles (0x01-0x11, 0x100)
    addr_low = Address("0x0000000000000000000000000000000000020000")
    addr_mid = Address("0x0000000000000000000000000000000002000000")
    addr_high = Address("0x2000000000000000000000000000000000000000")

    # Endian-trap addresses: byte-reversals to catch byte-order bugs
    # addr_endian_low:  0x01...02 (0x01 at byte 0, 0x02 at byte 19)
    # addr_endian_high: 0x02...01 (0x02 at byte 0, 0x01 at byte 19)
    # Note: reverse(addr_endian_low) = addr_endian_high
    # Correct order: endian_low < endian_high (0x01 < 0x02 at byte 0)
    # Reversed bytes would incorrectly get opposite order
    addr_endian_low = Address("0x0100000000000000000000000000000000000002")
    addr_endian_high = Address("0x0200000000000000000000000000000000000001")

    # Give each address a balance so they exist
    addr_balance = 100
    pre[addr_low] = Account(balance=addr_balance)
    pre[addr_mid] = Account(balance=addr_balance)
    pre[addr_high] = Account(balance=addr_balance)
    pre[addr_endian_low] = Account(balance=addr_balance)
    pre[addr_endian_high] = Account(balance=addr_balance)

    # Contract that accesses addresses in REVERSE lexicographic order
    # to verify sorting is applied correctly
    contract_code = (
        Op.BALANCE(addr_high)  # Access high first
        + Op.POP
        + Op.BALANCE(addr_low)  # Access low second
        + Op.POP
        + Op.BALANCE(addr_mid)  # Access mid third
        + Op.POP
        # Access endian-trap addresses in reverse order
        + Op.BALANCE(addr_endian_high)  # Access endian_high before endian_low
        + Op.POP
        + Op.BALANCE(addr_endian_low)
        + Op.POP
        + Op.STOP
    )

    contract = pre.deploy_contract(code=contract_code)

    tx = Transaction(sender=alice, to=contract)

    # BAL must be sorted lexicographically by address bytes
    # Order: low < mid < high < endian_low < endian_high
    # (sorted by raw address bytes, regardless of access order)
    block = Block(
        txs=[tx],
        expected_block_access_list=BlockAccessListExpectation(
            account_expectations={
                alice: BalAccountExpectation(
                    nonce_changes=[
                        BalNonceChange(block_access_index=1, post_nonce=1)
                    ],
                ),
                contract: BalAccountExpectation.empty(),
                # These addresses appear in BAL due to BALANCE access
                # The expectation framework verifies correct order
                addr_low: BalAccountExpectation.empty(),
                addr_mid: BalAccountExpectation.empty(),
                addr_high: BalAccountExpectation.empty(),
                # Endian-trap addresses: must be sorted correctly despite being
                # byte-reversals of each other
                addr_endian_low: BalAccountExpectation.empty(),
                addr_endian_high: BalAccountExpectation.empty(),
            }
        ),
    )

    blockchain_test(
        pre=pre,
        blocks=[block],
        post={
            alice: Account(nonce=1),
            contract: Account(),
            addr_low: Account(balance=addr_balance),
            addr_mid: Account(balance=addr_balance),
            addr_high: Account(balance=addr_balance),
            addr_endian_low: Account(balance=addr_balance),
            addr_endian_high: Account(balance=addr_balance),
        },
    )

Parametrized Test Cases

This test generates 1 parametrized test case across 1 fork.