Skip to content

Commit 4ec6097

Browse files
committed
feat(tests/static): Use unique shared coinbase based on pre and post hashes
1 parent 45f0a2e commit 4ec6097

File tree

4 files changed

+128
-15
lines changed

4 files changed

+128
-15
lines changed

scripts/convert_addresses.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ class Context(Enum):
138138
# stExample
139139
"add11_ymlFiller.yml",
140140
"add11Filler.json",
141-
141+
142142
# stExtCodeHash
143143
"extcodehashEmpty_ParisFiller.yml",
144144
"extCodeHashSelfInInitFiller.json",
@@ -165,7 +165,7 @@ class Context(Enum):
165165
"extCodeHashCreatedAndDeletedAccountCallFiller.json",
166166
"createEmptyThenExtcodehashFiller.json",
167167
"contractCreationOOGdontLeaveEmptyContractViaTransactionFiller.json",
168-
168+
169169
# Really only `ReturnTestFiller` and `ReturnTest2Filler` are compatible inside `stInitCodeTest`
170170
"CallContractToCreateContractAndCallItOOGFiller.json",
171171
"CallContractToCreateContractOOGBonusGasFiller.json",
@@ -272,8 +272,6 @@ class Context(Enum):
272272
"RevertOpcodeInInitFiller.json",
273273
"RevertOpcodeWithBigOutputInInitFiller.json",
274274
"ByZeroFiller.json",
275-
"RecursiveCreateContractsCreate4ContractsFiller.json",
276-
"RecursiveCreateContractsFiller.json",
277275
"TestCryptographicFunctionsFiller.json",
278276
"StackDepthLimitSECFiller.json",
279277
"eoaEmptyParisFiller.yml",
@@ -390,14 +388,14 @@ class Context(Enum):
390388
"suicideCallerAddresTooBigLeftFiller.json",
391389
"ABAcallsSuicide1Filler.json",
392390

393-
391+
394392
"/stCreate2/",
395393
"/stCreateTest/",
396394
"/stRecursiveCreate/",
397395
"/stWalletTest/",
398396
"/stZeroKnowledge/",
399397
"/stZeroKnowledge2/",
400-
398+
401399
# TODO: See if these can be turned on with fine tuning
402400
"/stTimeConsuming/",
403401
}

src/ethereum_test_specs/static_state/account.py

Lines changed: 104 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
"""Account structure of ethereum/tests fillers."""
22

3-
from typing import Any, Dict, List, Mapping, Set, Tuple
3+
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Set, Tuple
44

55
from pydantic import BaseModel
66

7-
from ethereum_test_base_types import Bytes, EthereumTestRootModel, HexNumber, Storage
7+
if TYPE_CHECKING:
8+
from .environment import EnvironmentInStateTestFiller
9+
from .expect_section import ResultInFiller
10+
11+
from ethereum_test_base_types import (
12+
Account,
13+
Address,
14+
Bytes,
15+
EthereumTestRootModel,
16+
HexNumber,
17+
Storage,
18+
)
819
from ethereum_test_types import Alloc
20+
from ethereum_test_types.block_types import EnvironmentGeneric
921

