Skip to content

Commit 550125c

Browse files
committed
address issues, refactor the code (WIP)
1 parent c7f235f commit 550125c

File tree

1 file changed

+161
-82
lines changed

1 file changed

+161
-82
lines changed

src/entry_points/gentest.py

Lines changed: 161 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
import os
77
import sys
88
from dataclasses import asdict, dataclass
9-
from typing import Dict, List, Union
9+
from typing import Dict, List
1010

1111
import requests
1212

13-
from ethereum_test_tools import Account, Address, Transaction
13+
from ethereum_test_tools import Account, Address, Transaction, common
1414

1515

1616
def main(): # noqa: D103
@@ -45,74 +45,131 @@ def make_test(args): # noqa: D103
4545
print("Perform debug_trace_call")
4646
state = request.debug_trace_call(tr)
4747

48+
print("Perform eth_get_block_by_number")
49+
block = request.eth_get_block_by_number(tr.block_number)
50+
4851
print("Generate py test >> " + output_file)
49-
test = make_test_template(tr, state)
52+
constructor = TestConstructor(PYTEST_TEMPLATE)
53+
test = constructor.make_test_template(block, tr, state)
5054
with open(output_file, "w") as file:
5155
file.write(test)
5256

53-
print(test)
57+
print("Finished")
5458

5559

56-
def make_test_template(
57-
tr: "RequestManager.RemoteTransaction", state: Dict[Address, Account]
58-
) -> str:
60+
class TestConstructor:
5961
"""
60-
Prepare the .py file template
62+
Construct .py file from test template, by replacing keywords with test data
6163
"""
62-
test = PYTEST_TEMPLATE
63-
test = test.replace(
64-
"$HEADLINE_COMMENT",
65-
"gentest autogenerated test with debug_traceCall of tx.hash " + tr.tr_hash,
66-
)
67-
test = test.replace("$TEST_NAME", "test_transaction_" + tr.tr_hash[2:])
68-
test = test.replace(
69-
"$TEST_COMMENT", "gentest autogenerated test for tx.hash " + tr.tr_hash[2:]
70-
)
7164

72-
# Print a nice .py storage pre
73-
pad = " "
74-
state_str = ""
75-
for address, account in state.items():
76-
if isinstance(account, dict):
77-
account_obj = Account(**account)
78-
state_str += ' "' + str(address) + '": Account(\n'
79-
state_str += pad + "balance=" + str(account_obj.balance) + ",\n"
80-
if address == tr.transaction.sender:
81-
state_str += pad + "nonce=" + str(tr.transaction.nonce) + ",\n"
82-
else:
83-
state_str += pad + "nonce=" + str(account_obj.nonce) + ",\n"
65+
test_template: str
8466

