Skip to content

Commit f306d8e

Browse files
spencer-tbmarioevz
andauthored
Update existing fixture test format to export correct schema (#47)
* Add function to correctly represent post accounts. * Add function to correctly pad fields within pre/post accounts. * Remove print statement :D * Update framework tests to have correct format. * Fix added format bug. * Update test fixtures due to bug. * Function refactor to work with other fields. * Add format filters to blockHeader and genesisBlockHeader. * Update test fixtures with formatted blockHeader and genesisBlockHeader. * Filter all coinbase and all hashes from reformating in blockHeader and genesisBlockHeader. * Filter trie's and bloom from reformating in blockHeader and genesisBlockHeader. * Add chainName/transactions/uncleHeader/withdrawals. * Unrelated additions to gitignore. * Fix format for block transactions. * Review changes, whitelist addition, more formatting. * Refactor helper function, add tests for them, fix for shanghai withdrawals only. --------- Co-authored-by: Mario Vega <[email protected]>
1 parent f8c329a commit f306d8e

File tree

12 files changed

+833
-378
lines changed

12 files changed

+833
-378
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,8 @@ pip-delete-this-directory.txt
4848
.coverage
4949

5050
# misc
51-
51+
.vscode
5252
*.code-workspace
53+
54+
# docs
55+
_readthedocs

src/ethereum_test_tools/common/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
Storage,
2828
Transaction,
2929
Withdrawal,
30+
alloc_to_accounts,
31+
even_padding,
32+
key_value_padding,
33+
storage_padding,
3034
str_or_none,
3135
to_json,
3236
to_json_or_none,
@@ -48,10 +52,14 @@
4852
"TestPrivateKey",
4953
"Transaction",
5054
"Withdrawal",
55+
"alloc_to_accounts",
56+
"even_padding",
5157
"ceiling_division",
5258
"compute_create2_address",
5359
"compute_create_address",
5460
"eip_2028_transaction_data_cost",
61+
"key_value_padding",
62+
"storage_padding",
5563
"str_or_none",
5664
"to_address",
5765
"to_hash",

src/ethereum_test_tools/common/types.py

Lines changed: 112 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,15 @@ def must_be_equal(self, other: "Storage"):
260260
raise Storage.KeyValueMismatch(key, 0, other.data[key])
261261

262262

263+
def storage_padding(storage: Dict) -> Dict:
264+
"""
265+
Adds even padding to each storage element.
266+
"""
267+
return {
268+
key_value_padding(k): key_value_padding(v) for k, v in storage.items()
269+
}
270+
271+
263272
@dataclass(kw_only=True)
264273
class Account:
265274
"""
@@ -425,6 +434,22 @@ def with_code(cls: Type, code: bytes | str | Code) -> "Account":
425434
return Account(nonce=1, code=code)
426435

427436

437+
def alloc_to_accounts(got_alloc: Dict[str, Any]) -> Mapping[str, Account]:
438+
"""
439+
Converts the post state alloc returned from t8n to a mapping of accounts.
440+
"""
441+
accounts = {}
442+
for address, value in got_alloc.items():
443+
account = Account(
444+
nonce=int_or_none(value.get("nonce", None)),
445+
balance=int_or_none(value.get("balance", None)),
446+
code=value.get("code", None),
447+
storage=value.get("storage", None),
448+
)
449+
accounts[address] = account
450+
return accounts
451+
452+
428453
ACCOUNT_DEFAULTS = Account(nonce=0, balance=0, code=bytes(), storage={})
429454

430455

@@ -832,7 +857,9 @@ class FixtureBlock:
832857
block_header: Optional[FixtureHeader] = None
833858
expected_exception: Optional[str] = None
834859
block_number: Optional[int] = None
835-
chain_name: Optional[str] = None
860+
txs: Optional[List[Transaction]] = None
861+
ommers: Optional[List[Header]] = None
862+
withdrawals: Optional[List[Withdrawal]] = None
836863

837864

838865
@dataclass(kw_only=True)
@@ -879,9 +906,9 @@ def default(self, obj):
879906
obj.balance, hex(ACCOUNT_DEFAULTS.balance)
880907
),
881908
"code": code_or_none(obj.code, "0x"),
882-
"storage": to_json_or_none(obj.storage, {}),
909+
"storage": storage_padding(to_json_or_none(obj.storage, {})),
883910
}
884-
return account
911+
return even_padding(account, excluded=["storage"])
885912
elif isinstance(obj, Transaction):
886913
tx = {
887914
"type": hex(obj.ty),
@@ -912,12 +939,13 @@ def default(self, obj):
912939

913940
return {k: v for (k, v) in tx.items() if v is not None}
914941
elif isinstance(obj, Withdrawal):
915-
return {
942+
withdrawal = {
916943
"index": hex(obj.index),
917944
"validatorIndex": hex(obj.validator),
918945
"address": obj.address,
919946
"amount": hex(obj.amount),
920947
}
948+
return withdrawal
921949
elif isinstance(obj, Environment):
922950
env = {
923951
"currentCoinbase": obj.coinbase,
@@ -967,11 +995,52 @@ def default(self, obj):
967995
header["hash"] = obj.hash
968996
if obj.withdrawals_root is not None:
969997
header["withdrawalsRoot"] = obj.withdrawals_root
970-
return header
998+
return even_padding(
999+
header,
1000+
excluded=[
1001+
"parentHash",
1002+
"uncleHash",
1003+
"coinbase",
1004+
"transactionsTrie",
1005+
"receiptTrie",
1006+
"bloom",
1007+
"nonce",
1008+
"mixHash",
1009+
"hash",
1010+
"withdrawalsRoot",
1011+
],
1012+
)
9711013
elif isinstance(obj, FixtureBlock):
972-
b = {
973-
"rlp": obj.rlp,
974-
}
1014+
# Format Fixture Block Txs
1015+
b_txs = [
1016+
even_padding(
1017+
to_json(
1018+
{
1019+
"nonce": hex(tx.nonce),
1020+
"to": tx.to if tx.to is not None else "",
1021+
"value": hex(tx.value),
1022+
"data": code_to_hex(tx.data),
1023+
"gasLimit": hex_or_none(tx.gas_limit),
1024+
"gasPrice": hex(tx.gas_price)
1025+
if tx.gas_price is not None
1026+
else "0x0A",
1027+
"secretKey": tx.secret_key,
1028+
}
1029+
),
1030+
excluded=["to"],
1031+
)
1032+
for tx in obj.txs or []
1033+
]
1034+
1035+
# Format Fixture Block Withdrawals
1036+
b_wds = []
1037+
if obj.withdrawals:
1038+
b_wds = [
1039+
even_padding(to_json(wd), excluded=["address"])
1040+
for wd in obj.withdrawals
1041+
]
1042+
1043+
b = {"rlp": obj.rlp}
9751044
if obj.block_header is not None:
9761045
b["blockHeader"] = json.loads(
9771046
json.dumps(obj.block_header, cls=JSONEncoder)
@@ -980,8 +1049,12 @@ def default(self, obj):
9801049
b["expectException"] = obj.expected_exception
9811050
if obj.block_number is not None:
9821051
b["blocknumber"] = str(obj.block_number)
983-
if obj.chain_name is not None:
984-
b["chainname"] = obj.chain_name
1052+
if obj.txs is not None:
1053+
b["transactions"] = b_txs
1054+
if obj.ommers is not None:
1055+
b["uncleHeaders"] = obj.ommers
1056+
if obj.withdrawals is not None:
1057+
b["withdrawals"] = b_wds
9851058
return b
9861059
elif isinstance(obj, Fixture):
9871060
f = {
@@ -1005,3 +1078,32 @@ def default(self, obj):
10051078
return f
10061079
else:
10071080
return super().default(obj)
1081+
1082+
1083+
def even_padding(input: Dict, excluded: List[Any | None]) -> Dict:
1084+
"""
1085+
Adds even padding to each field in the input (nested) dictionary.
1086+
"""
1087+
for key, value in input.items():
1088+
if key not in excluded:
1089+
if isinstance(value, dict):
1090+
even_padding(value, excluded)
1091+
elif value != "0x" and value is not None:
1092+
input[key] = key_value_padding(value)
1093+
else:
1094+
input[key] = "0x"
1095+
return input
1096+
1097+
1098+
def key_value_padding(value: str) -> str:
1099+
"""
1100+
Adds even padding to a dictionary key or value string.
1101+
"""
1102+
if value is not None:
1103+
new_value = value.lstrip("0x").lstrip("0")
1104+
new_value = "00" if new_value == "" else new_value
1105+
if len(new_value) % 2 == 1:
1106+
new_value = "0" + new_value
1107+
return "0x" + new_value
1108+
else:
1109+
return "0x"

src/ethereum_test_tools/filling/fill.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from evm_block_builder import BlockBuilder
88
from evm_transition_tool import TransitionTool, map_fork
99

10-
from ..common import Fixture
10+
from ..common import Fixture, alloc_to_accounts
1111
from ..spec import TestSpec
1212
from ..vm.fork import get_reward
1313

@@ -63,7 +63,7 @@ def fill_test(
6363
if eips is not None
6464
else fork,
6565
pre_state=copy(test.pre),
66-
post_state=alloc,
66+
post_state=alloc_to_accounts(alloc),
6767
seal_engine=engine,
6868
name=test.name,
6969
index=index,

src/ethereum_test_tools/spec/blockchain_test.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ def make_block(
194194
rlp=rlp,
195195
block_header=header,
196196
block_number=header.number,
197+
txs=block.txs if block.txs is not None else [],
198+
ommers=[],
199+
withdrawals=env.withdrawals,
197200
),
198201
env.apply_new_parent(header),
199202
next_alloc,

src/ethereum_test_tools/spec/state_test.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ def make_blocks(
105105
Raises exception on invalid test behavior.
106106
"""
107107
env = self.env.apply_new_parent(genesis)
108-
109108
env = set_fork_requirements(env, fork)
110109

111110
(alloc, result, txs_rlp) = t8n.evaluate(
@@ -152,18 +151,23 @@ def make_blocks(
152151
"baseFeePerGas": result.get("currentBaseFee"),
153152
}
154153
)
154+
155155
block, head = b11r.build(
156156
header=header.to_geth_dict(),
157157
txs=txs_rlp,
158158
ommers=[],
159159
withdrawals=to_json_or_none(env.withdrawals),
160160
)
161161
header.hash = head
162+
162163
return (
163164
[
164165
FixtureBlock(
165166
rlp=block,
166167
block_header=header,
168+
txs=self.txs if self.txs is not None else [],
169+
ommers=[],
170+
withdrawals=env.withdrawals,
167171
)
168172
],
169173
head,

0 commit comments

Comments
 (0)