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@c74f1a67.

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
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
@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.