diff --git a/packages/testing/src/execution_testing/test_types/block_access_list/account_changes.py b/packages/testing/src/execution_testing/test_types/block_access_list/account_changes.py index 92179f8e0d..3378b05432 100644 --- a/packages/testing/src/execution_testing/test_types/block_access_list/account_changes.py +++ b/packages/testing/src/execution_testing/test_types/block_access_list/account_changes.py @@ -13,8 +13,8 @@ Address, Bytes, CamelModel, - HexNumber, RLPSerializable, + ZeroPaddedHexNumber, ) @@ -23,11 +23,11 @@ class BalNonceChange(CamelModel, RLPSerializable): model_config = CamelModel.model_config | {"extra": "forbid"} - block_access_index: HexNumber = Field( - HexNumber(1), + block_access_index: ZeroPaddedHexNumber = Field( + ZeroPaddedHexNumber(1), description="Transaction index where the change occurred", ) - post_nonce: HexNumber = Field( + post_nonce: ZeroPaddedHexNumber = Field( ..., description="Nonce value after the transaction" ) @@ -39,11 +39,11 @@ class BalBalanceChange(CamelModel, RLPSerializable): model_config = CamelModel.model_config | {"extra": "forbid"} - block_access_index: HexNumber = Field( - HexNumber(1), + block_access_index: ZeroPaddedHexNumber = Field( + ZeroPaddedHexNumber(1), description="Transaction index where the change occurred", ) - post_balance: HexNumber = Field( + post_balance: ZeroPaddedHexNumber = Field( ..., description="Balance after the transaction" ) @@ -55,8 +55,8 @@ class BalCodeChange(CamelModel, RLPSerializable): model_config = CamelModel.model_config | {"extra": "forbid"} - block_access_index: HexNumber = Field( - HexNumber(1), + block_access_index: ZeroPaddedHexNumber = Field( + ZeroPaddedHexNumber(1), description="Transaction index where the change occurred", ) new_code: Bytes = Field(..., description="New code bytes") @@ -69,11 +69,11 @@ class BalStorageChange(CamelModel, RLPSerializable): model_config = CamelModel.model_config | {"extra": "forbid"} - block_access_index: HexNumber = Field( - HexNumber(1), + block_access_index: ZeroPaddedHexNumber = Field( + ZeroPaddedHexNumber(1), description="Transaction index where the change occurred", ) - post_value: HexNumber = Field( + post_value: ZeroPaddedHexNumber = Field( ..., description="Value after the transaction" ) @@ -85,7 +85,7 @@ class BalStorageSlot(CamelModel, RLPSerializable): model_config = CamelModel.model_config | {"extra": "forbid"} - slot: HexNumber = Field(..., description="Storage slot key") + slot: ZeroPaddedHexNumber = Field(..., description="Storage slot key") slot_changes: List[BalStorageChange] = Field( default_factory=list, description="List of changes to this slot" ) @@ -111,7 +111,7 @@ class BalAccountChange(CamelModel, RLPSerializable): storage_changes: List[BalStorageSlot] = Field( default_factory=list, description="List of storage changes" ) - storage_reads: List[HexNumber] = Field( + storage_reads: List[ZeroPaddedHexNumber] = Field( default_factory=list, description="List of storage slots that were read", ) diff --git a/packages/testing/src/execution_testing/test_types/block_access_list/modifiers.py b/packages/testing/src/execution_testing/test_types/block_access_list/modifiers.py index d28f7099ba..b71f8e73f8 100644 --- a/packages/testing/src/execution_testing/test_types/block_access_list/modifiers.py +++ b/packages/testing/src/execution_testing/test_types/block_access_list/modifiers.py @@ -8,7 +8,10 @@ from typing import Any, Callable, List, Optional -from execution_testing.base_types import Address, HexNumber +from execution_testing.base_types import ( + Address, + ZeroPaddedHexNumber, +) from .. import BalCodeChange from . import ( @@ -257,20 +260,28 @@ def transform(bal: BlockAccessList) -> BlockAccessList: for nonce_change in new_account.nonce_changes: if nonce_change.block_access_index == tx1: nonce_indices[tx1] = True - nonce_change.block_access_index = HexNumber(tx2) + nonce_change.block_access_index = ZeroPaddedHexNumber( + tx2 + ) elif nonce_change.block_access_index == tx2: nonce_indices[tx2] = True - nonce_change.block_access_index = HexNumber(tx1) + nonce_change.block_access_index = ZeroPaddedHexNumber( + tx1 + ) # Swap in balance changes if new_account.balance_changes: for balance_change in new_account.balance_changes: if balance_change.block_access_index == tx1: balance_indices[tx1] = True - balance_change.block_access_index = HexNumber(tx2) + balance_change.block_access_index = ( + ZeroPaddedHexNumber(tx2) + ) elif balance_change.block_access_index == tx2: balance_indices[tx2] = True - balance_change.block_access_index = HexNumber(tx1) + balance_change.block_access_index = ( + ZeroPaddedHexNumber(tx1) + ) # Swap in storage changes (nested structure) if new_account.storage_changes: @@ -278,10 +289,14 @@ def transform(bal: BlockAccessList) -> BlockAccessList: for storage_change in storage_slot.slot_changes: if storage_change.block_access_index == tx1: balance_indices[tx1] = True - storage_change.block_access_index = HexNumber(tx2) + storage_change.block_access_index = ( + ZeroPaddedHexNumber(tx2) + ) elif storage_change.block_access_index == tx2: balance_indices[tx2] = True - storage_change.block_access_index = HexNumber(tx1) + storage_change.block_access_index = ( + ZeroPaddedHexNumber(tx1) + ) # Note: storage_reads is just a list of StorageKey, no block_access_index to # swap @@ -291,10 +306,14 @@ def transform(bal: BlockAccessList) -> BlockAccessList: for code_change in new_account.code_changes: if code_change.block_access_index == tx1: code_indices[tx1] = True - code_change.block_access_index = HexNumber(tx2) + code_change.block_access_index = ZeroPaddedHexNumber( + tx2 + ) elif code_change.block_access_index == tx2: code_indices[tx2] = True - code_change.block_access_index = HexNumber(tx1) + code_change.block_access_index = ZeroPaddedHexNumber( + tx1 + ) new_root.append(new_account) diff --git a/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_expectation.py b/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_expectation.py index f8b16d4474..ae495e9bd7 100644 --- a/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_expectation.py +++ b/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_expectation.py @@ -567,7 +567,7 @@ def test_absent_values_nonce_changes(has_change_should_raise: bool) -> None: if has_change_should_raise: with pytest.raises( - Exception, match="Unexpected nonce change found at tx 0x2" + Exception, match="Unexpected nonce change found at tx 0x02" ): expectation.verify_against(actual_bal) else: @@ -614,7 +614,7 @@ def test_absent_values_balance_changes(has_change_should_raise: bool) -> None: if has_change_should_raise: with pytest.raises( Exception, - match="Unexpected balance change found at tx 0x2", + match="Unexpected balance change found at tx 0x02", ): expectation.verify_against(actual_bal) else: @@ -759,7 +759,7 @@ def test_absent_values_code_changes(has_change_should_raise: bool) -> None: if has_change_should_raise: with pytest.raises( - Exception, match="Unexpected code change found at tx 0x2" + Exception, match="Unexpected code change found at tx 0x02" ): expectation.verify_against(actual_bal) else: @@ -898,7 +898,7 @@ def test_absent_values_with_multiple_tx_indices() -> None: ) with pytest.raises( - Exception, match="Unexpected nonce change found at tx 0x1" + Exception, match="Unexpected nonce change found at tx 0x01" ): expectation_fail.verify_against(actual_bal) diff --git a/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_serialization.py b/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_serialization.py new file mode 100644 index 0000000000..a42d86ff65 --- /dev/null +++ b/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_serialization.py @@ -0,0 +1,86 @@ +""" +Tests for BlockAccessList serialization format. + +These tests verify that BAL models serialize to JSON with the correct +format, particularly zero-padded hex strings. +""" + +from execution_testing.base_types import Address, Bytes +from execution_testing.test_types.block_access_list import ( + BalAccountChange, + BalBalanceChange, + BalCodeChange, + BalNonceChange, + BalStorageChange, + BalStorageSlot, + BlockAccessList, +) + + +def test_bal_serialization_roundtrip_zero_padded_hex() -> None: + """ + Test that BAL serializes with zero-padded hex format and round-trips correctly. + + This verifies that values like 12 serialize as "0x0c" (not "0xc"), which is + required for consistency with other test vector fields. + """ + addr = Address(0xA) + + original = BlockAccessList( + [ + BalAccountChange( + address=addr, + nonce_changes=[ + BalNonceChange(block_access_index=1, post_nonce=12), + BalNonceChange(block_access_index=2, post_nonce=255), + ], + balance_changes=[ + BalBalanceChange(block_access_index=1, post_balance=15), + ], + code_changes=[ + BalCodeChange( + block_access_index=3, new_code=Bytes(b"\xde\xad") + ), + ], + storage_changes=[ + BalStorageSlot( + slot=12, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=255 + ), + BalStorageChange( + block_access_index=2, post_value=4096 + ), + ], + ), + ], + storage_reads=[1, 15, 256], + ) + ] + ) + + # Serialize to JSON + json_data = original.model_dump(mode="json") + account_data = json_data[0] + + # Verify zero-padded hex format (0x0c not 0xc, 0x01 not 0x1) + assert account_data["nonce_changes"][0]["block_access_index"] == "0x01" + assert account_data["nonce_changes"][0]["post_nonce"] == "0x0c" + assert account_data["nonce_changes"][1]["post_nonce"] == "0xff" + assert account_data["balance_changes"][0]["post_balance"] == "0x0f" + assert account_data["code_changes"][0]["block_access_index"] == "0x03" + assert account_data["storage_changes"][0]["slot"] == "0x0c" + assert ( + account_data["storage_changes"][0]["slot_changes"][0]["post_value"] + == "0xff" + ) + assert ( + account_data["storage_changes"][0]["slot_changes"][1]["post_value"] + == "0x1000" + ) + assert account_data["storage_reads"] == ["0x01", "0x0f", "0x0100"] + + # Round-trip: deserialize and verify equality + restored = BlockAccessList.model_validate(json_data) + assert restored == original diff --git a/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_t8n.py b/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_t8n.py index c33cf8a2c7..942abf6bb2 100644 --- a/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_t8n.py +++ b/packages/testing/src/execution_testing/test_types/tests/test_block_access_list_t8n.py @@ -9,7 +9,7 @@ import pytest -from execution_testing.base_types import Address, HexNumber, StorageKey +from execution_testing.base_types import Address from execution_testing.test_types.block_access_list import ( BalAccountChange, BalBalanceChange, @@ -61,9 +61,9 @@ def test_bal_storage_slot_ordering() -> None: BalAccountChange( address=addr, storage_changes=[ - BalStorageSlot(slot=StorageKey(0), slot_changes=[]), - BalStorageSlot(slot=StorageKey(1), slot_changes=[]), - BalStorageSlot(slot=StorageKey(2), slot_changes=[]), + BalStorageSlot(slot=0, slot_changes=[]), + BalStorageSlot(slot=1, slot_changes=[]), + BalStorageSlot(slot=2, slot_changes=[]), ], ) ] @@ -76,9 +76,9 @@ def test_bal_storage_slot_ordering() -> None: BalAccountChange( address=addr, storage_changes=[ - BalStorageSlot(slot=StorageKey(0), slot_changes=[]), - BalStorageSlot(slot=StorageKey(2), slot_changes=[]), - BalStorageSlot(slot=StorageKey(1), slot_changes=[]), + BalStorageSlot(slot=0, slot_changes=[]), + BalStorageSlot(slot=2, slot_changes=[]), + BalStorageSlot(slot=1, slot_changes=[]), ], ) ] @@ -100,7 +100,7 @@ def test_bal_storage_reads_ordering() -> None: [ BalAccountChange( address=addr, - storage_reads=[StorageKey(0), StorageKey(1), StorageKey(2)], + storage_reads=[0, 1, 2], ) ] ) @@ -111,7 +111,7 @@ def test_bal_storage_reads_ordering() -> None: [ BalAccountChange( address=addr, - storage_reads=[StorageKey(0), StorageKey(2), StorageKey(1)], + storage_reads=[0, 2, 1], ) ] ) @@ -142,59 +142,71 @@ def test_bal_block_access_indices_ordering(field_name: str) -> None: if field_name == "nonce_changes": changes_valid = [ BalNonceChange( - block_access_index=HexNumber(1), post_nonce=HexNumber(1) + block_access_index=1, + post_nonce=1, ), BalNonceChange( - block_access_index=HexNumber(2), post_nonce=HexNumber(2) + block_access_index=2, + post_nonce=2, ), BalNonceChange( - block_access_index=HexNumber(3), post_nonce=HexNumber(3) + block_access_index=3, + post_nonce=3, ), ] changes_invalid = [ BalNonceChange( - block_access_index=HexNumber(1), post_nonce=HexNumber(1) + block_access_index=1, + post_nonce=1, ), BalNonceChange( - block_access_index=HexNumber(3), post_nonce=HexNumber(3) + block_access_index=3, + post_nonce=3, ), BalNonceChange( - block_access_index=HexNumber(2), post_nonce=HexNumber(2) + block_access_index=2, + post_nonce=2, ), ] elif field_name == "balance_changes": changes_valid = [ BalBalanceChange( - block_access_index=HexNumber(1), post_balance=HexNumber(100) + block_access_index=1, + post_balance=100, ), BalBalanceChange( - block_access_index=HexNumber(2), post_balance=HexNumber(200) + block_access_index=2, + post_balance=200, ), BalBalanceChange( - block_access_index=HexNumber(3), post_balance=HexNumber(300) + block_access_index=3, + post_balance=300, ), ] changes_invalid = [ BalBalanceChange( - block_access_index=HexNumber(1), post_balance=HexNumber(100) + block_access_index=1, + post_balance=100, ), BalBalanceChange( - block_access_index=HexNumber(3), post_balance=HexNumber(300) + block_access_index=3, + post_balance=300, ), BalBalanceChange( - block_access_index=HexNumber(2), post_balance=HexNumber(200) + block_access_index=2, + post_balance=200, ), ] elif field_name == "code_changes": changes_valid = [ - BalCodeChange(block_access_index=HexNumber(1), new_code=b"code1"), - BalCodeChange(block_access_index=HexNumber(2), new_code=b"code2"), - BalCodeChange(block_access_index=HexNumber(3), new_code=b"code3"), + BalCodeChange(block_access_index=1, new_code=b"code1"), + BalCodeChange(block_access_index=2, new_code=b"code2"), + BalCodeChange(block_access_index=3, new_code=b"code3"), ] changes_invalid = [ - BalCodeChange(block_access_index=HexNumber(1), new_code=b"code1"), - BalCodeChange(block_access_index=HexNumber(3), new_code=b"code3"), - BalCodeChange(block_access_index=HexNumber(2), new_code=b"code2"), + BalCodeChange(block_access_index=1, new_code=b"code1"), + BalCodeChange(block_access_index=3, new_code=b"code3"), + BalCodeChange(block_access_index=2, new_code=b"code2"), ] bal_valid = BlockAccessList( @@ -229,34 +241,40 @@ def test_bal_duplicate_block_access_indices(field_name: str) -> None: if field_name == "nonce_changes": changes = [ BalNonceChange( - block_access_index=HexNumber(1), post_nonce=HexNumber(1) + block_access_index=1, + post_nonce=1, ), BalNonceChange( - block_access_index=HexNumber(1), post_nonce=HexNumber(2) + block_access_index=1, + post_nonce=2, ), # duplicate block_access_index BalNonceChange( - block_access_index=HexNumber(2), post_nonce=HexNumber(3) + block_access_index=2, + post_nonce=3, ), ] elif field_name == "balance_changes": changes = [ BalBalanceChange( - block_access_index=HexNumber(1), post_balance=HexNumber(100) + block_access_index=1, + post_balance=100, ), BalBalanceChange( - block_access_index=HexNumber(1), post_balance=HexNumber(200) + block_access_index=1, + post_balance=200, ), # duplicate block_access_index BalBalanceChange( - block_access_index=HexNumber(2), post_balance=HexNumber(300) + block_access_index=2, + post_balance=300, ), ] elif field_name == "code_changes": changes = [ - BalCodeChange(block_access_index=HexNumber(1), new_code=b"code1"), + BalCodeChange(block_access_index=1, new_code=b"code1"), BalCodeChange( - block_access_index=HexNumber(1), new_code=b"" + block_access_index=1, new_code=b"" ), # duplicate block_access_index - BalCodeChange(block_access_index=HexNumber(2), new_code=b"code2"), + BalCodeChange(block_access_index=2, new_code=b"code2"), ] bal = BlockAccessList( @@ -283,19 +301,19 @@ def test_bal_storage_duplicate_block_access_indices() -> None: address=addr, storage_changes=[ BalStorageSlot( - slot=StorageKey(0), + slot=0, slot_changes=[ BalStorageChange( - block_access_index=HexNumber(1), - post_value=StorageKey(100), + block_access_index=1, + post_value=100, ), BalStorageChange( - block_access_index=HexNumber(1), - post_value=StorageKey(200), + block_access_index=1, + post_value=200, ), # duplicate block_access_index BalStorageChange( - block_access_index=HexNumber(2), - post_value=StorageKey(300), + block_access_index=2, + post_value=300, ), ], ) @@ -325,12 +343,12 @@ def test_bal_multiple_violations() -> None: address=bob, # Should come after alice nonce_changes=[ BalNonceChange( - block_access_index=HexNumber(1), - post_nonce=HexNumber(1), + block_access_index=1, + post_nonce=1, ), BalNonceChange( - block_access_index=HexNumber(1), - post_nonce=HexNumber(2), + block_access_index=1, + post_nonce=2, ), # duplicate ], ), @@ -360,8 +378,8 @@ def test_bal_single_account_valid() -> None: address=Address(0xA), nonce_changes=[ BalNonceChange( - block_access_index=HexNumber(1), - post_nonce=HexNumber(1), + block_access_index=1, + post_nonce=1, ) ], )