Skip to content

Commit 65b7888

Browse files
marioevzfselmo
authored andcommitted
refactor(tests/static): Use of pydantic to resolve tags in static tests
* All pydantic simplifications * refactor(tests/static): rename sender:key -> eoa:sender * refactor: rename, use generics * fix: consider empty accounts * fix(tests): tests with empty accounts * fix(tests): addressOpcodesFiller.yml * feat: significantly improve test ids * fix: bugs in tag resolution * fix(tests): CREATE2_HighNonceDelegatecallFiller.yml * fix: types * Update src/ethereum_test_specs/static_state/account.py --- Co-authored-by: felipe <[email protected]> * fix: comment and generic tag regex * fix: Code raw code tag substitution
1 parent f0cb1bd commit 65b7888

File tree

1,788 files changed

+2810
-2690
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,788 files changed

+2810
-2690
lines changed
Lines changed: 125 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,140 @@
11
"""Account structure of ethereum/tests fillers."""
22

3-
from typing import Dict
3+
from typing import Any, Dict, Mapping
44

55
from pydantic import BaseModel
66

7-
from .common import CodeInFiller, ValueInFiller, ValueOrTagInFiller
7+
from ethereum_test_base_types import EthereumTestRootModel, HexNumber
8+
from ethereum_test_types import Alloc
89

10+
from .common import (
11+
AddressOrTagInFiller,
12+
CodeInFiller,
13+
ContractTag,
14+
SenderTag,
15+
Tag,
16+
TagDependentData,
17+
TagDict,
18+
ValueInFiller,
19+
ValueOrTagInFiller,
20+
)
921

10-
class AccountInFiller(BaseModel):
22+
23+
class StorageInPre(EthereumTestRootModel):
24+
"""Class that represents a storage in pre-state."""
25+
26+
root: Dict[ValueInFiller, ValueOrTagInFiller]
27+
28+
def tag_dependencies(self) -> Mapping[str, Tag]:
29+
"""Get tag dependencies."""
30+
tag_dependencies: Dict[str, Tag] = {}
31+
for k, v in self.root.items():
32+
if isinstance(k, Tag):
33+
tag_dependencies[k.name] = k
34+
if isinstance(v, Tag):
35+
tag_dependencies[v.name] = v
36+
return tag_dependencies
37+
38+
def resolve(self, tags: TagDict) -> Dict[ValueInFiller, ValueInFiller]:
39+
"""Resolve the storage."""
40+
resolved_storage: Dict[ValueInFiller, ValueInFiller] = {}
41+
for key, value in self.root.items():
42+
if isinstance(value, Tag):
43+
resolved_storage[key] = HexNumber(int.from_bytes(value.resolve(tags), "big"))
44+
else:
45+
resolved_storage[key] = value
46+
return resolved_storage
47+
48+
49+
class AccountInFiller(BaseModel, TagDependentData):
1150
"""Class that represents an account in filler."""
1251

13-
balance: ValueInFiller
14-
code: CodeInFiller
15-
nonce: ValueInFiller
16-
storage: Dict[ValueInFiller, ValueOrTagInFiller]
52+
balance: ValueInFiller | None = None
53+
code: CodeInFiller | None = None
54+
nonce: ValueInFiller | None = None
55+
storage: StorageInPre | None = None
1756

1857
class Config:
1958
"""Model Config."""
2059

