Skip to content

Commit 49ea239

Browse files
committed
chore: Unit test BAL ordering validation checks
1 parent ff8db69 commit 49ea239

File tree

1 file changed

+287
-1
lines changed

1 file changed

+287
-1
lines changed

src/ethereum_test_types/tests/test_block_access_lists.py

Lines changed: 287 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
import pytest
44

5-
from ethereum_test_base_types import Address
5+
from ethereum_test_base_types import Address, StorageKey
66
from ethereum_test_types.block_access_list import (
77
BalAccountChange,
88
BalAccountExpectation,
99
BalBalanceChange,
10+
BalCodeChange,
1011
BalNonceChange,
1112
BalStorageChange,
1213
BalStorageSlot,
@@ -206,3 +207,288 @@ def test_missing_expected_address():
206207

207208
with pytest.raises(Exception, match="Expected address .* not found in actual BAL"):
208209
expectation.verify_against(actual_bal)
210+
211+
212+
@pytest.mark.parametrize(
213+
"addresses,error_message",
214+
[
215+
(
216+
[
217+
Address(0xB),
218+
Address(0xA), # should come first
219+
],
220+
"BAL addresses not in lexicographic order per EIP-7928",
221+
),
222+
(
223+
[
224+
Address(0x1),
225+
Address(0x3),
226+
Address(0x2),
227+
],
228+
"BAL addresses not in lexicographic order per EIP-7928",
229+
),
230+
],
231+
)
232+
def test_actual_bal_address_ordering_validation(addresses, error_message):
233+
"""Test that actual BAL must have addresses in lexicographic order."""
234+
# Create BAL with addresses in the given order
235+
actual_bal = BlockAccessList(
236+
[BalAccountChange(address=addr, nonce_changes=[]) for addr in addresses]
237+
)
238+
239+
expectation = BlockAccessListExpectation(account_expectations={})
240+
241+
with pytest.raises(Exception, match=error_message):
242+
expectation.verify_against(actual_bal)
243+
244+
245+
@pytest.mark.parametrize(
246+
"storage_slots,error_message",
247+
[
248+
(
249+
[StorageKey(0x02), StorageKey(0x01)], # 0x02 before 0x01
250+
"Storage slots not in lexicographic order",
251+
),
252+
(
253+
[StorageKey(0x01), StorageKey(0x03), StorageKey(0x02)],
254+
"Storage slots not in lexicographic order",
255+
),
256+
],
257+
)
258+
def test_actual_bal_storage_slot_ordering(storage_slots, error_message):
259+
"""Test that actual BAL must have storage slots in lexicographic order."""
260+
addr = Address("0x000000000000000000000000000000000000000a")
261+
262+
actual_bal = BlockAccessList(
263+
[
264+
BalAccountChange(
265+
address=addr,
266+
storage_changes=[
267+
BalStorageSlot(slot=slot, slot_changes=[]) for slot in storage_slots
268+
],
269+
)
270+
]
271+
)
272+
273+
expectation = BlockAccessListExpectation(account_expectations={})
274+
275+
with pytest.raises(Exception, match=error_message):
276+
expectation.verify_against(actual_bal)
277+
278+
279+
@pytest.mark.parametrize(
280+
"storage_reads,error_message",
281+
[
282+
([StorageKey(0x02), StorageKey(0x01)], "Storage reads not in lexicographic order"),
283+
(
284+
[StorageKey(0x01), StorageKey(0x03), StorageKey(0x02)],
285+
"Storage reads not in lexicographic order",
286+
),
287+
],
288+
)
289+
def test_actual_bal_storage_reads_ordering(storage_reads, error_message):
290+
"""Test that actual BAL must have storage reads in lexicographic order."""
291+
addr = Address("0x000000000000000000000000000000000000000a")
292+
293+
actual_bal = BlockAccessList([BalAccountChange(address=addr, storage_reads=storage_reads)])
294+
295+
expectation = BlockAccessListExpectation(account_expectations={})
296+
297+
with pytest.raises(Exception, match=error_message):
298+
expectation.verify_against(actual_bal)
299+
300+
301+
@pytest.mark.parametrize(
302+
"field_name",
303+
["nonce_changes", "balance_changes", "code_changes"],
304+
)
305+
def test_actual_bal_tx_indices_ordering(field_name):
306+
"""Test that actual BAL must have tx indices in ascending order."""
307+
addr = Address(0xA)
308+
309+
tx_indices = [2, 3, 1] # out of order
310+
311+
changes = []
312+
if field_name == "nonce_changes":
313+
changes = [BalNonceChange(tx_index=idx, post_nonce=1) for idx in tx_indices]
314+
elif field_name == "balance_changes":
315+
changes = [BalBalanceChange(tx_index=idx, post_balance=100) for idx in tx_indices]
316+
elif field_name == "code_changes":
317+
changes = [BalCodeChange(tx_index=idx, new_code=b"code") for idx in tx_indices]
318+
319+
actual_bal = BlockAccessList([BalAccountChange(address=addr, **{field_name: changes})])
320+
321+
expectation = BlockAccessListExpectation(account_expectations={})
322+
323+
with pytest.raises(Exception, match="tx_indices not in ascending order"):
324+
expectation.verify_against(actual_bal)
325+
326+
327+
def test_expected_addresses_auto_sorted():
328+
"""
329+
Test that expected addresses are automatically sorted before comparison.
330+
331+
The BAL *Expectation address order should not matter for the dict.
332+
We DO, however, validate that the actual BAL (from t8n) is sorted correctly.
333+
"""
334+
alice = Address(0xA)
335+
bob = Address(0xB)
336+
charlie = Address(0xC)
337+
338+
actual_bal = BlockAccessList(
339+
[
340+
BalAccountChange(address=alice, nonce_changes=[]),
341+
BalAccountChange(address=bob, nonce_changes=[]),
342+
BalAccountChange(address=charlie, nonce_changes=[]),
343+
]
344+
)
345+
346+
# expectation order should not matter for the dict though we DO validate
347+
# that the _actual_ BAL (from t8n) is sorted correctly
348+
expectation = BlockAccessListExpectation(
349+
account_expectations={
350+
charlie: BalAccountExpectation(nonce_changes=[]),
351+
alice: BalAccountExpectation(nonce_changes=[]),
352+
bob: BalAccountExpectation(nonce_changes=[]),
353+
}
354+
)
355+
356+
expectation.verify_against(actual_bal)
357+
358+
359+
@pytest.mark.parametrize(
360+
"expected_slots,should_pass",
361+
[
362+
# Correct order - should pass
363+
([StorageKey(0x01), StorageKey(0x02), StorageKey(0x03)], True),
364+
# Partial subset in correct order - should pass
365+
([StorageKey(0x01), StorageKey(0x03)], True),
366+
# Out of order - should fail
367+
([StorageKey(0x01), StorageKey(0x03), StorageKey(0x02)], False),
368+
# Wrong order from start - should fail
369+
([StorageKey(0x02), StorageKey(0x01)], False),
370+
],
371+
)
372+
def test_expected_storage_slots_ordering(expected_slots, should_pass):
373+
"""Test that expected storage slots must be defined in correct order."""
374+
addr = Address(0xA)
375+
376+
# Actual BAL with storage slots in correct order
377+
actual_bal = BlockAccessList(
378+
[
379+
BalAccountChange(
380+
address=addr,
381+
storage_changes=[
382+
BalStorageSlot(slot=StorageKey(0x01), slot_changes=[]),
383+
BalStorageSlot(slot=StorageKey(0x02), slot_changes=[]),
384+
BalStorageSlot(slot=StorageKey(0x03), slot_changes=[]),
385+
],
386+
)
387+
]
388+
)
389+
390+
expectation = BlockAccessListExpectation(
391+
account_expectations={
392+
addr: BalAccountExpectation(
393+
storage_changes=[
394+
BalStorageSlot(slot=slot, slot_changes=[]) for slot in expected_slots
395+
],
396+
),
397+
}
398+
)
399+
400+
if should_pass:
401+
expectation.verify_against(actual_bal)
402+
else:
403+
with pytest.raises(Exception, match="not found or not in correct order"):
404+
expectation.verify_against(actual_bal)
405+
406+
407+
@pytest.mark.parametrize(
408+
"expected_reads,should_pass",
409+
[
410+
# Correct order - should pass
411+
([StorageKey(0x01), StorageKey(0x02), StorageKey(0x03)], True),
412+
# Partial subset in correct order - should pass
413+
([StorageKey(0x02), StorageKey(0x03)], True),
414+
# Out of order - should fail
415+
([StorageKey(0x03), StorageKey(0x02)], False),
416+
# Wrong order with all elements - should fail
417+
([StorageKey(0x01), StorageKey(0x03), StorageKey(0x02)], False),
418+
],
419+
)
420+
def test_expected_storage_reads_ordering(expected_reads, should_pass):
421+
"""Test that expected storage reads must be defined in correct order."""
422+
addr = Address(0xA)
423+
424+
# Actual BAL with storage reads in correct order
425+
actual_bal = BlockAccessList(
426+
[
427+
BalAccountChange(
428+
address=addr,
429+
storage_reads=[StorageKey(0x01), StorageKey(0x02), StorageKey(0x03)],
430+
)
431+
]
432+
)
433+
434+
expectation = BlockAccessListExpectation(
435+
account_expectations={
436+
addr: BalAccountExpectation(storage_reads=expected_reads),
437+
}
438+
)
439+
440+
if should_pass:
441+
expectation.verify_against(actual_bal)
442+
else:
443+
with pytest.raises(Exception, match="not found or not in correct order"):
444+
expectation.verify_against(actual_bal)
445+
446+
447+
@pytest.mark.parametrize(
448+
"expected_tx_indices,should_pass",
449+
[
450+
# Correct order - should pass
451+
([1, 2, 3], True),
452+
# Partial subset in correct order - should pass
453+
([1, 3], True),
454+
# Single element - should pass
455+
([2], True),
456+
# Out of order - should fail
457+
([2, 1], False),
458+
# Wrong order with all elements - should fail
459+
([1, 3, 2], False),
460+
],
461+
)
462+
def test_expected_tx_indices_ordering(expected_tx_indices, should_pass):
463+
"""Test that expected tx indices must be defined in correct order."""
464+
addr = Address(0xA)
465+
466+
# actual BAL with tx indices in correct order
467+
actual_bal = BlockAccessList(
468+
[
469+
BalAccountChange(
470+
address=addr,
471+
nonce_changes=[
472+
BalNonceChange(tx_index=1, post_nonce=1),
473+
BalNonceChange(tx_index=2, post_nonce=2),
474+
BalNonceChange(tx_index=3, post_nonce=3),
475+
],
476+
)
477+
]
478+
)
479+
480+
expectation = BlockAccessListExpectation(
481+
account_expectations={
482+
addr: BalAccountExpectation(
483+
nonce_changes=[
484+
BalNonceChange(tx_index=idx, post_nonce=idx) for idx in expected_tx_indices
485+
],
486+
),
487+
}
488+
)
489+
490+
if should_pass:
491+
expectation.verify_against(actual_bal)
492+
else:
493+
with pytest.raises(Exception, match="not found or not in correct order"):
494+
expectation.verify_against(actual_bal)

0 commit comments

Comments
 (0)