Skip to content

Commit cb969b6

Browse files
authored
fix(fixtures,specs): Fix Engine X Env->Genesis Conversion (#1914)
* fix(specs,fixtures): Pre-alloc groups on tests that modify the pre-allocation * fix(specs): state tests and blockchain tests with default env have same pre-alloc group * fix(tests): Remove some unnecessary custom envs * refactor(specs): Genesis header creation * fix(fixtures): Use defaults in FixtureHeader for genesis generation
1 parent 6cf5107 commit cb969b6

File tree

11 files changed

+304
-177
lines changed

11 files changed

+304
-177
lines changed

src/ethereum_test_fixtures/blockchain.py

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -119,25 +119,27 @@ class FixtureHeader(CamelModel):
119119
We combine the `Environment` and `Result` contents to create this model.
120120
"""
121121

122-
parent_hash: Hash
122+
parent_hash: Hash = Hash(0)
123123
ommers_hash: Hash = Field(Hash(EmptyOmmersRoot), alias="uncleHash")
124124
fee_recipient: Address = Field(
125125
..., alias="coinbase", validation_alias=AliasChoices("coinbase", "miner")
126126
)
127127
state_root: Hash
128128
transactions_trie: Hash = Field(
129-
validation_alias=AliasChoices("transactionsTrie", "transactionsRoot")
129+
Hash(EmptyTrieRoot), validation_alias=AliasChoices("transactionsTrie", "transactionsRoot")
130130
)
131131
receipts_root: Hash = Field(
132-
..., alias="receiptTrie", validation_alias=AliasChoices("receiptTrie", "receiptsRoot")
132+
Hash(EmptyTrieRoot),
133+
alias="receiptTrie",
134+
validation_alias=AliasChoices("receiptTrie", "receiptsRoot"),
133135
)
134136
logs_bloom: Bloom = Field(
135-
..., alias="bloom", validation_alias=AliasChoices("bloom", "logsBloom")
137+
Bloom(0), alias="bloom", validation_alias=AliasChoices("bloom", "logsBloom")
136138
)
137139
difficulty: ZeroPaddedHexNumber = ZeroPaddedHexNumber(0)
138140
number: ZeroPaddedHexNumber
139141
gas_limit: ZeroPaddedHexNumber
140-
gas_used: ZeroPaddedHexNumber
142+
gas_used: ZeroPaddedHexNumber = ZeroPaddedHexNumber(0)
141143
timestamp: ZeroPaddedHexNumber
142144
extra_data: Bytes
143145
prev_randao: Hash = Field(Hash(0), alias="mixHash")
@@ -219,32 +221,16 @@ def block_hash(self) -> Hash:
219221
@classmethod
220222
def genesis(cls, fork: Fork, env: Environment, state_root: Hash) -> "FixtureHeader":
221223
"""Get the genesis header for the given fork."""
222-
return FixtureHeader(
223-
parent_hash=0,
224-
ommers_hash=EmptyOmmersRoot,
225-
fee_recipient=0,
226-
state_root=state_root,
227-
transactions_trie=EmptyTrieRoot,
228-
receipts_root=EmptyTrieRoot,
229-
logs_bloom=0,
230-
difficulty=0x20000 if env.difficulty is None else env.difficulty,
231-
number=0,
232-
gas_limit=env.gas_limit,
233-
gas_used=0,
234-
timestamp=0,
235-
extra_data=b"\x00",
236-
prev_randao=0,
237-
nonce=0,
238-
base_fee_per_gas=env.base_fee_per_gas,
239-
blob_gas_used=env.blob_gas_used,
240-
excess_blob_gas=env.excess_blob_gas,
241-
withdrawals_root=(
242-
Withdrawal.list_root(env.withdrawals) if env.withdrawals is not None else None
243-
),
244-
parent_beacon_block_root=env.parent_beacon_block_root,
245-
requests_hash=Requests() if fork.header_requests_required(0, 0) else None,
246-
fork=fork,
247-
)
224+
environment_values = env.model_dump(exclude_none=True, exclude={"withdrawals"})
225+
if env.withdrawals is not None:
226+
environment_values["withdrawals_root"] = Withdrawal.list_root(env.withdrawals)
227+
environment_values["extra_data"] = env.extra_data
228+
extras = {
229+
"state_root": state_root,
230+
"requests_hash": Requests() if fork.header_requests_required(0, 0) else None,
231+
"fork": fork,
232+
}
233+
return FixtureHeader(**environment_values, **extras)
248234

249235

250236
class FixtureExecutionPayload(CamelModel):

src/ethereum_test_fixtures/pre_alloc_groups.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,30 @@ class PreAllocGroup(CamelModel):
2323

2424
model_config = {"populate_by_name": True} # Allow both field names and aliases
2525

26-
test_count: int = Field(0, description="Number of tests in this group")
27-
pre_account_count: int = Field(0, description="Number of accounts in the pre-allocation")
28-
test_ids: List[str] = Field(default_factory=list, alias="testIds")
26+
test_ids: List[str] = Field(default_factory=list)
2927
environment: Environment = Field(..., description="Grouping environment for this test group")
3028
fork: Fork = Field(..., alias="network")
3129
pre: Alloc
3230

33-
def model_post_init(self, __context):
34-
"""Post-init hook to ensure pre is not None."""
35-
super().model_post_init(__context)
31+
@computed_field(description="Number of accounts in the pre-allocation") # type: ignore[prop-decorator]
32+
@property
33+
def pre_account_count(self) -> int:
34+
"""Return the amount of accounts the pre-allocation group holds."""
35+
return len(self.pre.root)
3636

37-
self.pre = Alloc.merge(
38-
Alloc.model_validate(self.fork.pre_allocation_blockchain()),
39-
self.pre,
40-
)
37+
@computed_field(description="Number of tests in this group") # type: ignore[prop-decorator]
38+
@property
39+
def test_count(self) -> int:
40+
"""Return the amount of tests that use this pre-allocation group."""
41+
return len(self.test_ids)
4142

42-
@computed_field # type: ignore[misc]
43+
@computed_field # type: ignore[prop-decorator]
44+
@property
4345
def genesis(self) -> FixtureHeader:
4446
"""Get the genesis header for this group."""
4547
return FixtureHeader.genesis(
4648
self.fork,
47-
self.environment.set_fork_requirements(self.fork),
49+
self.environment,
4850
self.pre.state_root(),
4951
)
5052

@@ -58,8 +60,6 @@ def to_file(self, file: Path) -> None:
5860
for account in previous_pre_alloc_group.pre:
5961
if account not in self.pre:
6062
self.pre[account] = previous_pre_alloc_group.pre[account]
61-
self.pre_account_count += previous_pre_alloc_group.pre_account_count
62-
self.test_count += previous_pre_alloc_group.test_count
6363
self.test_ids.extend(previous_pre_alloc_group.test_ids)
6464

6565
with open(file, "w") as f:

src/ethereum_test_specs/base.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,18 +237,19 @@ def update_pre_alloc_groups(
237237
)
238238
group.fork = fork
239239
group.test_ids.append(str(test_id))
240-
group.test_count = len(group.test_ids)
241-
group.pre_account_count = len(group.pre.root)
242240
pre_alloc_groups[pre_alloc_hash] = group
243241
else:
244242
# Create new group - use Environment instead of expensive genesis generation
243+
genesis_env = self.get_genesis_environment(fork)
244+
pre_alloc = Alloc.merge(
245+
Alloc.model_validate(fork.pre_allocation_blockchain()),
246+
self.pre,
247+
)
245248
group = PreAllocGroup(
246-
test_count=1,
247-
pre_account_count=len(self.pre.root),
248249
test_ids=[str(test_id)],
249250
fork=fork,
250-
environment=self.get_genesis_environment(fork),
251-
pre=self.pre,
251+
environment=genesis_env,
252+
pre=pre_alloc,
252253
)
253254
pre_alloc_groups[pre_alloc_hash] = group
254255
return pre_alloc_groups

src/ethereum_test_specs/blockchain.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,18 @@ def verify_block_exception(self, transition_tool_exceptions_reliable: bool):
385385
)
386386

387387

388+
GENESIS_ENVIRONMENT_DEFAULTS: Dict[str, Any] = {
389+
"fee_recipient": 0,
390+
"number": 0,
391+
"timestamp": 0,
392+
"extra_data": b"\x00",
393+
"prev_randao": 0,
394+
}
395+
"""
396+
Default values for the genesis environment that are used to create all genesis headers.
397+
"""
398+
399+
388400
class BlockchainTest(BaseTest):
389401
"""Filler type that tests multiple blocks (valid or invalid) in a chain."""
390402

@@ -432,25 +444,31 @@ def discard_fixture_format_by_marks(
432444
return fixture_format != BlockchainEngineFixture
433445
return False
434446

435-
@staticmethod
447+
def get_genesis_environment(self, fork: Fork) -> Environment:
448+
"""Get the genesis environment for pre-allocation groups."""
449+
modified_values = self.genesis_environment.set_fork_requirements(fork).model_dump(
450+
exclude_unset=True
451+
)
452+
return Environment(**(GENESIS_ENVIRONMENT_DEFAULTS | modified_values))
453+
436454
def make_genesis(
437-
genesis_environment: Environment,
438-
pre: Alloc,
439-
fork: Fork,
455+
self, *, fork: Fork, apply_pre_allocation_blockchain: bool
440456
) -> Tuple[Alloc, FixtureBlock]:
441457
"""Create a genesis block from the blockchain test definition."""
442-
env = genesis_environment.set_fork_requirements(fork)
458+
env = self.get_genesis_environment(fork)
443459
assert env.withdrawals is None or len(env.withdrawals) == 0, (
444460
"withdrawals must be empty at genesis"
445461
)
446462
assert env.parent_beacon_block_root is None or env.parent_beacon_block_root == Hash(0), (
447463
"parent_beacon_block_root must be empty at genesis"
448464
)
449465

450-
pre_alloc = Alloc.merge(
451-
Alloc.model_validate(fork.pre_allocation_blockchain()),
452-
pre,
453-
)
466+
pre_alloc = self.pre
467+
if apply_pre_allocation_blockchain:
468+
pre_alloc = Alloc.merge(
469+
Alloc.model_validate(fork.pre_allocation_blockchain()),
470+
pre_alloc,
471+
)
454472
if empty_accounts := pre_alloc.empty_accounts():
455473
raise Exception(f"Empty accounts in pre state: {empty_accounts}")
456474
state_root = pre_alloc.state_root()
@@ -635,7 +653,7 @@ def make_fixture(
635653
"""Create a fixture from the blockchain test definition."""
636654
fixture_blocks: List[FixtureBlock | InvalidFixtureBlock] = []
637655

638-
pre, genesis = BlockchainTest.make_genesis(self.genesis_environment, self.pre, fork)
656+
pre, genesis = self.make_genesis(fork=fork, apply_pre_allocation_blockchain=True)
639657

640658
alloc = pre
641659
env = environment_from_parent_header(genesis.header)
@@ -692,7 +710,10 @@ def make_hive_fixture(
692710
"""Create a hive fixture from the blocktest definition."""
693711
fixture_payloads: List[FixtureEngineNewPayload] = []
694712

695-
pre, genesis = BlockchainTest.make_genesis(self.genesis_environment, self.pre, fork)
713+
pre, genesis = self.make_genesis(
714+
fork=fork,
715+
apply_pre_allocation_blockchain=fixture_format != BlockchainEngineXFixture,
716+
)
696717
alloc = pre
697718
env = environment_from_parent_header(genesis.header)
698719
head_hash = genesis.header.block_hash
@@ -787,10 +808,6 @@ def make_hive_fixture(
787808
)
788809
return BlockchainEngineFixture(**fixture_data)
789810

790-
def get_genesis_environment(self, fork: Fork) -> Environment:
791-
"""Get the genesis environment for pre-allocation groups."""
792-
return self.genesis_environment
793-
794811
def generate(
795812
self,
796813
t8n: TransitionTool,
@@ -799,9 +816,7 @@ def generate(
799816
) -> BaseFixture:
800817
"""Generate the BlockchainTest fixture."""
801818
t8n.reset_traces()
802-
if fixture_format == BlockchainEngineFixture:
803-
return self.make_hive_fixture(t8n, fork, fixture_format)
804-
elif fixture_format == BlockchainEngineXFixture:
819+
if fixture_format in [BlockchainEngineFixture, BlockchainEngineXFixture]:
805820
return self.make_hive_fixture(t8n, fork, fixture_format)
806821
elif fixture_format == BlockchainFixture:
807822
return self.make_fixture(t8n, fork)

src/ethereum_test_specs/state.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def make_state_test_fixture(
242242

243243
def get_genesis_environment(self, fork: Fork) -> Environment:
244244
"""Get the genesis environment for pre-allocation groups."""
245-
return self._generate_blockchain_genesis_environment(fork=fork)
245+
return self.generate_blockchain_test(fork=fork).get_genesis_environment(fork=fork)
246246

247247
def generate(
248248
self,

src/ethereum_test_specs/tests/test_fixtures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ def genesis_environment(self): # noqa: D102
470470
return Environment(
471471
gas_limit=100_000_000_000_000_000,
472472
base_fee_per_gas=1000,
473-
fee_recipient="0xba5e000000000000000000000000000000000000",
473+
fee_recipient="0x0000000000000000000000000000000000000000",
474474
)
475475

476476
@pytest.fixture
@@ -867,7 +867,7 @@ def test_fill_blockchain_invalid_txs(
867867
genesis_environment = Environment(
868868
gas_limit=100_000_000_000_000_000,
869869
base_fee_per_gas=1000,
870-
fee_recipient="0xba5e000000000000000000000000000000000000",
870+
fee_recipient="0x0000000000000000000000000000000000000000",
871871
)
872872

873873
fixture_format: FixtureFormat = (

0 commit comments

Comments
 (0)