85-
if account_obj.code is None:
86-
state_str += pad + 'code="0x",\n'
87-
else:
88-
state_str += pad + 'code="' + str(account_obj.code) + '",\n'
89-
state_str += pad + "storage={\n"
90-
91-
if account_obj.storage is not None:
92-
for record, value in account_obj.storage.items():
93-
state_str += pad + ' "' + str(record) + '" : "' + str(value) + '",\n'
94-
95-
state_str += pad + "}\n"
96-
state_str += " ),\n"
97-
test = test.replace("$PRE", state_str)
98-
99-
# Print legacy transaction in .py
100-
tr_str = ""
101-
tr_str += pad + "ty=" + str(tr.transaction.ty) + ",\n"
102-
tr_str += pad + "chain_id=" + str(tr.transaction.chain_id) + ",\n"
103-
tr_str += pad + "nonce=" + str(tr.transaction.nonce) + ",\n"
104-
tr_str += pad + 'to="' + str(tr.transaction.to) + '",\n'
105-
tr_str += pad + "gas_price=" + str(tr.transaction.gas_price) + ",\n"
106-
tr_str += pad + "protected=False,\n"
107-
tr_str += pad + 'data="' + str(tr.transaction.data) + '",\n'
108-
tr_str += pad + "gas_limit=" + str(tr.transaction.gas_limit) + ",\n"
109-
tr_str += pad + "value=" + str(tr.transaction.value) + ",\n"
110-
tr_str += pad + "v=" + str(tr.transaction.v) + ",\n"
111-
tr_str += pad + "r=" + str(tr.transaction.r) + ",\n"
112-
tr_str += pad + "s=" + str(tr.transaction.s) + ",\n"
113-
114-
test = test.replace("$TR", tr_str)
115-
return test
67+
def __init__(self, test_template: str):
68+
"""
69+
Initialize with template
70+
"""
71+
self.test_template = test_template
72+
73+
def _make_test_comments(self, test: str, tr_hash: str) -> str:
74+
test = test.replace(
75+
"$HEADLINE_COMMENT",
76+
"gentest autogenerated test with debug_traceCall of tx.hash " + tr_hash,
77+
)
78+
test = test.replace("$TEST_NAME", "test_transaction_" + tr_hash[2:])
79+
test = test.replace(
80+
"$TEST_COMMENT", "gentest autogenerated test for tx.hash " + tr_hash[2:]
81+
)
82+
return test
83+
84+
def _make_test_environment(self, test: str, bl: "RequestManager.RemoteBlock") -> str:
85+
env_str = ""
86+
pad = " "
87+
for field, value in asdict(bl).items():
88+
env_str += (
89+
f'{pad}{field}="{value}",\n' if field == "coinbase" else f"{pad}{field}={value},\n"
90+
)
91+
test = test.replace("$ENV", env_str)
92+
return test
93+
94+
def _make_pre_state(
95+
self, test: str, tr: "RequestManager.RemoteTransaction", state: Dict[Address, Account]
96+
) -> str:
97+
# Print a nice .py storage pre
98+
pad = " "
99+
state_str = ""
100+
for address, account in state.items():
101+
if isinstance(account, dict):
102+
account_obj = Account(**account)
103+
state_str += f' "{address}": Account(\n'
104+
state_str += f"{pad}balance={account_obj.balance},\n"
105+
if address == tr.transaction.sender:
106+
state_str += f"{pad}nonce={tr.transaction.nonce},\n"
107+
else:
108+
state_str += f"{pad}nonce={account_obj.nonce},\n"
109+
110+
if account_obj.code is None:
111+
state_str += f'{pad}code="0x",\n'
112+
else:
113+
state_str += f'{pad}code="{account_obj.code}",\n'
114+
state_str += pad + "storage={\n"
115+
116+
if account_obj.storage is not None:
117+
for record, value in account_obj.storage.items():
118+
pad_record = common.ZeroPaddedHexNumber(record)
119+
pad_value = common.ZeroPaddedHexNumber(value)
120+
state_str += f'{pad} "{pad_record}" : "{pad_value}",\n'
121+
122+
state_str += pad + "}\n"
123+
state_str += " ),\n"
124+
return test.replace("$PRE", state_str)
125+
126+
def _make_transaction(self, test: str, tr: "RequestManager.RemoteTransaction") -> str:
127+
"""
128+
Print legacy transaction in .py
129+
"""
130+
pad = " "
131+
tr_str = ""
132+
quoted_fields_array = ["data", "to"]
133+
hex_fields_array = ["v", "r", "s"]
134+
legacy_fields_array = [
135+
"ty",
136+
"chain_id",
137+
"nonce",
138+
"gas_price",
139+
"protected",
140+
"gas_limit",
141+
"value",
142+
]
143+
for field, value in asdict(tr.transaction).items():
144+
if value is None:
145+
continue
146+
147+
if field in legacy_fields_array:
148+
tr_str += f"{pad}{field}={value},\n"
149+
150+
if field in quoted_fields_array:
151+
tr_str += f'{pad}{field}="{value}",\n'
152+
153+
if field in hex_fields_array:
154+
tr_str += f"{pad}{field}={hex(value)},\n"
155+
156+
return test.replace("$TR", tr_str)
157+
158+
def make_test_template(
159+
self,
160+
bl: "RequestManager.RemoteBlock",
161+
tr: "RequestManager.RemoteTransaction",
162+
state: Dict[Address, Account],
163+
) -> str:
164+
"""
165+
Prepare the .py file template
166+
"""
167+
test = self.test_template
168+
test = self._make_test_comments(test, tr.tr_hash)
169+
test = self._make_test_environment(test, bl)
170+
test = self._make_pre_state(test, tr, state)
171+
test = self._make_transaction(test, tr)
172+
return test
116173

117174

