From 659e6ce31889c0f15812fa225754ab4fab3b1483 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Mon, 30 Dec 2024 16:42:33 +0000 Subject: [PATCH 1/6] feat(forks,tests): EIP-2935: Contract update --- docs/CHANGELOG.md | 5 +- .../forks/contracts/history_contract.bin | Bin 100 -> 83 bytes src/ethereum_test_forks/forks/forks.py | 4 +- .../contract_deploy_tx.json | 15 ++++ .../spec.py | 6 +- .../test_block_hashes.py | 81 ++++++++++++++++-- .../test_contract_deployment.py | 26 ++++++ .../eip7702_set_code_tx/test_set_code_txs.py | 2 +- 8 files changed, 126 insertions(+), 13 deletions(-) create mode 100644 tests/prague/eip2935_historical_block_hashes_from_state/contract_deploy_tx.json create mode 100644 tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 663a1a502d0..961883bfac9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -21,6 +21,9 @@ Test fixtures for use by clients are available for each release on the [Github r - ✨ [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623) Increase calldata cost ([#1004](https://github.com/ethereum/execution-spec-tests/pull/1004)) - ✨ Add generic precompile-absence test ([#1036](https://github.com/ethereum/execution-spec-tests/pull/1036)) - ✨ Add test for [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) which uses the full discount table of G2 MSM ([#1038](https://github.com/ethereum/execution-spec-tests/pull/1038)) +- 🔀 Update EIP-7251 according to [spec updates](https://github.com/ethereum/EIPs/pull/9127) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)). +- 🔀 Update EIP-7002 according to [spec updates](https://github.com/ethereum/EIPs/pull/9119) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)). +- 🔀 Update EIP-2935 according to [spec updates](https://github.com/ethereum/EIPs/pull/9144) ([#1046](https://github.com/ethereum/execution-spec-tests/pull/1046)) ### 🛠️ Framework @@ -67,8 +70,6 @@ Test fixtures for use by clients are available for each release on the [Github r - ✨ Add a default location for evm logs (`--evm-dump-dir`) when filling tests ([#999](https://github.com/ethereum/execution-spec-tests/pull/999)). - ✨ Slow tests now have greater timeout when making a request to the T8N server ([#1037](https://github.com/ethereum/execution-spec-tests/pull/1037)). - ✨ Introduce [`pytest.mark.parametrize_by_fork`](https://ethereum.github.io/execution-spec-tests/main/writing_tests/test_markers/#pytestmarkfork_parametrize) helper marker ([#1019](https://github.com/ethereum/execution-spec-tests/pull/1019), [#1057](https://github.com/ethereum/execution-spec-tests/pull/1057)). -- 🔀 Update EIP-7251 according to [spec updates](https://github.com/ethereum/EIPs/pull/9127) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)). -- 🔀 Update EIP-7002 according to [spec updates](https://github.com/ethereum/EIPs/pull/9119) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)). - 🐞 fix(consume): allow absolute paths with `--evm-bin` ([#1052](https://github.com/ethereum/execution-spec-tests/pull/1052)). ### 🔧 EVM Tools diff --git a/src/ethereum_test_forks/forks/contracts/history_contract.bin b/src/ethereum_test_forks/forks/contracts/history_contract.bin index 84b6d9f3868c72ab0b065f8e0ab6573e008c4d16..b135cf2c69e8e74211703e2103c4b988924b9b2b 100644 GIT binary patch delta 64 zcmYcZo*-fE7M`GB#+=|39&eh!=*-+G2xKP8|8I0=hO#EGg~SIXD8zq`j*tHv9d8Ph K1Sw<-WdHzz!W41< literal 100 zcmXpm{*MX%i6n%Fr^CeKO$8Hz!xI>tnL#Y?@OaZi1qMcE5HnH!f4r$!NPJL&Lj33G ScsTtx8mLJAKS+;QC<6d6g)&b7 diff --git a/src/ethereum_test_forks/forks/forks.py b/src/ethereum_test_forks/forks/forks.py index 5e8c90185fa..3be81f31204 100644 --- a/src/ethereum_test_forks/forks/forks.py +++ b/src/ethereum_test_forks/forks/forks.py @@ -986,7 +986,7 @@ def system_contracts(cls, block_number: int = 0, timestamp: int = 0) -> List[Add Address(0x00000000219AB540356CBB839CBE05303D7705FA), Address(0x0C15F14308530B7CDB8460094BBB9CC28B9AAAAA), Address(0x00431F263CE400F4455C2DCF564E53007CA4BBBB), - Address(0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E), + Address(0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC), ] + super(Prague, cls).system_contracts(block_number, timestamp) @classmethod @@ -1126,7 +1126,7 @@ def pre_allocation_blockchain(cls) -> Mapping: with open(CURRENT_FOLDER / "contracts" / "history_contract.bin", mode="rb") as f: new_allocation.update( { - 0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E: { + 0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC: { "nonce": 1, "code": f.read(), } diff --git a/tests/prague/eip2935_historical_block_hashes_from_state/contract_deploy_tx.json b/tests/prague/eip2935_historical_block_hashes_from_state/contract_deploy_tx.json new file mode 100644 index 00000000000..cfb28514a23 --- /dev/null +++ b/tests/prague/eip2935_historical_block_hashes_from_state/contract_deploy_tx.json @@ -0,0 +1,15 @@ +{ + "type": "0x0", + "nonce": "0x0", + "to": null, + "gas": "0x3d090", + "gasPrice": "0xe8d4a51000", + "maxPriorityFeePerGas": null, + "maxFeePerGas": null, + "value": "0x0", + "input": "0x60538060095f395ff33373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500", + "v": "0x1b", + "r": "0x539", + "s": "0xbaefe09f0109759", + "hash": "0x8c7bd2d3713a0b2bb693463d2a78c4d612ac47dd38ecb74f8996a4b6fc96f03c" +} \ No newline at end of file diff --git a/tests/prague/eip2935_historical_block_hashes_from_state/spec.py b/tests/prague/eip2935_historical_block_hashes_from_state/spec.py index ba13f6c5c1b..0a709547cda 100644 --- a/tests/prague/eip2935_historical_block_hashes_from_state/spec.py +++ b/tests/prague/eip2935_historical_block_hashes_from_state/spec.py @@ -11,7 +11,7 @@ class ReferenceSpec: version: str -ref_spec_2935 = ReferenceSpec("EIPS/eip-2935.md", "68d54a80a4f5b9c0cf4ae3a10586d63ef221de36") +ref_spec_2935 = ReferenceSpec("EIPS/eip-2935.md", "a04da454a5a6ba86a87bb9e15f811feaff3c849a") @dataclass(frozen=True) @@ -22,6 +22,6 @@ class Spec: """ FORK_TIMESTAMP = 15_000 - HISTORY_STORAGE_ADDRESS = 0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E - HISTORY_SERVE_WINDOW = 8192 + HISTORY_STORAGE_ADDRESS = 0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC + HISTORY_SERVE_WINDOW = 8191 BLOCKHASH_OLD_WINDOW = 256 diff --git a/tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py b/tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py index 6e0ff09ddd0..090764aca97 100644 --- a/tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py +++ b/tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py @@ -311,9 +311,9 @@ def test_block_hashes_history( @pytest.mark.parametrize( "block_number,reverts", [ - pytest.param(1, False, id="current_block"), - pytest.param(2, False, id="future_block"), - pytest.param(2**64 - 1, False, id="2**64-1"), + pytest.param(1, True, id="current_block"), + pytest.param(2, True, id="future_block"), + pytest.param(2**64 - 1, True, id="2**64-1"), pytest.param(2**64, True, id="2**64"), ], ) @@ -337,13 +337,24 @@ def test_invalid_history_contract_calls( returned_block_hash_slot = storage.store_next(0) block_hash_opcode_slot = storage.store_next(0) + return_offset = 64 + return_size = 32 + args_size = 32 + # Check the first block outside of the window if any code = ( Op.MSTORE(0, block_number) + Op.SSTORE( - return_code_slot, Op.CALL(Op.GAS, Spec.HISTORY_STORAGE_ADDRESS, 0, 0, 32, 32, 64) + return_code_slot, + Op.CALL( + address=Spec.HISTORY_STORAGE_ADDRESS, + args_offset=0, + args_size=args_size, + ret_offset=return_offset, + ret_size=return_size, + ), ) - + Op.SSTORE(returned_block_hash_slot, Op.MLOAD(32)) + + Op.SSTORE(returned_block_hash_slot, Op.MLOAD(return_offset)) + Op.SSTORE(block_hash_opcode_slot, Op.BLOCKHASH(block_number)) ) check_contract_address = pre.deploy_contract(code, storage=storage.canary()) @@ -364,3 +375,63 @@ def test_invalid_history_contract_calls( post=post, reverts=reverts, ) + + +@pytest.mark.parametrize( + "args_size,reverts", + [ + pytest.param(0, True, id="zero_size"), + pytest.param(33, True, id="too_large"), + pytest.param(31, True, id="too_small"), + ], +) +@pytest.mark.valid_from("Prague") +def test_invalid_history_contract_calls_input_size( + blockchain_test: BlockchainTestFiller, + pre: Alloc, + reverts: bool, + args_size: int, +): + """Test calling the history contract with invalid input sizes.""" + storage = Storage() + + return_code_slot = storage.store_next(not reverts) + returned_block_hash_slot = storage.store_next(0) + + return_offset = 64 + return_size = 32 + block_number = 0 + + # Check the first block outside of the window if any + code = ( + Op.MSTORE(0, block_number) + + Op.SSTORE( + return_code_slot, + Op.CALL( + address=Spec.HISTORY_STORAGE_ADDRESS, + args_offset=0, + args_size=args_size, + ret_offset=return_offset, + ret_size=return_size, + ), + ) + + Op.SSTORE(returned_block_hash_slot, Op.MLOAD(return_offset)) + ) + check_contract_address = pre.deploy_contract(code, storage=storage.canary()) + + txs = [ + Transaction( + to=check_contract_address, + gas_limit=10_000_000, + sender=pre.fund_eoa(), + ) + ] + post = {check_contract_address: Account(storage=storage)} + + blocks = [Block(txs=txs)] + blockchain_test( + pre=pre, + blocks=blocks, + post=post, + reverts=reverts, + ) diff --git a/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py b/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py new file mode 100644 index 00000000000..5aff02cdfa9 --- /dev/null +++ b/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py @@ -0,0 +1,26 @@ +""" +abstract: Tests [EIP-2935: Serve historical block hashes from state](https://eips.ethereum.org/EIPS/eip-2935). + Test system contract deployment for [EIP-2935: Serve historical block hashes from state](https://eips.ethereum.org/EIPS/eip-2935). +""" # noqa: E501 + +from os.path import realpath +from pathlib import Path + +from ethereum_test_forks import Prague +from ethereum_test_tools import Address, generate_system_contract_deploy_test + +from .spec import Spec, ref_spec_2935 + +REFERENCE_SPEC_GIT_PATH = ref_spec_2935.git_path +REFERENCE_SPEC_VERSION = ref_spec_2935.version + + +@generate_system_contract_deploy_test( + fork=Prague, + tx_json_path=Path(realpath(__file__)).parent / "contract_deploy_tx.json", + expected_deploy_address=Address(Spec.HISTORY_STORAGE_ADDRESS), + expected_system_contract_storage=None, +) +def test_system_contract_deployment(*args, **kwargs): + """Verify deployment of the block hashes system contract.""" + yield from [] diff --git a/tests/prague/eip7702_set_code_tx/test_set_code_txs.py b/tests/prague/eip7702_set_code_tx/test_set_code_txs.py index 5f898cde56e..bb54fa68069 100644 --- a/tests/prague/eip7702_set_code_tx/test_set_code_txs.py +++ b/tests/prague/eip7702_set_code_tx/test_set_code_txs.py @@ -2654,7 +2654,7 @@ def test_set_code_to_system_contract( ) caller_payload = consolidation_request.calldata call_value = consolidation_request.value - case Address(0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E): # EIP-2935 + case Address(0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC): # EIP-2935 caller_payload = Hash(0) caller_code_storage[call_return_data_size_slot] = 32 case _: From 128767dc26a2e93d2e29e44d9d1bb84b7fb726e9 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 10 Jan 2025 11:01:31 -0600 Subject: [PATCH 2/6] Update tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py Co-authored-by: danceratopz --- .../test_contract_deployment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py b/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py index 5aff02cdfa9..8536023d6ec 100644 --- a/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py +++ b/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py @@ -23,4 +23,4 @@ ) def test_system_contract_deployment(*args, **kwargs): """Verify deployment of the block hashes system contract.""" - yield from [] + yield from [] # empty list: only test deployment; don't test with any subsequent transactions From 591ea55f4ec00a67827a8d4398aabf7d40cf7b6f Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 10 Jan 2025 17:07:35 +0000 Subject: [PATCH 3/6] fix: Add storage hint --- .../test_block_hashes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py b/tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py index 090764aca97..c31512696b9 100644 --- a/tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py +++ b/tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py @@ -395,7 +395,7 @@ def test_invalid_history_contract_calls_input_size( """Test calling the history contract with invalid input sizes.""" storage = Storage() - return_code_slot = storage.store_next(not reverts) + return_code_slot = storage.store_next(not reverts, "history storage call result") returned_block_hash_slot = storage.store_next(0) return_offset = 64 From 749acbebbf604dc27ad1260e4932fb2380628900 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 10 Jan 2025 17:46:26 +0000 Subject: [PATCH 4/6] refactor(tools): Use `Block` instead of `Tuple` --- src/ethereum_test_tools/utility/generators.py | 39 +++++++++---------- .../test_contract_deployment.py | 1 - .../test_contract_deployment.py | 19 +++++---- .../test_contract_deployment.py | 19 +++++---- 4 files changed, 36 insertions(+), 42 deletions(-) diff --git a/src/ethereum_test_tools/utility/generators.py b/src/ethereum_test_tools/utility/generators.py index 806041ed62f..c5f70971696 100644 --- a/src/ethereum_test_tools/utility/generators.py +++ b/src/ethereum_test_tools/utility/generators.py @@ -3,14 +3,14 @@ import json from enum import Enum from pathlib import Path -from typing import Dict, Generator, List, Protocol, Tuple +from typing import Dict, Generator, List, Protocol import pytest from ethereum_test_base_types import Account, Address from ethereum_test_forks import Fork from ethereum_test_specs import BlockchainTestFiller -from ethereum_test_specs.blockchain import Block, Header +from ethereum_test_specs.blockchain import Block from ethereum_test_types import Alloc, Transaction @@ -25,16 +25,18 @@ def __call__( *, fork: Fork, pre: Alloc, - ) -> Generator[Tuple[Transaction | None, Header | None], None, None]: + post: Alloc, + ) -> Generator[Block, None, None]: """ Args: fork (Fork): The fork to test. pre (Alloc): The pre state of the blockchain. + post (Alloc): The post state of the blockchain. Yields: - Tuple[Transaction | None, Header | None]: Once per block to add after the contract is - deployed, with a single transaction to execute and the header object used to - verify the block. + Block: To add after the block where the contract was deployed (e.g. can contain extra + transactions to execute after the system contract has been deployed, and/or a header + object to verify that the headers are correct). """ ... @@ -48,10 +50,11 @@ class DeploymentTestType(Enum): def generate_system_contract_deploy_test( + *, fork: Fork, tx_json_path: Path, expected_deploy_address: Address, - expected_system_contract_storage: Dict | None, + expected_system_contract_storage: Dict | None = None, ): """ Generate a test that verifies the correct deployment of a system contract. @@ -66,7 +69,8 @@ def generate_system_contract_deploy_test( contract. Providing a JSON file is useful to copy-paste the transaction from the EIP. expected_deploy_address (Address): The expected address of the deployed contract. - expected_system_contract_storage (Dict): The expected storage of the system contract. + expected_system_contract_storage (Dict | None): The expected storage of the system + contract. """ with open(tx_json_path, mode="r") as f: @@ -126,18 +130,6 @@ def wrapper( ), ] - for tx_header_verify in func(fork=fork, pre=pre): - txs = [] - if tx_header_verify[0] is not None: - txs.append(tx_header_verify[0]) - header_verify = tx_header_verify[1] - blocks.append( - Block( - txs=txs, - header_verify=header_verify, - ) - ) - pre[expected_deploy_address] = Account( code=b"", # Remove the code that is automatically allocated on the fork nonce=0, @@ -149,7 +141,7 @@ def wrapper( expected_deploy_address_int = int.from_bytes(expected_deploy_address, "big") - post = {} + post = Alloc() fork_pre_allocation = fork.pre_allocation_blockchain() assert expected_deploy_address_int in fork_pre_allocation expected_code = fork_pre_allocation[expected_deploy_address_int]["code"] @@ -167,6 +159,11 @@ def wrapper( post[deployer_address] = Account( nonce=1, ) + + # Extra blocks (if any) returned by the decorated function to add after the + # contract is deployed. + blocks += list(func(fork=fork, pre=pre, post=post)) + blockchain_test( pre=pre, blocks=blocks, diff --git a/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py b/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py index 8536023d6ec..5dd6582d463 100644 --- a/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py +++ b/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py @@ -19,7 +19,6 @@ fork=Prague, tx_json_path=Path(realpath(__file__)).parent / "contract_deploy_tx.json", expected_deploy_address=Address(Spec.HISTORY_STORAGE_ADDRESS), - expected_system_contract_storage=None, ) def test_system_contract_deployment(*args, **kwargs): """Verify deployment of the block hashes system contract.""" diff --git a/tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py b/tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py index 4cf87601d31..8af8208d083 100644 --- a/tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py +++ b/tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py @@ -5,12 +5,13 @@ from os.path import realpath from pathlib import Path -from typing import Generator, Tuple +from typing import Generator from ethereum_test_forks import Fork, Prague from ethereum_test_tools import ( Address, Alloc, + Block, Header, Requests, Transaction, @@ -28,13 +29,13 @@ fork=Prague, tx_json_path=Path(realpath(__file__)).parent / "contract_deploy_tx.json", expected_deploy_address=Address(Spec.WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS), - expected_system_contract_storage=None, ) def test_system_contract_deployment( *, fork: Fork, pre: Alloc, -) -> Generator[Tuple[Transaction, Header], None, None]: + **kwargs, +) -> Generator[Block, None, None]: """Verify calling the withdrawals system contract after deployment.""" sender = pre.fund_eoa() withdrawal_request = WithdrawalRequest( @@ -55,11 +56,9 @@ def test_system_contract_deployment( value=withdrawal_request.value, ) - yield from [ - ( - test_transaction, - Header( - requests_hash=Requests(withdrawal_request), - ), + yield Block( + txs=[test_transaction], + header=Header( + requests_hash=Requests(withdrawal_request), ), - ] + ) diff --git a/tests/prague/eip7251_consolidations/test_contract_deployment.py b/tests/prague/eip7251_consolidations/test_contract_deployment.py index 0f16d6c5479..c7c326ebdb8 100644 --- a/tests/prague/eip7251_consolidations/test_contract_deployment.py +++ b/tests/prague/eip7251_consolidations/test_contract_deployment.py @@ -5,12 +5,13 @@ from os.path import realpath from pathlib import Path -from typing import Generator, Tuple +from typing import Generator from ethereum_test_forks import Fork, Prague from ethereum_test_tools import ( Address, Alloc, + Block, Header, Requests, Transaction, @@ -28,13 +29,13 @@ fork=Prague, tx_json_path=Path(realpath(__file__)).parent / "contract_deploy_tx.json", expected_deploy_address=Address(Spec.CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS), - expected_system_contract_storage=None, ) def test_system_contract_deployment( *, fork: Fork, pre: Alloc, -) -> Generator[Tuple[Transaction, Header], None, None]: + **kwargs, +) -> Generator[Block, None, None]: """Verify calling the consolidation system contract after deployment.""" sender = pre.fund_eoa() consolidation_request = ConsolidationRequest( @@ -55,11 +56,9 @@ def test_system_contract_deployment( value=consolidation_request.value, ) - yield from [ - ( - test_transaction, - Header( - requests_hash=Requests(consolidation_request), - ), + yield Block( + txs=[test_transaction], + header=Header( + requests_hash=Requests(consolidation_request), ), - ] + ) From da84e2a594fc46ca9e5515e07861b35feac1ee73 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 10 Jan 2025 18:42:07 +0000 Subject: [PATCH 5/6] fix(tools): pass test_type --- src/ethereum_test_tools/__init__.py | 3 ++- src/ethereum_test_tools/utility/generators.py | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/ethereum_test_tools/__init__.py b/src/ethereum_test_tools/__init__.py index d67de473ca5..7383377c192 100644 --- a/src/ethereum_test_tools/__init__.py +++ b/src/ethereum_test_tools/__init__.py @@ -82,7 +82,7 @@ Yul, YulCompiler, ) -from .utility.generators import generate_system_contract_deploy_test +from .utility.generators import DeploymentTestType, generate_system_contract_deploy_test from .utility.pytest import extend_with_defaults __all__ = ( @@ -108,6 +108,7 @@ "Conditional", "ConsolidationRequest", "DepositRequest", + "DeploymentTestType", "EngineAPIError", "Environment", "EOFException", diff --git a/src/ethereum_test_tools/utility/generators.py b/src/ethereum_test_tools/utility/generators.py index c5f70971696..72135a5bb52 100644 --- a/src/ethereum_test_tools/utility/generators.py +++ b/src/ethereum_test_tools/utility/generators.py @@ -14,6 +14,13 @@ from ethereum_test_types import Alloc, Transaction +class DeploymentTestType(Enum): + """Represents the type of deployment test.""" + + DEPLOY_BEFORE_FORK = "deploy_before_fork" + DEPLOY_AFTER_FORK = "deploy_after_fork" + + class SystemContractDeployTestFunction(Protocol): """ Represents a function to be decorated with the `generate_system_contract_deploy_test` @@ -26,12 +33,14 @@ def __call__( fork: Fork, pre: Alloc, post: Alloc, + test_type: DeploymentTestType, ) -> Generator[Block, None, None]: """ Args: fork (Fork): The fork to test. pre (Alloc): The pre state of the blockchain. post (Alloc): The post state of the blockchain. + test_type (DeploymentTestType): The type of deployment test currently being filled. Yields: Block: To add after the block where the contract was deployed (e.g. can contain extra @@ -42,13 +51,6 @@ def __call__( ... -class DeploymentTestType(Enum): - """Represents the type of deployment test.""" - - DEPLOY_BEFORE_FORK = "deploy_before_fork" - DEPLOY_AFTER_FORK = "deploy_after_fork" - - def generate_system_contract_deploy_test( *, fork: Fork, @@ -162,7 +164,7 @@ def wrapper( # Extra blocks (if any) returned by the decorated function to add after the # contract is deployed. - blocks += list(func(fork=fork, pre=pre, post=post)) + blocks += list(func(fork=fork, pre=pre, post=post, test_type=test_type)) blockchain_test( pre=pre, From a272989dcbfe5107d9b51a6f0170625142e5e36d Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 10 Jan 2025 18:58:25 +0000 Subject: [PATCH 6/6] new(tests): Add block hash checking on deployment test --- .../test_contract_deployment.py | 74 ++++++++++++++++++- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py b/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py index 5dd6582d463..2d8bc81cef6 100644 --- a/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py +++ b/tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py @@ -5,9 +5,19 @@ from os.path import realpath from pathlib import Path +from typing import Dict from ethereum_test_forks import Prague -from ethereum_test_tools import Address, generate_system_contract_deploy_test +from ethereum_test_tools import ( + Account, + Address, + Alloc, + Block, + DeploymentTestType, + Transaction, + generate_system_contract_deploy_test, +) +from ethereum_test_tools import Opcodes as Op from .spec import Spec, ref_spec_2935 @@ -20,6 +30,64 @@ tx_json_path=Path(realpath(__file__)).parent / "contract_deploy_tx.json", expected_deploy_address=Address(Spec.HISTORY_STORAGE_ADDRESS), ) -def test_system_contract_deployment(*args, **kwargs): +def test_system_contract_deployment( + *, + pre: Alloc, + post: Alloc, + test_type: DeploymentTestType, + **kwargs, +): """Verify deployment of the block hashes system contract.""" - yield from [] # empty list: only test deployment; don't test with any subsequent transactions + # Deploy a contract that calls the history contract and verifies the block hashes. + yield Block() # Empty block just to have more history in the contract. + + # We are going to query blocks even before contract deployment. + code = ( + sum( + Op.MSTORE(0, block_number) + + Op.POP( + Op.CALL( + address=Spec.HISTORY_STORAGE_ADDRESS, + args_offset=0, + args_size=32, + ret_offset=0, + ret_size=32, + ), + ) + + Op.SSTORE(block_number, Op.ISZERO(Op.ISZERO(Op.MLOAD(0)))) + for block_number in range(1, 4) + ) + + Op.STOP + ) + deployed_contract = pre.deploy_contract(code) + + tx = Transaction( + to=deployed_contract, + gas_limit=10_000_000, + sender=pre.fund_eoa(), + ) + + yield Block(txs=[tx]) + + storage: Dict + if test_type == DeploymentTestType.DEPLOY_BEFORE_FORK: + # Fork happens at block 2, and the contract is already there, so from block number 1 and + # after, the block hashes should be there. + storage = { + 1: 1, # Block prior to the fork, it's the first hash saved. + 2: 1, # Fork block, hash should be there. + 3: 1, # Empty block added at the start of this function, hash should be there. + } + elif test_type == DeploymentTestType.DEPLOY_AFTER_FORK: + # The contract should have the block hashes after contract deployment. + storage = { + 1: 0, # Fork block, but contract is not there yet. + 2: 1, # Deployment block, this is the first hash that gets added because it's added on + # the next block. + 3: 1, # Empty block added at the start of this function, hash should be there. + } + + post[deployed_contract] = Account( + balance=0, + storage=storage, + )