From de486bab6937a9363b04bf5086fc2bef2996cbb3 Mon Sep 17 00:00:00 2001 From: Dimitry Kh Date: Mon, 9 Dec 2024 22:02:02 +0100 Subject: [PATCH 1/4] unit tests --- src/ethereum_test_base_types/conversions.py | 15 ++ .../tests/test_base_types.py | 139 ++++++++++++++---- .../tests/test_state.py | 16 +- .../tests/test_fixtures.py | 2 +- src/ethereum_test_specs/tests/test_types.py | 36 ++++- src/ethereum_test_types/tests/test_helpers.py | 10 +- .../tests/test_post_alloc.py | 28 ++-- .../tests/test_transactions.py | 21 ++- src/ethereum_test_types/tests/test_types.py | 25 +++- .../test_tstorage_execution_contexts.py | 2 +- .../test_tstorage_selfdestruct.py | 4 +- .../eip4895_withdrawals/test_withdrawals.py | 8 +- 12 files changed, 230 insertions(+), 76 deletions(-) diff --git a/src/ethereum_test_base_types/conversions.py b/src/ethereum_test_base_types/conversions.py index ede06d1e718..98d31d2bd20 100644 --- a/src/ethereum_test_base_types/conversions.py +++ b/src/ethereum_test_base_types/conversions.py @@ -60,6 +60,21 @@ def to_fixed_size_bytes(input_bytes: FixedSizeBytesConvertible, size: int) -> by return bytes(input_bytes).rjust(size, b"\x00") +def left_pad_zeros_up_to_size(input: bytes, size: int) -> bytes: + """ + Pads the given data to fit into a size-byte bytes. If the data is longer than + size bytes, it raises a ValueError. If it is shorter, it left pads with zero bytes. + + :param data: The input data to pad. + :return: A Hash object of exactly size bytes. + """ + input = to_bytes(input) + if len(input) > size: + raise ValueError(f"Data cannot be longer than {size} bytes.") + padded_data = bytes(input).rjust(size, b"\x00") + return bytes(padded_data) + + def to_hex(input_bytes: BytesConvertible) -> str: """Convert multiple types into a bytes hex string.""" return "0x" + to_bytes(input_bytes).hex() diff --git a/src/ethereum_test_base_types/tests/test_base_types.py b/src/ethereum_test_base_types/tests/test_base_types.py index cac0c89bfa5..42734e2d5f3 100644 --- a/src/ethereum_test_base_types/tests/test_base_types.py +++ b/src/ethereum_test_base_types/tests/test_base_types.py @@ -12,34 +12,117 @@ @pytest.mark.parametrize( "a, b, equal", [ - (Address("0x0"), Address("0x0"), True), - (Address("0x0"), Address("0x1"), False), - (Address("0x1"), Address("0x0"), False), - (Address("0x1"), "0x1", True), - (Address("0x1"), "0x2", False), - (Address("0x1"), 1, True), - (Address("0x1"), 2, False), - (Address("0x1"), b"\x01", True), - (Address("0x1"), b"\x02", False), - ("0x1", Address("0x1"), True), - ("0x2", Address("0x1"), False), - (1, Address("0x1"), True), - (2, Address("0x1"), False), - (b"\x01", Address("0x1"), True), - (b"\x02", Address("0x1"), False), - (Hash("0x0"), Hash("0x0"), True), - (Hash("0x0"), Hash("0x1"), False), - (Hash("0x1"), Hash("0x0"), False), - (Hash("0x1"), "0x1", True), - (Hash("0x1"), "0x2", False), - (Hash("0x1"), 1, True), - (Hash("0x1"), 2, False), - (Hash("0x1"), b"\x01", True), - (Hash("0x1"), b"\x02", False), - ("0x1", Hash("0x1"), True), - ("0x2", Hash("0x1"), False), - (1, Hash("0x1"), True), - (2, Hash("0x1"), False), + (Address(0), Address(0), True), + ( + Address("0x0000000000000000000000000000000000000000"), + Address("0x0000000000000000000000000000000000000000"), + True, + ), + ( + Address("0x0000000000000000000000000000000000000000"), + Address("0x0000000000000000000000000000000000000001"), + False, + ), + ( + Address("0x0000000000000000000000000000000000000001"), + Address("0x0000000000000000000000000000000000000000"), + False, + ), + ( + Address("0x0000000000000000000000000000000000000001"), + "0x0000000000000000000000000000000000000001", + True, + ), + ( + Address("0x0000000000000000000000000000000000000001"), + "0x0000000000000000000000000000000000000002", + False, + ), + (Address("0x0000000000000000000000000000000000000001"), 1, True), + (Address("0x0000000000000000000000000000000000000001"), 2, False), + ( + Address("0x0000000000000000000000000000000000000001"), + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", + True, + ), + ( + Address("0x0000000000000000000000000000000000000001"), + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02", + False, + ), + ( + "0x0000000000000000000000000000000000000001", + Address("0x0000000000000000000000000000000000000001"), + True, + ), + ( + "0x0000000000000000000000000000000000000002", + Address("0x0000000000000000000000000000000000000001"), + False, + ), + (1, Address("0x0000000000000000000000000000000000000001"), True), + (2, Address("0x0000000000000000000000000000000000000001"), False), + ( + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", + Address("0x0000000000000000000000000000000000000001"), + True, + ), + ( + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02", + Address("0x0000000000000000000000000000000000000001"), + False, + ), + ( + Hash("0x0000000000000000000000000000000000000000000000000000000000000000"), + Hash("0x0000000000000000000000000000000000000000000000000000000000000000"), + True, + ), + ( + Hash("0x0000000000000000000000000000000000000000000000000000000000000000"), + Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), + False, + ), + ( + Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), + Hash("0x0000000000000000000000000000000000000000000000000000000000000000"), + False, + ), + ( + Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), + "0x0000000000000000000000000000000000000000000000000000000000000001", + True, + ), + ( + Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), + "0x0000000000000000000000000000000000000000000000000000000000000002", + False, + ), + (Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), 1, True), + (Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), 2, False), + ( + Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", + True, + ), + ( + Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02", + False, + ), + ( + "0x0000000000000000000000000000000000000000000000000000000000000001", + Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), + True, + ), + ( + "0x0000000000000000000000000000000000000000000000000000000000000002", + Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), + False, + ), + (1, Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), True), + (2, Hash("0x0000000000000000000000000000000000000000000000000000000000000001"), False), ], ) def test_comparisons(a: Any, b: Any, equal: bool): diff --git a/src/ethereum_test_fixtures/tests/test_state.py b/src/ethereum_test_fixtures/tests/test_state.py index efa55942965..0bffdd8a9be 100644 --- a/src/ethereum_test_fixtures/tests/test_state.py +++ b/src/ethereum_test_fixtures/tests/test_state.py @@ -16,8 +16,8 @@ pytest.param( True, FixtureForkPost( - state_root="0x00", - logs_hash="0x01", + state_root=0, + logs_hash=1, tx_bytes="0x02", ), { @@ -31,8 +31,8 @@ pytest.param( True, FixtureForkPost( - state_root="0x00", - logs_hash="0x01", + state_root=0, + logs_hash=1, tx_bytes="0x02", expect_exception=TransactionException.INITCODE_SIZE_EXCEEDED, ), @@ -49,8 +49,8 @@ False, # Can not be deserialized: A single expect_exception str will not be # deserialized as a list and therefore will not match the model_instance definition. FixtureForkPost( - state_root="0x00", - logs_hash="0x01", + state_root=0, + logs_hash=1, tx_bytes="0x02", expect_exception=[TransactionException.INITCODE_SIZE_EXCEEDED], ), @@ -66,8 +66,8 @@ pytest.param( True, FixtureForkPost( - state_root="0x00", - logs_hash="0x01", + state_root=0, + logs_hash=1, tx_bytes="0x02", expect_exception=[ TransactionException.INITCODE_SIZE_EXCEEDED, diff --git a/src/ethereum_test_specs/tests/test_fixtures.py b/src/ethereum_test_specs/tests/test_fixtures.py index 3144e7b94e6..e14374b52ea 100644 --- a/src/ethereum_test_specs/tests/test_fixtures.py +++ b/src/ethereum_test_specs/tests/test_fixtures.py @@ -532,7 +532,7 @@ def test_fixture_header_join(self, blockchain_test_fixture: BlockchainFixture): new_state_root = Hash(12345) # See description of https://github.com/ethereum/execution-spec-tests/pull/398 - new_transactions_root = "0x100" + new_transactions_root = 0x100 header_new_fields = Header( difficulty=new_difficulty, state_root=new_state_root, diff --git a/src/ethereum_test_specs/tests/test_types.py b/src/ethereum_test_specs/tests/test_types.py index 4475e859a58..8dce447d954 100644 --- a/src/ethereum_test_specs/tests/test_types.py +++ b/src/ethereum_test_specs/tests/test_types.py @@ -42,8 +42,12 @@ ), pytest.param( fixture_header_ones, - Header(state_root="0x100"), - fixture_header_ones.copy(state_root="0x100"), + Header( + state_root="0x0000000000000000000000000000000000000000000000000000000000000100" + ), + fixture_header_ones.copy( + state_root="0x0000000000000000000000000000000000000000000000000000000000000100" + ), id="state_root_as_str", ), pytest.param( @@ -72,8 +76,24 @@ ), pytest.param( fixture_header_ones, - Header(logs_bloom="0x100"), - fixture_header_ones.copy(logs_bloom="0x100"), + Header( + logs_bloom="0x00000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000100" + ), + fixture_header_ones.copy( + logs_bloom="0x00000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000100" + ), id="bloom_as_str", ), pytest.param( @@ -84,13 +104,17 @@ ), pytest.param( fixture_header_ones, - Header(logs_bloom=Hash(100)), + Header(logs_bloom=Bloom(100)), fixture_header_ones.copy(logs_bloom=100), id="bloom_as_hash", ), pytest.param( fixture_header_ones, - Header(state_root="0x100", logs_bloom=Hash(200), difficulty=300), + Header( + state_root="0x0000000000000000000000000000000000000000000000000000000000000100", + logs_bloom=Bloom(200), + difficulty=300, + ), fixture_header_ones.copy( state_root=0x100, logs_bloom=200, diff --git a/src/ethereum_test_types/tests/test_helpers.py b/src/ethereum_test_types/tests/test_helpers.py index d76bb41fef3..a929b4d80b0 100644 --- a/src/ethereum_test_types/tests/test_helpers.py +++ b/src/ethereum_test_types/tests/test_helpers.py @@ -9,11 +9,17 @@ def test_address(): """Test `ethereum_test.base_types.Address`.""" - assert Address("0x0") == "0x0000000000000000000000000000000000000000" + assert ( + Address("0x0000000000000000000000000000000000000000") + == "0x0000000000000000000000000000000000000000" + ) assert Address(0) == "0x0000000000000000000000000000000000000000" assert Address(1) == "0x0000000000000000000000000000000000000001" assert Address(10) == "0x000000000000000000000000000000000000000a" - assert Address("0x10") == "0x0000000000000000000000000000000000000010" + assert ( + Address("0x0000000000000000000000000000000000000010") + == "0x0000000000000000000000000000000000000010" + ) assert Address(2 ** (20 * 8) - 1) == "0xffffffffffffffffffffffffffffffffffffffff" assert Address(0) == Address(0) assert Address(0) != Address(1) diff --git a/src/ethereum_test_types/tests/test_post_alloc.py b/src/ethereum_test_types/tests/test_post_alloc.py index 4bd8d99ec76..c9639b7e543 100644 --- a/src/ethereum_test_types/tests/test_post_alloc.py +++ b/src/ethereum_test_types/tests/test_post_alloc.py @@ -25,9 +25,9 @@ def alloc(request: pytest.FixtureRequest) -> Alloc: [ # Account should not exist but contained in alloc ( - {"0x0": Account.NONEXISTENT}, + {"0x0000000000000000000000000000000000000000": Account.NONEXISTENT}, { - "0x00": { + "0x0000000000000000000000000000000000000000": { "nonce": "1", "code": "0x123", "balance": "1", @@ -38,27 +38,27 @@ def alloc(request: pytest.FixtureRequest) -> Alloc: ), # Account should not exist but contained in alloc ( - {"0x00": Account.NONEXISTENT}, - {"0x0": {"nonce": "1"}}, + {"0x0000000000000000000000000000000000000000": Account.NONEXISTENT}, + {"0x0000000000000000000000000000000000000000": {"nonce": "1"}}, Alloc.UnexpectedAccountError, ), # Account should not exist but contained in alloc ( - {"0x1": Account.NONEXISTENT}, - {"0x01": {"balance": "1"}}, + {"0x0000000000000000000000000000000000000001": Account.NONEXISTENT}, + {"0x0000000000000000000000000000000000000001": {"balance": "1"}}, Alloc.UnexpectedAccountError, ), # Account should not exist but contained in alloc ( - {"0x0a": Account.NONEXISTENT}, - {"0x0A": {"code": "0x00"}}, + {"0x000000000000000000000000000000000000000a": Account.NONEXISTENT}, + {"0x000000000000000000000000000000000000000A": {"code": "0x00"}}, Alloc.UnexpectedAccountError, ), # Account should exist but not in alloc ( - {"0x0A": Account()}, + {"0x000000000000000000000000000000000000000A": Account()}, { - "0x0B": { + "0x000000000000000000000000000000000000000B": { "nonce": "1", "code": "0x123", "balance": "1", @@ -70,9 +70,9 @@ def alloc(request: pytest.FixtureRequest) -> Alloc: # Account should exist and contained in alloc, but don't care about # values ( - {"0x1": Account()}, + {"0x0000000000000000000000000000000000000001": Account()}, { - "0x1": { + "0x0000000000000000000000000000000000000001": { "nonce": "1", "code": "0x123", "balance": "1", @@ -83,9 +83,9 @@ def alloc(request: pytest.FixtureRequest) -> Alloc: ), # Account should exist and contained in alloc, single incorrect value ( - {"0x1": Account(nonce=0)}, + {"0x0000000000000000000000000000000000000001": Account(nonce=0)}, { - "0x1": { + "0x0000000000000000000000000000000000000001": { "nonce": "1", "code": "0x123", "balance": "1", diff --git a/src/ethereum_test_types/tests/test_transactions.py b/src/ethereum_test_types/tests/test_transactions.py index c3d05b54620..7fbd2a729cb 100644 --- a/src/ethereum_test_types/tests/test_transactions.py +++ b/src/ethereum_test_types/tests/test_transactions.py @@ -4,7 +4,7 @@ import pytest -from ..types import AccessList, Transaction +from ..types import AccessList, Hash, Transaction @pytest.mark.parametrize( @@ -105,7 +105,12 @@ ty=1, nonce=0, gas_price=1000000000, - access_list=[AccessList(address="0x123", storage_keys=["0x456", "0x789"])], + access_list=[ + AccessList( + address="0x0000000000000000000000000000000000000123", + storage_keys=[0x456, 0x789], + ) + ], ), ( 0, @@ -125,7 +130,7 @@ nonce=0, gas_price=1000000000, to=None, - access_list=[AccessList(address="0x123", storage_keys=["0x456", "0x789"])], + access_list=[AccessList(address=0x123, storage_keys=[0x456, 0x789])], ), ( 0, @@ -143,7 +148,7 @@ Transaction( ty=2, nonce=0, - access_list=[AccessList(address="0x123", storage_keys=["0x456", "0x789"])], + access_list=[AccessList(address=0x123, storage_keys=[0x456, 0x789])], max_fee_per_gas=10, max_priority_fee_per_gas=5, ), @@ -164,7 +169,7 @@ ty=2, nonce=0, to=None, - access_list=[AccessList(address="0x123", storage_keys=["0x456", "0x789"])], + access_list=[AccessList(address=0x123, storage_keys=[0x456, 0x789])], max_fee_per_gas=10, max_priority_fee_per_gas=5, ), @@ -184,7 +189,7 @@ Transaction( ty=3, nonce=0, - access_list=[AccessList(address="0x123", storage_keys=["0x456", "0x789"])], + access_list=[AccessList(address=0x123, storage_keys=[0x456, 0x789])], max_fee_per_gas=10, max_priority_fee_per_gas=5, max_fee_per_blob_gas=100, @@ -206,11 +211,11 @@ Transaction( ty=3, nonce=0, - access_list=[AccessList(address="0x123", storage_keys=["0x456", "0x789"])], + access_list=[AccessList(address=0x123, storage_keys=[0x456, 0x789])], max_fee_per_gas=10, max_priority_fee_per_gas=5, max_fee_per_blob_gas=100, - blob_versioned_hashes=[bytes(), bytes([0x01])], + blob_versioned_hashes=[Hash(0), Hash(0x01)], ), ( 1, diff --git a/src/ethereum_test_types/tests/test_types.py b/src/ethereum_test_types/tests/test_types.py index 87c853a57ce..e657d0fd821 100644 --- a/src/ethereum_test_types/tests/test_types.py +++ b/src/ethereum_test_types/tests/test_types.py @@ -327,13 +327,13 @@ def test_account_check_alloc(account: Account, alloc_dict: Dict[Any, Any], shoul ), pytest.param( Alloc({0x2: {"nonce": 1}}), # type: ignore - Alloc({"0x02": {"nonce": 2}}), # type: ignore + Alloc({"0x0000000000000000000000000000000000000002": {"nonce": 2}}), # type: ignore Alloc({0x2: Account(nonce=2)}), # type: ignore id="overwrite_account", ), pytest.param( Alloc({0x2: {"balance": 1}}), # type: ignore - Alloc({"0x02": {"nonce": 1}}), # type: ignore + Alloc({"0x0000000000000000000000000000000000000002": {"nonce": 1}}), # type: ignore Alloc({0x2: Account(balance=1, nonce=1)}), # type: ignore id="mix_account", ), @@ -550,6 +550,27 @@ def test_account_merge( }, id="transaction_t8n_to_none", ), + pytest.param( + True, + Transaction( + to="", + ).with_signature_and_sender(), + { + "type": "0x0", + "chainId": "0x1", + "nonce": "0x0", + "to": None, + "value": "0x0", + "input": "0x", + "gas": "0x5208", + "gasPrice": "0xa", + "v": "0x25", + "r": "0x1cfe2cbb0c3577f74d9ae192a7f1ee2d670fe806a040f427af9cb768be3d07ce", + "s": "0xcbe2d029f52dbf93ade486625bed0603945d2c7358b31de99fe8786c00f13da", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + }, + id="transaction_t8n_to_empty_str", + ), pytest.param( True, Transaction( diff --git a/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py b/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py index f495c5ba27b..d4edb44c364 100644 --- a/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py +++ b/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py @@ -308,7 +308,7 @@ def tx(pre: Alloc, caller_address: Address, callee_address: Address) -> Transact return Transaction( sender=pre.fund_eoa(), to=caller_address, - data=Hash(callee_address), + data=left_pad_zeros_up_to_size(callee_address, 32), gas_limit=1_000_000, ) diff --git a/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py b/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py index 8c547e3fdcd..fd450f0286e 100644 --- a/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py +++ b/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py @@ -230,10 +230,10 @@ def test_reentrant_selfdestructing_call( caller_address = pre.deploy_contract(code=caller_bytecode) - data: Hash | Bytecode + data: bytes | Bytecode if pre_existing_contract: callee_address = pre.deploy_contract(code=callee_bytecode) - data = Hash(callee_address) + data = left_pad_zeros_up_to_size(callee_address, 32) else: callee_address = compute_create_address(address=caller_address, nonce=1) data = Initcode(deploy_code=callee_bytecode) diff --git a/tests/shanghai/eip4895_withdrawals/test_withdrawals.py b/tests/shanghai/eip4895_withdrawals/test_withdrawals.py index 12ca7f1edaa..4d009e09529 100644 --- a/tests/shanghai/eip4895_withdrawals/test_withdrawals.py +++ b/tests/shanghai/eip4895_withdrawals/test_withdrawals.py @@ -10,6 +10,7 @@ import pytest from ethereum_clis import TransitionTool +from ethereum_test_base_types.conversions import left_pad_zeros_up_to_size from ethereum_test_forks import Cancun, Fork from ethereum_test_tools import ( EOA, @@ -18,7 +19,6 @@ Alloc, Block, BlockchainTestFiller, - Hash, Transaction, TransactionException, Withdrawal, @@ -201,7 +201,7 @@ def test_balance_within_block(blockchain_test: BlockchainTestFiller, pre: Alloc) sender=sender, gas_limit=100000, to=contract_address, - data=Hash(recipient), + data=left_pad_zeros_up_to_size(recipient, 32), ) ], withdrawals=[ @@ -219,7 +219,7 @@ def test_balance_within_block(blockchain_test: BlockchainTestFiller, pre: Alloc) sender=sender, gas_limit=100000, to=contract_address, - data=Hash(recipient), + data=left_pad_zeros_up_to_size(recipient, 32), ) ] ), @@ -373,7 +373,7 @@ def test_self_destructing_account( sender=sender, gas_limit=100000, to=self_destruct_contract_address, - data=Hash(recipient), + data=left_pad_zeros_up_to_size(recipient, 32), ) withdrawal = Withdrawal( From 2a614f7973f38feb3bcd6d2eec63b59467d73a43 Mon Sep 17 00:00:00 2001 From: Dimitry Kh Date: Wed, 8 Jan 2025 13:36:49 +0100 Subject: [PATCH 2/4] fix after rebase --- src/ethereum_test_base_types/conversions.py | 10 +++++----- .../eip1153_tstore/test_tstorage_execution_contexts.py | 2 +- .../eip1153_tstore/test_tstorage_selfdestruct.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ethereum_test_base_types/conversions.py b/src/ethereum_test_base_types/conversions.py index 98d31d2bd20..242d96e908f 100644 --- a/src/ethereum_test_base_types/conversions.py +++ b/src/ethereum_test_base_types/conversions.py @@ -60,18 +60,18 @@ def to_fixed_size_bytes(input_bytes: FixedSizeBytesConvertible, size: int) -> by return bytes(input_bytes).rjust(size, b"\x00") -def left_pad_zeros_up_to_size(input: bytes, size: int) -> bytes: +def left_pad_zeros_up_to_size(input_bytes: bytes, size: int) -> bytes: """ - Pads the given data to fit into a size-byte bytes. If the data is longer than + Pad the given data to fit into a size-byte bytes. If the data is longer than size bytes, it raises a ValueError. If it is shorter, it left pads with zero bytes. :param data: The input data to pad. :return: A Hash object of exactly size bytes. """ - input = to_bytes(input) - if len(input) > size: + input_bytes = to_bytes(input_bytes) + if len(input_bytes) > size: raise ValueError(f"Data cannot be longer than {size} bytes.") - padded_data = bytes(input).rjust(size, b"\x00") + padded_data = bytes(input_bytes).rjust(size, b"\x00") return bytes(padded_data) diff --git a/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py b/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py index d4edb44c364..0d89022d5cc 100644 --- a/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py +++ b/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py @@ -8,13 +8,13 @@ import pytest +from ethereum_test_base_types.conversions import left_pad_zeros_up_to_size from ethereum_test_tools import ( Account, Address, Alloc, Bytecode, Environment, - Hash, StateTestFiller, Transaction, ) diff --git a/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py b/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py index fd450f0286e..001532f6898 100644 --- a/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py +++ b/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py @@ -9,13 +9,13 @@ import pytest +from ethereum_test_base_types.conversions import left_pad_zeros_up_to_size from ethereum_test_tools import ( Account, Alloc, Bytecode, CalldataCase, Environment, - Hash, Initcode, StateTestFiller, Switch, From 31f218a71d1914cd44cae6c806e192b6c74a3e85 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 9 Jan 2025 15:52:28 +0000 Subject: [PATCH 3/4] fix(types): Transaction `to` validation fix --- src/ethereum_test_types/types.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index 6dc36821748..9529d5dbf4b 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -594,17 +594,26 @@ class TransactionGeneric(BaseModel, Generic[NumberBoundTypeVar]): sender: EOA | None = None -class TransactionFixtureConverter(CamelModel): - """Handler for serializing and validating the `to` field as an empty string.""" +class TransactionValidateToAsEmptyString(CamelModel): + """Handler to validate the `to` field from an empty string.""" @model_validator(mode="before") @classmethod def validate_to_as_empty_string(cls, data: Any) -> Any: """If the `to` field is an empty string, set the model value to None.""" - if isinstance(data, dict) and "to" in data and data["to"] == "": + if ( + isinstance(data, dict) + and "to" in data + and isinstance(data["to"], str) + and data["to"] == "" + ): data["to"] = None return data + +class TransactionFixtureConverter(TransactionValidateToAsEmptyString): + """Handler for serializing and validating the `to` field as an empty string.""" + @model_serializer(mode="wrap", when_used="json-unless-none") def serialize_to_as_empty_string(self, serializer): """Serialize the `to` field as the empty string if the model value is None.""" @@ -614,22 +623,9 @@ def serialize_to_as_empty_string(self, serializer): return default -class TransactionTransitionToolConverter(CamelModel): +class TransactionTransitionToolConverter(TransactionValidateToAsEmptyString): """Handler for serializing and validating the `to` field as an empty string.""" - @model_validator(mode="before") - @classmethod - def validate_to_as_empty_string(cls, data: Any) -> Any: - """If the `to` field is an empty string, set the model value to None.""" - if ( - isinstance(data, dict) - and "to" in data - and isinstance(data["to"], str) - and data["to"] == "" - ): - data["to"] = None - return data - @model_serializer(mode="wrap", when_used="json-unless-none") def serialize_to_as_none(self, serializer): """ From 4c1ad8d4f1a113d3aed172a0749a970d498c18e7 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 9 Jan 2025 15:59:49 +0000 Subject: [PATCH 4/4] refactor(base_types): replace left_pad_zeros_up_to_size with fixed bytes constructor parameter --- src/ethereum_test_base_types/base_types.py | 16 +++++- src/ethereum_test_base_types/conversions.py | 50 ++++++++++--------- .../test_tstorage_execution_contexts.py | 4 +- .../test_tstorage_selfdestruct.py | 4 +- .../eip4895_withdrawals/test_withdrawals.py | 8 +-- 5 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/ethereum_test_base_types/base_types.py b/src/ethereum_test_base_types/base_types.py index fa97db4fb89..69b369acc66 100644 --- a/src/ethereum_test_base_types/base_types.py +++ b/src/ethereum_test_base_types/base_types.py @@ -242,12 +242,24 @@ class Sized(cls): # type: ignore Sized._sized_ = Sized return Sized - def __new__(cls, input_bytes: FixedSizeBytesConvertible | T): + def __new__( + cls, + input_bytes: FixedSizeBytesConvertible | T, + *, + left_padding: bool = False, + right_padding: bool = False, + ): """Create a new FixedSizeBytes object.""" if type(input_bytes) is cls: return input_bytes return super(FixedSizeBytes, cls).__new__( - cls, to_fixed_size_bytes(input_bytes, cls.byte_length) + cls, + to_fixed_size_bytes( + input_bytes, + cls.byte_length, + left_padding=left_padding, + right_padding=right_padding, + ), ) def __hash__(self) -> int: diff --git a/src/ethereum_test_base_types/conversions.py b/src/ethereum_test_base_types/conversions.py index 242d96e908f..cd57ac8b72a 100644 --- a/src/ethereum_test_base_types/conversions.py +++ b/src/ethereum_test_base_types/conversions.py @@ -50,29 +50,38 @@ def to_bytes(input_bytes: BytesConvertible) -> bytes: raise Exception("invalid type for `bytes`") -def to_fixed_size_bytes(input_bytes: FixedSizeBytesConvertible, size: int) -> bytes: - """Convert multiple types into fixed-size bytes.""" +def to_fixed_size_bytes( + input_bytes: FixedSizeBytesConvertible, + size: int, + *, + left_padding: bool = False, + right_padding: bool = False, +) -> bytes: + """ + Convert multiple types into fixed-size bytes. + + :param input_bytes: The input data to convert. + :param size: The size of the output bytes. + :param left_padding: Whether to allow left-padding of the input data bytes using zeros. If the + input data is an integer, padding is always performed. + :param right_padding: Whether to allow right-padding of the input data bytes using zeros. If + the input data is an integer, padding is always performed. + """ if isinstance(input_bytes, int): return int.to_bytes(input_bytes, length=size, byteorder="big", signed=input_bytes < 0) input_bytes = to_bytes(input_bytes) if len(input_bytes) > size: raise Exception(f"input is too large for fixed size bytes: {len(input_bytes)} > {size}") - return bytes(input_bytes).rjust(size, b"\x00") - - -def left_pad_zeros_up_to_size(input_bytes: bytes, size: int) -> bytes: - """ - Pad the given data to fit into a size-byte bytes. If the data is longer than - size bytes, it raises a ValueError. If it is shorter, it left pads with zero bytes. - - :param data: The input data to pad. - :return: A Hash object of exactly size bytes. - """ - input_bytes = to_bytes(input_bytes) - if len(input_bytes) > size: - raise ValueError(f"Data cannot be longer than {size} bytes.") - padded_data = bytes(input_bytes).rjust(size, b"\x00") - return bytes(padded_data) + if len(input_bytes) < size: + if left_padding: + return bytes(input_bytes).rjust(size, b"\x00") + if right_padding: + return bytes(input_bytes).ljust(size, b"\x00") + raise Exception( + f"input is too small for fixed size bytes: {len(input_bytes)} < {size}\n" + "Use `left_padding=True` or `right_padding=True` to allow padding." + ) + return input_bytes def to_hex(input_bytes: BytesConvertible) -> str: @@ -89,8 +98,3 @@ def to_number(input_number: NumberConvertible) -> int: if isinstance(input_number, bytes) or isinstance(input_number, SupportsBytes): return int.from_bytes(input_number, byteorder="big") raise Exception("invalid type for `number`") - - -def to_fixed_size_hex(input_bytes: FixedSizeBytesConvertible, size: int) -> str: - """Convert multiple types into a bytes hex string.""" - return "0x" + to_fixed_size_bytes(input_bytes, size).hex() diff --git a/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py b/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py index 0d89022d5cc..99c83f05566 100644 --- a/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py +++ b/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py @@ -8,13 +8,13 @@ import pytest -from ethereum_test_base_types.conversions import left_pad_zeros_up_to_size from ethereum_test_tools import ( Account, Address, Alloc, Bytecode, Environment, + Hash, StateTestFiller, Transaction, ) @@ -308,7 +308,7 @@ def tx(pre: Alloc, caller_address: Address, callee_address: Address) -> Transact return Transaction( sender=pre.fund_eoa(), to=caller_address, - data=left_pad_zeros_up_to_size(callee_address, 32), + data=Hash(callee_address, left_padding=True), gas_limit=1_000_000, ) diff --git a/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py b/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py index 001532f6898..fadd9fde032 100644 --- a/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py +++ b/tests/cancun/eip1153_tstore/test_tstorage_selfdestruct.py @@ -9,13 +9,13 @@ import pytest -from ethereum_test_base_types.conversions import left_pad_zeros_up_to_size from ethereum_test_tools import ( Account, Alloc, Bytecode, CalldataCase, Environment, + Hash, Initcode, StateTestFiller, Switch, @@ -233,7 +233,7 @@ def test_reentrant_selfdestructing_call( data: bytes | Bytecode if pre_existing_contract: callee_address = pre.deploy_contract(code=callee_bytecode) - data = left_pad_zeros_up_to_size(callee_address, 32) + data = Hash(callee_address, left_padding=True) else: callee_address = compute_create_address(address=caller_address, nonce=1) data = Initcode(deploy_code=callee_bytecode) diff --git a/tests/shanghai/eip4895_withdrawals/test_withdrawals.py b/tests/shanghai/eip4895_withdrawals/test_withdrawals.py index 4d009e09529..87cc1501016 100644 --- a/tests/shanghai/eip4895_withdrawals/test_withdrawals.py +++ b/tests/shanghai/eip4895_withdrawals/test_withdrawals.py @@ -10,7 +10,6 @@ import pytest from ethereum_clis import TransitionTool -from ethereum_test_base_types.conversions import left_pad_zeros_up_to_size from ethereum_test_forks import Cancun, Fork from ethereum_test_tools import ( EOA, @@ -19,6 +18,7 @@ Alloc, Block, BlockchainTestFiller, + Hash, Transaction, TransactionException, Withdrawal, @@ -201,7 +201,7 @@ def test_balance_within_block(blockchain_test: BlockchainTestFiller, pre: Alloc) sender=sender, gas_limit=100000, to=contract_address, - data=left_pad_zeros_up_to_size(recipient, 32), + data=Hash(recipient, left_padding=True), ) ], withdrawals=[ @@ -219,7 +219,7 @@ def test_balance_within_block(blockchain_test: BlockchainTestFiller, pre: Alloc) sender=sender, gas_limit=100000, to=contract_address, - data=left_pad_zeros_up_to_size(recipient, 32), + data=Hash(recipient, left_padding=True), ) ] ), @@ -373,7 +373,7 @@ def test_self_destructing_account( sender=sender, gas_limit=100000, to=self_destruct_contract_address, - data=left_pad_zeros_up_to_size(recipient, 32), + data=Hash(recipient, left_padding=True), ) withdrawal = Withdrawal(