Skip to content

Commit ab46fff

Browse files
marioevzspencer-tb
andauthored
feat(fw): Add with_all_contract_creating_tx_types marker (#602)
* fix(evm_transition_tool): Allow formatting to="" for some tool types * fix(fw,types): Make Transaction more permissible to always create a valid tx * feat(forks): Add `contract_creating_tx_types` class method to forks * feat(plugins): create "with_all_contract_creating_tx_types" marker * docs: Add test markers page * fix(fw): remove auto-fill blob_versioned_hashes * chengelog * Update src/evm_transition_tool/evmone.py Co-authored-by: spencer <[email protected]> * Update src/evm_transition_tool/transition_tool.py Co-authored-by: spencer <[email protected]> * Update src/evm_transition_tool/transition_tool.py Co-authored-by: spencer <[email protected]> * docs: lint --------- Co-authored-by: spencer <[email protected]>
1 parent 87ef9be commit ab46fff

File tree

9 files changed

+196
-18
lines changed

9 files changed

+196
-18
lines changed

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Test fixtures for use by clients are available for each release on the [Github r
3737
- ✨ Add more Transaction and Block exceptions from existing ethereum/tests repo ([#572](https://github.com/ethereum/execution-spec-tests/pull/572)).
3838
- ✨ Add "description" and "url" fields containing test case documentation and a source code permalink to fixtures during `fill` and use them in `consume`-generated Hive test reports ([#579](https://github.com/ethereum/execution-spec-tests/pull/579)).
3939
- ✨ Add git workflow evmone coverage script for any new lines mentioned in converted_ethereum_tests.txt ([#503](https://github.com/ethereum/execution-spec-tests/pull/503)).
40+
- ✨ Add a new covariant marker `with_all_contract_creating_tx_types` that allows automatic parametrization of a test with all contract-creating transaction types at the current executing fork ([#602](https://github.com/ethereum/execution-spec-tests/pull/602)).
4041

4142
### 🔧 EVM Tools
4243

docs/navigation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* [Types of Test](writing_tests/types_of_tests.md)
1414
* [Adding a New Test](writing_tests/adding_a_new_test.md)
1515
* [Writing a New Test](writing_tests/writing_a_new_test.md)
16+
* [Test Markers](writing_tests/test_markers.md)
1617
* [Referencing an EIP Spec Version](writing_tests/reference_specification.md)
1718
* [Verifying Changes Locally](writing_tests/verifying_changes.md)
1819
* [Exception Tests](writing_tests/exception_tests.md)

docs/writing_tests/test_markers.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Test Markers
2+
3+
Test markers are used to categorize tests and to run specific subsets of tests. They are defined in the test files using the `pytest.mark` decorator.
4+
5+
## Fork Markers
6+
7+
These markers are used to specify the forks for which a test is valid.
8+
9+
### pytest.mark.valid_from("FORK_NAME")
10+
11+
This marker is used to specify the fork from which the test is valid. The test will not be filled for forks before the specified fork.
12+
13+
```python
14+
import pytest
15+
16+
@pytest.mark.valid_from("London")
17+
def test_something_only_valid_after_london():
18+
pass
19+
```
20+
21+
In this example, the test will only be filled for the London fork and after, e.g. London, Paris, Shanghai, Cancun, etc.
22+
23+
### pytest.mark.valid_until("FORK_NAME")
24+
25+
This marker is used to specify the fork until which the test is valid. The test will not be filled for forks after the specified fork.
26+
27+
```python
28+
import pytest
29+
30+
@pytest.mark.valid_until("London")
31+
def test_something_only_valid_until_london():
32+
pass
33+
```
34+
35+
In this example, the test will only be filled for the London fork and before, e.g. London, Berlin, Istanbul, etc.
36+
37+
### pytest.mark.valid_at_transition_to("FORK_NAME")
38+
39+
This marker is used to specify that a test is only meant to be filled at the transition to the specified fork.
40+
41+
The test usually starts at the fork prior to the specified fork at genesis and at block 5 (for pre-merge forks) or at timestamp 15,000 (for post-merge forks) the fork transition occurs.
42+
43+
## Fork Covariant Markers
44+
45+
These markers are used in conjunction with the fork markers to automatically parameterize tests with values that are valid for the fork being tested.
46+
47+
### pytest.mark.with_all_tx_types
48+
49+
This marker is used to automatically parameterize a test with all transaction types that are valid for the fork being tested.
50+
51+
```python
52+
import pytest
53+
54+
@pytest.mark.with_all_tx_types
55+
@pytest.mark.valid_from("Berlin")
56+
def test_something_with_all_tx_types(tx_type):
57+
pass
58+
```
59+
60+
In this example, the test will be parameterized for parameter `tx_type` with values `[0, 1]` for fork Berlin, but with values `[0, 1, 2]` for fork London (because of EIP-1559).
61+
62+
### pytest.mark.with_all_contract_creating_tx_types
63+
64+
This marker is used to automatically parameterize a test with all contract creating transaction types that are valid for the fork being tested.
65+
66+
This marker only differs from `pytest.mark.with_all_tx_types` in that it does not include transaction type 3 (Blob Transaction type) on fork Cancun and after.
67+
68+
### pytest.mark.with_all_precompiles
69+
70+
This marker is used to automatically parameterize a test with all precompiles that are valid for the fork being tested.
71+
72+
```python
73+
import pytest
74+
75+
@pytest.mark.with_all_precompiles
76+
@pytest.mark.valid_from("Shanghai")
77+
def test_something_with_all_precompiles(precompile):
78+
pass
79+
```
80+
81+
In this example, the test will be parameterized for parameter `precompile` with values `[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]` for fork Shanghai, but with values `[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]` for fork Cancun (because of EIP-4844).
82+
83+
## Other Markers
84+
85+
### pytest.mark.slow
86+
87+
This marker is used to mark tests that are slow to run. These tests are not run during tox testing, and are only run when a release is being prepared.
88+
89+
### pytest.mark.skip("reason")
90+
91+
This marker is used to skip a test with a reason.
92+
93+
```python
94+
import pytest
95+
96+
@pytest.mark.skip("Not implemented")
97+
def test_something():
98+
pass
99+
```
100+
101+
### pytest.mark.xfail("reason")
102+
103+
This marker is used to mark a test as expected to fail.
104+
105+
```python
106+
import pytest
107+
108+
@pytest.mark.xfail("EVM binary doesn't support this opcode")
109+
def test_something():
110+
pass
111+
```

src/ethereum_test_forks/base_fork.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ def tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
181181
"""
182182
pass
183183

184+
@classmethod
185+
@abstractmethod
186+
def contract_creating_tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
187+
"""
188+
Returns a list of the transaction types supported by the fork that can create contracts
189+
"""
190+
pass
191+
184192
@classmethod
185193
@abstractmethod
186194
def precompiles(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:

src/ethereum_test_forks/forks/forks.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ def tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
156156
"""
157157
return [0]
158158

159+
@classmethod
160+
def contract_creating_tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
161+
"""
162+
At Genesis, only legacy transactions are allowed
163+
"""
164+
return [0]
165+
159166
@classmethod
160167
def precompiles(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
161168
"""
@@ -274,6 +281,13 @@ def tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
274281
"""
275282
return [1] + super(Berlin, cls).tx_types(block_number, timestamp)
276283

284+
@classmethod
285+
def contract_creating_tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
286+
"""
287+
At Berlin, access list transactions are introduced
288+
"""
289+
return [1] + super(Berlin, cls).contract_creating_tx_types(block_number, timestamp)
290+
277291

278292
class London(Berlin):
279293
"""
@@ -294,6 +308,13 @@ def tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
294308
"""
295309
return [2] + super(London, cls).tx_types(block_number, timestamp)
296310

311+
@classmethod
312+
def contract_creating_tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
313+
"""
314+
At London, dynamic fee transactions are introduced
315+
"""
316+
return [2] + super(London, cls).contract_creating_tx_types(block_number, timestamp)
317+
297318

298319
# Glacier forks skipped, unless explicitly specified
299320
class ArrowGlacier(London, solc_name="london", ignore=True):

src/ethereum_test_tools/common/types.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -937,39 +937,37 @@ def model_post_init(self, __context):
937937
or self.max_fee_per_blob_gas is not None
938938
):
939939
raise Transaction.InvalidFeePayment()
940-
941-
if (
942-
self.gas_price is None
943-
and self.max_fee_per_gas is None
944-
and self.max_priority_fee_per_gas is None
945-
and self.max_fee_per_blob_gas is None
946-
):
947-
self.gas_price = 10
948-
949-
if self.v is not None and self.secret_key is not None:
950-
raise Transaction.InvalidSignaturePrivateKey()
951-
952-
if self.v is None and self.secret_key is None:
953-
self.secret_key = Hash(TestPrivateKey)
954-
955940
if "ty" not in self.model_fields_set:
956941
# Try to deduce transaction type from included fields
957-
if self.max_fee_per_blob_gas is not None:
942+
if self.max_fee_per_blob_gas is not None or self.blob_kzg_commitments is not None:
958943
self.ty = 3
959-
elif self.max_fee_per_gas is not None:
944+
elif self.max_fee_per_gas is not None or self.max_priority_fee_per_gas is not None:
960945
self.ty = 2
961946
elif self.access_list is not None:
962947
self.ty = 1
963948
else:
964949
self.ty = 0
965950

951+
if self.v is not None and self.secret_key is not None:
952+
raise Transaction.InvalidSignaturePrivateKey()
953+
954+
if self.v is None and self.secret_key is None:
955+
self.secret_key = Hash(TestPrivateKey)
956+
966957
# Set default values for fields that are required for certain tx types
958+
if self.ty <= 1 and self.gas_price is None:
959+
self.gas_price = 10
967960
if self.ty >= 1 and self.access_list is None:
968961
self.access_list = []
969962

963+
if self.ty >= 2 and self.max_fee_per_gas is None:
964+
self.max_fee_per_gas = 7
970965
if self.ty >= 2 and self.max_priority_fee_per_gas is None:
971966
self.max_priority_fee_per_gas = 0
972967

968+
if self.ty == 3 and self.max_fee_per_blob_gas is None:
969+
self.max_fee_per_blob_gas = 1
970+
973971
def with_error(
974972
self, error: List[TransactionException] | TransactionException
975973
) -> "Transaction":

src/evm_transition_tool/evmone.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,11 @@ def is_fork_supported(self, fork: Fork) -> bool:
3737
Currently, evmone-t8n provides no way to determine supported forks.
3838
"""
3939
return True
40+
41+
@classmethod
42+
def empty_string_to(cls) -> bool:
43+
"""
44+
Evmone requires an empty string within the `to` field for
45+
contract-creating transactions.
46+
"""
47+
return True

src/evm_transition_tool/transition_tool.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,14 @@ def detect_binary(cls, binary_output: str) -> bool:
225225

226226
return cls.detect_binary_pattern.match(binary_output) is not None
227227

228+
@classmethod
229+
def empty_string_to(cls) -> bool:
230+
"""
231+
Returns True if the tool requires an empty string `to` field
232+
for contract creating transactions.
233+
"""
234+
return False
235+
228236
def version(self) -> str:
229237
"""
230238
Return name and version of tool used to state transition
@@ -311,6 +319,15 @@ class TransitionToolData:
311319
fork_name: str
312320
chain_id: int = field(default=1)
313321
reward: int = field(default=0)
322+
empty_string_to: bool = field(default=False)
323+
324+
def __post_init__(self):
325+
"""
326+
Ensure that the `to` field of transactions is not None
327+
"""
328+
if self.empty_string_to:
329+
for tx in self.txs:
330+
tx["to"] = tx.get("to") or ""
314331

315332
def _evaluate_filesystem(
316333
self,
@@ -571,7 +588,13 @@ def evaluate(
571588
if int(env["currentNumber"], 0) == 0:
572589
reward = -1
573590
t8n_data = TransitionTool.TransitionToolData(
574-
alloc=alloc, txs=txs, env=env, fork_name=fork_name, chain_id=chain_id, reward=reward
591+
alloc=alloc,
592+
txs=txs,
593+
env=env,
594+
fork_name=fork_name,
595+
chain_id=chain_id,
596+
reward=reward,
597+
empty_string_to=self.empty_string_to(),
575598
)
576599

577600
if self.t8n_use_stream:

src/pytest_plugins/forks/forks.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,13 @@ def add_values(self, metafunc: Metafunc, fork_parametrizer: ForkParametrizer) ->
139139
fork_attribute_name="tx_types",
140140
parameter_name="tx_type",
141141
),
142+
CovariantDescriptor(
143+
marker_name="with_all_contract_creating_tx_types",
144+
description="marks a test to be parametrized for all tx types that can create a contract"
145+
" at parameter named tx_type of type int",
146+
fork_attribute_name="contract_creating_tx_types",
147+
parameter_name="tx_type",
148+
),
142149
CovariantDescriptor(
143150
marker_name="with_all_precompiles",
144151
description="marks a test to be parametrized for all precompiles at parameter named"

0 commit comments

Comments
 (0)