2160
extra = "forbid"
2261
arbitrary_types_allowed = True # For CodeInFiller
62+
63+
def tag_dependencies(self) -> Mapping[str, Tag]:
64+
"""Get tag dependencies."""
65+
tag_dependencies: Dict[str, Tag] = {}
66+
if self.storage is not None:
67+
tag_dependencies.update(self.storage.tag_dependencies())
68+
if self.code is not None and isinstance(self.code, CodeInFiller):
69+
tag_dependencies.update(self.code.tag_dependencies())
70+
return tag_dependencies
71+
72+
def resolve(self, tags: TagDict) -> Dict[str, Any]:
73+
"""Resolve the account."""
74+
account_properties: Dict[str, Any] = {}
75+
if self.balance is not None:
76+
account_properties["balance"] = self.balance
77+
if self.code is not None:
78+
if compiled_code := self.code.compiled(tags):
79+
account_properties["code"] = compiled_code
80+
if self.nonce is not None:
81+
account_properties["nonce"] = self.nonce
82+
if self.storage is not None:
83+
if resolved_storage := self.storage.resolve(tags):
84+
account_properties["storage"] = resolved_storage
85+
return account_properties
86+
87+
88+
class PreInFiller(EthereumTestRootModel):
89+
"""Class that represents a pre-state in filler."""
90+
91+
root: Dict[AddressOrTagInFiller, AccountInFiller]
92+
93+
def setup(self, pre: Alloc, all_dependencies: Dict[str, Tag]) -> TagDict:
94+
"""Resolve the pre-state."""
95+
max_tries = len(self.root)
96+
97+
unresolved_accounts_in_pre = dict(self.root)
98+
resolved_accounts: TagDict = {}
99+
100+
while max_tries > 0:
101+
# Naive approach to resolve accounts
102+
unresolved_accounts_keys = list(unresolved_accounts_in_pre.keys())
103+
for address_or_tag in unresolved_accounts_keys:
104+
account = unresolved_accounts_in_pre[address_or_tag]
105+
tag_dependencies = account.tag_dependencies()
106+
# check if all tag dependencies are resolved
107+
if all(dependency in resolved_accounts for dependency in tag_dependencies):
108+
account_properties = account.resolve(resolved_accounts)
109+
if isinstance(address_or_tag, Tag):
110+
if isinstance(address_or_tag, ContractTag):
111+
resolved_accounts[address_or_tag.name] = pre.deploy_contract(
112+
**account_properties,
113+
label=address_or_tag.name,
114+
)
115+
elif isinstance(address_or_tag, SenderTag):
116+
if "balance" in account_properties:
117+
account_properties["amount"] = account_properties.pop("balance")
118+
resolved_accounts[address_or_tag.name] = pre.fund_eoa(
119+
**account_properties,
120+
label=address_or_tag.name,
121+
)
122+
else:
123+
raise ValueError(f"Unknown tag type: {address_or_tag}")
124+
else:
125+
raise ValueError(f"Unknown tag type: {address_or_tag}")
126+
del unresolved_accounts_in_pre[address_or_tag]
127+
128+
max_tries -= 1
129+
assert len(unresolved_accounts_in_pre) == 0, (
130+
f"Unresolved accounts: {unresolved_accounts_in_pre}, probably a circular dependency"
131+
)
132+
for extra_dependency in all_dependencies:
133+
if extra_dependency not in resolved_accounts:
134+
if all_dependencies[extra_dependency].type != "eoa":
135+
raise ValueError(f"Contract dependency {extra_dependency} not found in pre")
136+
# Assume the extra EOA is an empty account
137+
resolved_accounts[extra_dependency] = pre.fund_eoa(
138+
amount=0, label=extra_dependency
139+
)
140+
return resolved_accounts

src/ethereum_test_specs/static_state/common/__init__.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,37 @@
33
from .common import (
44
AccessListInFiller,
55
AddressInFiller,
6+
AddressOrCreateTagInFiller,
67
AddressOrTagInFiller,
78
AddressTag,
89
CodeInFiller,
9-
Hash32InFiller,
10-
Hash32OrTagInFiller,
10+
ContractTag,
11+
HashOrTagInFiller,
12+
SenderTag,
13+
Tag,
14+
TagDependentData,
15+
TagDict,
1116
ValueInFiller,
17+
ValueOrCreateTagInFiller,
1218
ValueOrTagInFiller,
1319
parse_address_or_tag,
1420
)
1521

1622
__all__ = [
1723
"AccessListInFiller",
1824
"AddressInFiller",
25+
"AddressOrCreateTagInFiller",
1926
"AddressOrTagInFiller",
2027
"AddressTag",
28+
"CodeInFiller",
29+
"ContractTag",
30+
"HashOrTagInFiller",
31+
"Tag",
32+
"TagDict",
33+
"TagDependentData",
34+
"SenderTag",
2135
"ValueInFiller",
36+
"ValueOrCreateTagInFiller",
2237
"ValueOrTagInFiller",
23-
"CodeInFiller",
24-
"Hash32InFiller",
25-
"Hash32OrTagInFiller",
2638
"parse_address_or_tag",
2739
]

0 commit comments

Comments
 (0)