diff --git a/packages/testing/src/execution_testing/fixtures/__init__.py b/packages/testing/src/execution_testing/fixtures/__init__.py index 18d4b3a118..25e0805b28 100644 --- a/packages/testing/src/execution_testing/fixtures/__init__.py +++ b/packages/testing/src/execution_testing/fixtures/__init__.py @@ -9,6 +9,7 @@ from .blockchain import ( BlockchainEngineFixture, BlockchainEngineFixtureCommon, + BlockchainEngineStatefulFixture, BlockchainEngineSyncFixture, BlockchainEngineXFixture, BlockchainFixture, @@ -33,6 +34,7 @@ "BaseFixture", "BlockchainEngineFixture", "BlockchainEngineFixtureCommon", + "BlockchainEngineStatefulFixture", "BlockchainEngineSyncFixture", "BlockchainEngineXFixture", "BlockchainFixture", diff --git a/packages/testing/src/execution_testing/fixtures/blockchain.py b/packages/testing/src/execution_testing/fixtures/blockchain.py index e2bad75ae5..0da7031b68 100644 --- a/packages/testing/src/execution_testing/fixtures/blockchain.py +++ b/packages/testing/src/execution_testing/fixtures/blockchain.py @@ -813,6 +813,39 @@ class BlockchainEngineXFixture(BlockchainEngineFixtureCommon): """Engine API payloads for blockchain execution.""" +class BlockchainEngineStatefulFixture(BlockchainEngineFixtureCommon): + """ + Engine fixture for snapshot-based stateful testing. + + Instead of embedding pre-allocation or referencing a computed group, + this fixture references an external snapshot that the consumer must + pre-load. Setup payloads deploy contracts and seed accounts on top + of the snapshot before the actual test payloads execute. + """ + + model_config = CamelModel.model_config | {"extra": "ignore"} + + format_name: ClassVar[str] = "blockchain_test_stateful_engine" + description: ClassVar[str] = ( + "Tests that generate a Blockchain Test fixture for " + "snapshot-based stateful Engine API testing." + ) + format_phases: ClassVar[Set[FixtureFillingPhase]] = { + FixtureFillingPhase.FILL, + FixtureFillingPhase.PRE_ALLOC_GENERATION, + } + + snapshot_block_number: HexNumber + snapshot_block_hash: Hash + + setup_payloads: List[FixtureEngineNewPayload] = Field( + ..., alias="setupEngineNewPayloads" + ) + payloads: List[FixtureEngineNewPayload] = Field( + ..., alias="engineNewPayloads" + ) + + class BlockchainEngineSyncFixture(BlockchainEngineFixture): """ Engine Sync specific test fixture information. diff --git a/packages/testing/src/execution_testing/fixtures/tests/test_base.py b/packages/testing/src/execution_testing/fixtures/tests/test_base.py index 9aaa251329..045c137da5 100644 --- a/packages/testing/src/execution_testing/fixtures/tests/test_base.py +++ b/packages/testing/src/execution_testing/fixtures/tests/test_base.py @@ -2,7 +2,23 @@ import pytest +from execution_testing.base_types import ( + Address, + Bloom, + Bytes, + Hash, + HeaderNonce, +) +from execution_testing.forks import Prague +from execution_testing.test_types import Transaction + from ..base import BaseFixture +from ..blockchain import ( + BlockchainEngineStatefulFixture, + FixtureConfig, + FixtureEngineNewPayload, + FixtureHeader, +) from ..file import Fixtures from ..state import FixtureEnvironment, FixtureTransaction, StateFixture from ..transaction import FixtureResult, TransactionFixture @@ -44,6 +60,87 @@ def test_json_dict() -> None: ), id="TransactionFixture", ), + pytest.param( + BlockchainEngineStatefulFixture( + fork=Prague, + last_block_hash=Hash(1), + post_state_hash=Hash(2), + config=FixtureConfig(fork=Prague), + snapshot_block_number=0, + snapshot_block_hash=Hash(0), + setup_payloads=[ + FixtureEngineNewPayload.from_fixture_header( + fork=Prague, + header=FixtureHeader( + parent_hash=Hash(0), + ommers_hash=Hash(1), + fee_recipient=Address(2), + state_root=Hash(3), + transactions_trie=Hash(4), + receipts_root=Hash(5), + logs_bloom=Bloom(6), + difficulty=7, + number=1, + gas_limit=9, + gas_used=10, + timestamp=11, + extra_data=Bytes([12]), + prev_randao=Hash(13), + nonce=HeaderNonce(14), + base_fee_per_gas=15, + withdrawals_root=Hash(16), + blob_gas_used=17, + excess_blob_gas=18, + parent_beacon_block_root=19, + requests_hash=20, + ), + transactions=[ + Transaction( + max_fee_per_gas=7, + ).with_signature_and_sender(), + ], + withdrawals=[], + requests=[], + ), + ], + payloads=[ + FixtureEngineNewPayload.from_fixture_header( + fork=Prague, + header=FixtureHeader( + parent_hash=Hash(10), + ommers_hash=Hash(1), + fee_recipient=Address(2), + state_root=Hash(3), + transactions_trie=Hash(4), + receipts_root=Hash(5), + logs_bloom=Bloom(6), + difficulty=7, + number=2, + gas_limit=9, + gas_used=10, + timestamp=12, + extra_data=Bytes([12]), + prev_randao=Hash(13), + nonce=HeaderNonce(14), + base_fee_per_gas=15, + withdrawals_root=Hash(16), + blob_gas_used=17, + excess_blob_gas=18, + parent_beacon_block_root=19, + requests_hash=20, + ), + transactions=[ + Transaction( + max_fee_per_gas=7, + ).with_signature_and_sender(), + ], + withdrawals=[], + requests=[], + ), + ], + ), + id="BlockchainEngineStatefulFixture", + ), ], ) def test_base_fixtures_parsing(fixture: BaseFixture) -> None: diff --git a/packages/testing/src/execution_testing/fixtures/tests/test_blockchain.py b/packages/testing/src/execution_testing/fixtures/tests/test_blockchain.py index 33a153b6a6..126d2a9df3 100644 --- a/packages/testing/src/execution_testing/fixtures/tests/test_blockchain.py +++ b/packages/testing/src/execution_testing/fixtures/tests/test_blockchain.py @@ -36,8 +36,10 @@ ) from ..blockchain import ( + BlockchainEngineStatefulFixture, EngineNewPayloadParameters, FixtureBlockBase, + FixtureConfig, FixtureEngineNewPayload, FixtureExecutionPayload, FixtureHeader, @@ -985,6 +987,158 @@ }, id="fixture_engine_new_payload_2", ), + pytest.param( + True, + BlockchainEngineStatefulFixture( + fork=Prague, + last_block_hash=Hash(1), + post_state_hash=Hash(2), + config=FixtureConfig(fork=Prague), + snapshot_block_number=100, + snapshot_block_hash=Hash(99), + setup_payloads=[ + FixtureEngineNewPayload.from_fixture_header( + fork=Prague, + header=FixtureHeader( + parent_hash=Hash(0), + ommers_hash=Hash(1), + fee_recipient=Address(2), + state_root=Hash(3), + transactions_trie=Hash(4), + receipts_root=Hash(5), + logs_bloom=Bloom(6), + difficulty=7, + number=1, + gas_limit=9, + gas_used=10, + timestamp=11, + extra_data=Bytes([12]), + prev_randao=Hash(13), + nonce=HeaderNonce(14), + base_fee_per_gas=15, + withdrawals_root=Hash(16), + blob_gas_used=17, + excess_blob_gas=18, + parent_beacon_block_root=19, + requests_hash=20, + ), + transactions=[], + withdrawals=[], + requests=[], + ), + ], + payloads=[ + FixtureEngineNewPayload.from_fixture_header( + fork=Prague, + header=FixtureHeader( + parent_hash=Hash(0), + ommers_hash=Hash(1), + fee_recipient=Address(2), + state_root=Hash(3), + transactions_trie=Hash(4), + receipts_root=Hash(5), + logs_bloom=Bloom(6), + difficulty=7, + number=1, + gas_limit=9, + gas_used=10, + timestamp=11, + extra_data=Bytes([12]), + prev_randao=Hash(13), + nonce=HeaderNonce(14), + base_fee_per_gas=15, + withdrawals_root=Hash(16), + blob_gas_used=17, + excess_blob_gas=18, + parent_beacon_block_root=19, + requests_hash=20, + ), + transactions=[], + withdrawals=[], + requests=[], + ), + ], + ), + { + "_info": {}, + "network": "Prague", + "postStateHash": Hash(2).hex(), + "lastblockhash": Hash(1).hex(), + "config": { + "network": "Prague", + "chainid": "0x01", + }, + "snapshotBlockNumber": "0x64", + "snapshotBlockHash": Hash(99).hex(), + "setupEngineNewPayloads": [ + to_json( + FixtureEngineNewPayload.from_fixture_header( + fork=Prague, + header=FixtureHeader( + parent_hash=Hash(0), + ommers_hash=Hash(1), + fee_recipient=Address(2), + state_root=Hash(3), + transactions_trie=Hash(4), + receipts_root=Hash(5), + logs_bloom=Bloom(6), + difficulty=7, + number=1, + gas_limit=9, + gas_used=10, + timestamp=11, + extra_data=Bytes([12]), + prev_randao=Hash(13), + nonce=HeaderNonce(14), + base_fee_per_gas=15, + withdrawals_root=Hash(16), + blob_gas_used=17, + excess_blob_gas=18, + parent_beacon_block_root=19, + requests_hash=20, + ), + transactions=[], + withdrawals=[], + requests=[], + ) + ), + ], + "engineNewPayloads": [ + to_json( + FixtureEngineNewPayload.from_fixture_header( + fork=Prague, + header=FixtureHeader( + parent_hash=Hash(0), + ommers_hash=Hash(1), + fee_recipient=Address(2), + state_root=Hash(3), + transactions_trie=Hash(4), + receipts_root=Hash(5), + logs_bloom=Bloom(6), + difficulty=7, + number=1, + gas_limit=9, + gas_used=10, + timestamp=11, + extra_data=Bytes([12]), + prev_randao=Hash(13), + nonce=HeaderNonce(14), + base_fee_per_gas=15, + withdrawals_root=Hash(16), + blob_gas_used=17, + excess_blob_gas=18, + parent_beacon_block_root=19, + requests_hash=20, + ), + transactions=[], + withdrawals=[], + requests=[], + ) + ), + ], + }, + id="blockchain_engine_stateful_fixture", + ), ], ) class TestPydanticModelConversion: