Skip to content

Commit 88370ff

Browse files
committed
feat: Validate t8n BAL does not have duplicate entries for the same tx_index
1 parent d446684 commit 88370ff

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

src/ethereum_test_types/block_access_list/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,15 @@ def _validate_bal_ordering(bal: "BlockAccessList") -> None:
360360
f"Account {account.address}, Slot {slot_change.slot}: "
361361
f"tx_indices not in ascending order. Got: {tx_indices}"
362362
)
363+
# Check for duplicate indices - each slot change must have a
364+
# unique tx_index
365+
if len(tx_indices) != len(set(tx_indices)):
366+
duplicates = [idx for idx in tx_indices if tx_indices.count(idx) > 1]
367+
raise BlockAccessListValidationError(
368+
f"Account {account.address}, Slot {slot_change.slot}: "
369+
f"has duplicate tx_indices. Got: {tx_indices}, "
370+
f"Duplicates: {list(set(duplicates))}"
371+
)
363372

364373
# Check storage reads are in lexicographic order
365374
if account.storage_reads:
@@ -385,6 +394,12 @@ def _validate_bal_ordering(bal: "BlockAccessList") -> None:
385394
f"Account {account.address}: {field_name} tx_indices "
386395
f"not in ascending order. Got: {tx_indices}"
387396
)
397+
if len(tx_indices) != len(set(tx_indices)):
398+
duplicates = [idx for idx in tx_indices if tx_indices.count(idx) > 1]
399+
raise BlockAccessListValidationError(
400+
f"Account {account.address}: {field_name} has duplicate tx_indices. "
401+
f"Got: {tx_indices}, Duplicates: {list(set(duplicates))}"
402+
)
388403

389404
def _compare_account_expectations(
390405
self, expected: BalAccountExpectation, actual: BalAccountChange

src/ethereum_test_types/tests/test_block_access_lists.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,78 @@ def test_actual_bal_tx_indices_ordering(field_name):
341341
expectation.verify_against(actual_bal)
342342

343343

344+
@pytest.mark.parametrize(
345+
"field_name",
346+
["nonce_changes", "balance_changes", "code_changes"],
347+
)
348+
def test_actual_bal_duplicate_tx_indices(field_name):
349+
"""Test that actual BAL must not have duplicate tx indices in change lists."""
350+
addr = Address(0xA)
351+
352+
# Duplicate tx_index=1
353+
changes = []
354+
if field_name == "nonce_changes":
355+
changes = [
356+
BalNonceChange(tx_index=1, post_nonce=1),
357+
BalNonceChange(tx_index=1, post_nonce=2), # duplicate tx_index
358+
BalNonceChange(tx_index=2, post_nonce=3),
359+
]
360+
elif field_name == "balance_changes":
361+
changes = [
362+
BalBalanceChange(tx_index=1, post_balance=100),
363+
BalBalanceChange(tx_index=1, post_balance=200), # duplicate tx_index
364+
BalBalanceChange(tx_index=2, post_balance=300),
365+
]
366+
elif field_name == "code_changes":
367+
changes = [
368+
BalCodeChange(tx_index=1, new_code=b"code1"),
369+
BalCodeChange(tx_index=1, new_code=b""), # duplicate tx_index
370+
BalCodeChange(tx_index=2, new_code=b"code2"),
371+
]
372+
373+
actual_bal = BlockAccessList([BalAccountChange(address=addr, **{field_name: changes})])
374+
375+
expectation = BlockAccessListExpectation(account_expectations={})
376+
377+
with pytest.raises(
378+
BlockAccessListValidationError,
379+
match=f"{field_name} has duplicate tx_indices.*Duplicates: \\[1\\]",
380+
):
381+
expectation.verify_against(actual_bal)
382+
383+
384+
def test_actual_bal_storage_duplicate_tx_indices():
385+
"""Test that storage changes must not have duplicate tx indices within same slot."""
386+
addr = Address(0xA)
387+
388+
# Create storage changes with duplicate tx_index within the same slot
389+
actual_bal = BlockAccessList(
390+
[
391+
BalAccountChange(
392+
address=addr,
393+
storage_changes=[
394+
BalStorageSlot(
395+
slot=0x01,
396+
slot_changes=[
397+
BalStorageChange(tx_index=1, post_value=0x100),
398+
BalStorageChange(tx_index=1, post_value=0x200), # duplicate tx_index
399+
BalStorageChange(tx_index=2, post_value=0x300),
400+
],
401+
)
402+
],
403+
)
404+
]
405+
)
406+
407+
expectation = BlockAccessListExpectation(account_expectations={})
408+
409+
with pytest.raises(
410+
BlockAccessListValidationError,
411+
match="Slot 0x.*1: has duplicate tx_indices.*Duplicates: \\[1\\]",
412+
):
413+
expectation.verify_against(actual_bal)
414+
415+
344416
def test_expected_addresses_auto_sorted():
345417
"""
346418
Test that expected addresses are automatically sorted before comparison.

0 commit comments

Comments
 (0)