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@892e6d1e.

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
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
@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,
        gas_limit=1_000_000,
    )

    # 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.