-
Notifications
You must be signed in to change notification settings - Fork 463
Expand file tree
/
Copy pathexecution_specs.py
More file actions
241 lines (218 loc) · 8.45 KB
/
execution_specs.py
File metadata and controls
241 lines (218 loc) · 8.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
"""
Ethereum Specs EVM Transition Tool Interface.
"""
import tempfile
from pathlib import Path
from typing import Any, ClassVar, Dict, Optional
import ethereum
from ethereum_spec_tools.evm_tools.t8n import T8N, ForkCache
from ethereum_spec_tools.evm_tools.t8n.evm_trace.eip3155 import Eip3155Tracer
from ethereum_spec_tools.evm_tools.t8n.evm_trace.group import GroupTracer
from ethereum_spec_tools.evm_tools.utils import get_supported_forks
from typing_extensions import override
from execution_testing.client_clis.cli_types import (
TransitionToolOutput,
)
from execution_testing.client_clis.file_utils import (
dump_files_to_directory,
)
from execution_testing.client_clis.transition_tool import (
Profiler,
TransitionTool,
model_dump_config,
)
from execution_testing.exceptions import (
BlockException,
ExceptionBase,
ExceptionMapper,
TransactionException,
)
from execution_testing.forks import Fork
class ExecutionSpecsTransitionTool(TransitionTool):
"""Implementation of the EELS T8N for execution-spec-tests."""
supports_opcode_count: ClassVar[bool] = True
supports_blob_params: ClassVar[bool] = True
def __init__(
self,
*,
binary: Optional[Path] = None,
trace: bool = False,
):
"""Initialize the EELS Transition Tool interface."""
del binary # EELS doesn't use an external binary
self.exception_mapper = ExecutionSpecsExceptionMapper()
self.trace = trace
self._info_metadata: Optional[Dict[str, Any]] = {}
self.fork_cache = ForkCache()
@override
def shutdown(self) -> None:
self.fork_cache.__exit__()
def version(self) -> str:
"""Version of the t8n tool."""
return ethereum.__version__
def is_fork_supported(self, fork: Fork) -> bool:
"""Return True if the fork is supported by the tool."""
return fork.transition_tool_name() in get_supported_forks()
def _evaluate(
self,
*,
transition_tool_data: TransitionTool.TransitionToolData,
debug_output_path: Path | None,
slow_request: bool,
profiler: Profiler,
) -> TransitionToolOutput:
"""
Evaluate using the EELS T8N entry point in-process.
``transition_tool_data`` is handed to ``T8N`` as-is — fork,
chain_id, reward, state_test, blob_schedule all flow through
— and ``T8N.run()`` returns the ``TransitionToolOutput``
directly.
"""
del slow_request, profiler
temp_dir = tempfile.TemporaryDirectory()
tracers = None
if self.trace:
# TODO: Eip3155 traces still round-trip through tempfile
# JSON — the tracer writes one ``trace-<i>.jsonl`` per tx
# to ``output_basedir`` and ``collect_traces`` reads them
# back. Same JSON round-trip we eliminated for alloc /
# result / body; a follow-up should wire the tracer
# output through memory like the rest of the in-process
# path.
tracers = GroupTracer()
tracers.add(
Eip3155Tracer(
trace_memory=True,
trace_stack=True,
trace_return_data=True,
output_basedir=temp_dir.name,
)
)
t8n = T8N(
transition_tool_data,
cache=self.fork_cache,
tracers=tracers,
exception_mapper=self.exception_mapper,
)
output = t8n.run()
if debug_output_path:
dump_files_to_directory(
debug_output_path,
{
"input/alloc.json": transition_tool_data.alloc,
"input/env.json": transition_tool_data.env,
"input/txs.json": [
tx.model_dump(mode="json", **model_dump_config)
for tx in transition_tool_data.txs
],
},
)
dump_files_to_directory(
debug_output_path,
{
"output/alloc.json": output.alloc,
"output/result.json": output.result,
},
)
if self.trace:
self.collect_traces(
output.result.receipts, temp_dir, debug_output_path
)
temp_dir.cleanup()
return output
@classmethod
def is_installed(cls, binary_path: Optional[Path] = None) -> bool:
"""ExecutionSpecs is always installed."""
del binary_path
return True
class ExecutionSpecsExceptionMapper(ExceptionMapper):
"""
Translate between EEST exceptions and error strings returned by
ExecutionSpecs.
"""
mapping_substring: ClassVar[Dict[ExceptionBase, str]] = {
TransactionException.TYPE_4_EMPTY_AUTHORIZATION_LIST: (
"EmptyAuthorizationListError"
),
TransactionException.SENDER_NOT_EOA: "InvalidSenderError",
TransactionException.TYPE_4_TX_CONTRACT_CREATION: (
"TransactionTypeContractCreationError("
"'transaction type `SetCodeTransaction` not allowed to "
"create contracts')"
),
TransactionException.INSUFFICIENT_ACCOUNT_FUNDS: (
"InsufficientBalanceError"
),
TransactionException.TYPE_3_TX_MAX_BLOB_GAS_ALLOWANCE_EXCEEDED: (
"BlobGasLimitExceededError"
),
TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS: (
"InsufficientMaxFeePerBlobGasError"
),
TransactionException.TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH: (
"InvalidBlobVersionedHashError"
),
# This message is the same as TYPE_3_TX_MAX_BLOB_GAS_ALLOWANCE_EXCEEDED
TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED: (
"BlobCountExceededError"
),
TransactionException.TYPE_3_TX_ZERO_BLOBS: "NoBlobDataError",
TransactionException.INTRINSIC_GAS_TOO_LOW: (
"InsufficientTransactionGasError"
),
TransactionException.INTRINSIC_GAS_BELOW_FLOOR_GAS_COST: (
"InsufficientTransactionGasError"
),
TransactionException.INITCODE_SIZE_EXCEEDED: "InitCodeTooLargeError",
TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS: (
"PriorityFeeGreaterThanMaxFeeError"
),
TransactionException.NONCE_MISMATCH_TOO_HIGH: (
"NonceMismatchError('nonce too high')"
),
TransactionException.NONCE_MISMATCH_TOO_LOW: (
"NonceMismatchError('nonce too low')"
),
TransactionException.TYPE_3_TX_CONTRACT_CREATION: (
"TransactionTypeContractCreationError("
"'transaction type `BlobTransaction` not allowed to "
"create contracts')"
),
TransactionException.NONCE_IS_MAX: "NonceOverflowError",
TransactionException.GAS_ALLOWANCE_EXCEEDED: (
"GasUsedExceedsLimitError"
),
TransactionException.GAS_LIMIT_EXCEEDS_MAXIMUM: (
"TransactionGasLimitExceededError"
),
BlockException.SYSTEM_CONTRACT_EMPTY: "System contract address",
BlockException.SYSTEM_CONTRACT_CALL_FAILED: "call failed:",
BlockException.INVALID_DEPOSIT_EVENT_LAYOUT: "deposit",
BlockException.BLOCK_ACCESS_LIST_GAS_LIMIT_EXCEEDED: (
"Block access list exceeds gas limit"
),
TransactionException.LOG_MISMATCH: "LogMismatchError",
}
mapping_regex: ClassVar[Dict[ExceptionBase, str]] = {
# Temporary solution for issue #1981.
TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS: (
r"InsufficientMaxFeePerGasError|InvalidBlock"
),
TransactionException.TYPE_1_TX_PRE_FORK: (
r"module '.*transactions' has no attribute "
r"'AccessListTransaction'|"
r"transaction type 1 is not supported in .*"
),
TransactionException.TYPE_2_TX_PRE_FORK: (
r"'.*transactions' has no attribute 'FeeMarketTransaction'|"
r"transaction type 2 is not supported in .*"
),
TransactionException.TYPE_3_TX_PRE_FORK: (
r"module '.*transactions' has no attribute 'BlobTransaction'|"
r"transaction type 3 is not supported in .*"
),
TransactionException.TYPE_4_TX_PRE_FORK: (
r"'.*transactions' has no attribute 'SetCodeTransaction'|"
r"transaction type 4 is not supported in .*"
),
}