118175
class PyspecConfig:
@@ -139,15 +196,7 @@ def __init__(self, config_path: str):
139196
"""
140197
with open(config_path, "r") as file:
141198
data = json.load(file)
142-
self.remote_nodes = [self._json_to_remote_node(node) for node in data["remote_nodes"]]
143-
144-
def _json_to_remote_node(self, d):
145-
return PyspecConfig.RemoteNode(
146-
name=d["name"],
147-
node_url=d["node_url"],
148-
client_id=d["client_id"],
149-
secret=d["secret"],
150-
)
199+
self.remote_nodes = [PyspecConfig.RemoteNode(**node) for node in data["remote_nodes"]]
151200

152201

153202
class RequestManager:
@@ -165,6 +214,18 @@ class RemoteTransaction:
165214
tr_hash: str
166215
transaction: Transaction
167216

217+
@dataclass
218+
class RemoteBlock:
219+
"""
220+
Remote block header information structure
221+
"""
222+
223+
coinbase: str
224+
difficulty: str
225+
gas_limit: str
226+
number: str
227+
timestamp: str
228+
168229
node_url: str
169230
headers: dict[str, str]
170231

@@ -179,6 +240,19 @@ def __init__(self, node_config: PyspecConfig.RemoteNode):
179240
"Content-Type": "application/json",
180241
}
181242

243+
def _make_request(self, data) -> requests.Response:
244+
error_str = "An error occurred while making remote request: "
245+
try:
246+
response = requests.post(self.node_url, headers=self.headers, data=json.dumps(data))
247+
if response.status_code >= 200 and response.status_code < 300:
248+
return response
249+
else:
250+
print(error_str + response.text)
251+
raise requests.exceptions.HTTPError
252+
except requests.exceptions.RequestException as e:
253+
print(error_str, e)
254+
raise e
255+
182256
def eth_get_transaction_by_hash(self, transaction_hash: str) -> RemoteTransaction:
183257
"""
184258
Get transaction data.
@@ -189,7 +263,8 @@ def eth_get_transaction_by_hash(self, transaction_hash: str) -> RemoteTransactio
189263
"params": [f"{transaction_hash}"],
190264
"id": 1,
191265
}
192-
response = requests.post(self.node_url, headers=self.headers, data=json.dumps(data))
266+
267+
response = self._make_request(data)
193268
res = response.json().get("result", None)
194269

195270
return RequestManager.RemoteTransaction(
@@ -207,10 +282,11 @@ def eth_get_transaction_by_hash(self, transaction_hash: str) -> RemoteTransactio
207282
v=int(res["v"], 16),
208283
r=int(res["r"], 16),
209284
s=int(res["s"], 16),
285+
protected=True if int(res["v"], 16) > 30 else False,
210286
),
211287
)
212288

213-
def eth_get_block_by_number(self, block_number: str):
289+
def eth_get_block_by_number(self, block_number: str) -> RemoteBlock:
214290
"""
215291
Get block by number
216292
"""
@@ -220,8 +296,16 @@ def eth_get_block_by_number(self, block_number: str):
220296
"params": [f"{block_number}", False],
221297
"id": 1,
222298
}
223-
response = requests.post(self.node_url, headers=self.headers, data=json.dumps(data))
224-
return response.json().get("result", None)
299+
response = self._make_request(data)
300+
res = response.json().get("result", None)
301+
302+
return RequestManager.RemoteBlock(
303+
coinbase=res["miner"],
304+
number=res["number"],
305+
difficulty=res["difficulty"],
306+
gas_limit=res["gasLimit"],
307+
timestamp=res["timestamp"],
308+
)
225309

226310
def debug_trace_call(self, tr: RemoteTransaction) -> Dict[Address, Account]:
227311
"""
@@ -242,7 +326,7 @@ def debug_trace_call(self, tr: RemoteTransaction) -> Dict[Address, Account]:
242326
"id": 1,
243327
}
244328

245-
response = requests.post(self.node_url, headers=self.headers, data=json.dumps(data))
329+
response = self._make_request(data)
246330
return response.json().get("result", None)
247331

248332

@@ -262,19 +346,14 @@ def debug_trace_call(self, tr: RemoteTransaction) -> Dict[Address, Account]:
262346
Transaction,
263347
)
264348
265-
REFERENCE_SPEC_GIT_PATH = "EIPS/eip-6780.md"
266-
REFERENCE_SPEC_VERSION = "2f8299df31bb8173618901a03a8366a3183479b0"
349+
REFERENCE_SPEC_GIT_PATH = "N/A"
350+
REFERENCE_SPEC_VERSION = "N/A"
267351
268352
269353
@pytest.fixture
270354
def env(): # noqa: D103
271355
return Environment(
272-
coinbase="0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
273-
difficulty=3402669764409613,
274-
#gas_limit=12469470,
275-
gas_limit=0x016345785d8a0000,
276-
number=11114732,
277-
timestamp="0x5f933d46",
356+
$ENV
278357
)
279358
280359

0 commit comments

Comments
 (0)