1022
from .common import (
1123
AddressOrTagInFiller,
@@ -84,6 +96,17 @@ def resolve(self, tags: TagDict) -> Dict[str, Any]:
8496
account_properties["storage"] = resolved_storage
8597
return account_properties
8698

99+
def __hash__(self) -> int:
100+
"""Generate deterministic hash for the account."""
101+
# Create a tuple of hashable representations
102+
hash_tuple = (
103+
self.balance if self.balance is not None else None,
104+
str(self.code) if self.code is not None else None,
105+
self.nonce if self.nonce is not None else None,
106+
str(self.storage) if self.storage is not None else None,
107+
)
108+
return hash(hash_tuple)
109+
87110

88111
class PreInFiller(EthereumTestRootModel):
89112
"""Class that represents a pre-state in filler."""
@@ -142,7 +165,14 @@ def _topological_sort(self, dep_graph: Dict[str, Set[str]]) -> List[str]:
142165

143166
return sorted_nodes
144167

145-
def setup(self, pre: Alloc, all_dependencies: Dict[str, Tag]) -> TagDict:
168+
def setup(
169+
self,
170+
pre: Alloc,
171+
all_dependencies: Dict[str, Tag],
172+
env: "EnvironmentInStateTestFiller | None" = None,
173+
expect_result: "ResultInFiller | None" = None,
174+
request: Any = None,
175+
) -> TagDict:
146176
"""Resolve the pre-state with improved tag resolution."""
147177
resolved_accounts: TagDict = {}
148178

@@ -174,12 +204,78 @@ def setup(self, pre: Alloc, all_dependencies: Dict[str, Tag]) -> TagDict:
174204
if tag_name in tag_to_address:
175205
tag = tag_to_address[tag_name]
176206
if isinstance(tag, ContractTag):
177-
# Deploy with placeholder to get address
178-
deployed_address = pre.deploy_contract(
179-
code=b"", # Temporary placeholder
180-
label=tag_name,
207+
# Check if this is a coinbase tag during pre-alloc generation
208+
is_coinbase = (
209+
env is not None
210+
and isinstance(env.current_coinbase, Tag)
211+
and env.current_coinbase.name == tag_name
181212
)
182-
resolved_accounts[tag_name] = deployed_address
213+
214+
if is_coinbase and expect_result is not None and request is not None:
215+
# Check if we're in pre-alloc generation or usage mode
216+
generate_groups = request.config.getoption(
217+
"generate_pre_alloc_groups", False
218+
)
219+
use_groups = request.config.getoption("use_pre_alloc_groups", False)
220+
if generate_groups or use_groups:
221+
# Get pre-state account
222+
coinbase_account = tagged_accounts.get(tag)
223+
224+
# Check if account has any pre-state
225+
has_pre_state = coinbase_account is not None and (
226+
coinbase_account.code is not None
227+
or coinbase_account.storage is not None
228+
or coinbase_account.balance is not None
229+
or coinbase_account.nonce is not None
230+
)
231+
232+
# Find post-state expectations for this coinbase
233+
coinbase_post_account = None
234+
has_post_state = False
235+
for addr_or_tag, result_account in expect_result.root.items():
236+
if isinstance(addr_or_tag, Tag) and addr_or_tag.name == tag_name:
237+
if result_account is not None:
238+
has_post_state = True
239+
coinbase_post_account = result_account
240+
break
241+
242+
# If no state changes, use the default fee_recipient
243+
if not has_pre_state and not has_post_state:
244+
deployed_address = (
245+
EnvironmentGeneric.model_fields["fee_recipient"].default
246+
)
247+
resolved_accounts[tag_name] = deployed_address
248+
else:
249+
# Use hash to create deterministic address
250+
# Combine pre and post hashes
251+
pre_hash = hash(coinbase_account) if coinbase_account else 0
252+
post_hash = (
253+
hash(coinbase_post_account) if coinbase_post_account else 0
254+
)
255+
combined_hash = hash((pre_hash, post_hash))
256+
257+
hash_bytes = abs(combined_hash).to_bytes(32, byteorder="big")
258+
# Start with 0xc01b (coinbase) prefix
259+
address_bytes = b"\xc0\x1b" + hash_bytes[:18]
260+
deployed_address = Address(address_bytes)
261+
resolved_accounts[tag_name] = deployed_address
262+
# Manually add to pre-alloc if it doesn't exist
263+
if deployed_address not in pre:
264+
pre[deployed_address] = Account()
265+
else:
266+
# Not in pre-alloc generation, normal deployment
267+
deployed_address = pre.deploy_contract(
268+
code=b"", # Temporary placeholder
269+
label=tag_name,
270+
)
271+
resolved_accounts[tag_name] = deployed_address
272+
else:
273+
# Not a coinbase tag, normal deployment
274+
deployed_address = pre.deploy_contract(
275+
code=b"", # Temporary placeholder
276+
label=tag_name,
277+
)
278+
resolved_accounts[tag_name] = deployed_address
183279
elif isinstance(tag, SenderTag):
184280
# Create EOA to get address - use amount=1 to ensure account is created
185281
eoa = pre.fund_eoa(amount=1, label=tag_name)

src/ethereum_test_specs/static_state/expect_section.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ def resolve(self, tags: TagDict) -> Account:
126126
account_kwargs["nonce"] = self.nonce
127127
return Account(**account_kwargs)
128128

129+
def __hash__(self) -> int:
130+
"""Generate deterministic hash for the account in expect section."""
131+
# Create a tuple of hashable representations
132+
hash_tuple = (
133+
self.balance if self.balance is not None else None,
134+
str(self.code) if self.code is not None else None,
135+
self.nonce if self.nonce is not None else None,
136+
str(self.storage) if self.storage is not None else None,
137+
)
138+
return hash(hash_tuple)
139+
129140

130141
class CMP(Enum):
131142
"""Comparison action."""

src/ethereum_test_specs/static_state/state_static.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def test_state_vectors(
130130
state_test: StateTestFiller,
131131
pre: Alloc,
132132
fork: Fork,
133+
request: pytest.FixtureRequest,
133134
d: int,
134135
g: int,
135136
v: int,
@@ -140,7 +141,14 @@ def test_state_vectors(
140141
tx_tag_dependencies = self.transaction.tag_dependencies()
141142
result_tag_dependencies = expect.result.tag_dependencies()
142143
all_dependencies = {**tx_tag_dependencies, **result_tag_dependencies}
143-
tags = self.pre.setup(pre, all_dependencies)
144+
# Pass context for coinbase resolution during pre-alloc generation
145+
tags = self.pre.setup(
146+
pre,
147+
all_dependencies,
148+
env=self.env,
149+
expect_result=expect.result,
150+
request=request,
151+
)
144152
env = self.env.get_environment(tags)
145153
exception = (
146154
None

0 commit comments

Comments
 (0)