Skip to content

Commit ca18196

Browse files
committed
fix(tools,tests): EIP-2935,7002,7251: Improve generate_system_contract_deploy_test
1 parent 1574434 commit ca18196

File tree

5 files changed

+77
-30
lines changed

5 files changed

+77
-30
lines changed

docs/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ Test fixtures for use by clients are available for each release on the [Github r
2020

2121
- 🐞 Fix fixture tarball downloading with regular, non-Github release URLS and with numerical versions in regular release specs, e.g., `[email protected]` ([#1437](https://github.com/ethereum/execution-spec-tests/pull/1437)).
2222

23+
#### Tools
24+
25+
- 🔀 `generate_system_contract_deploy_test` test generator has been updated to handle system contracts that are not allowed to be absent when the fork happens ([#1394](https://github.com/ethereum/execution-spec-tests/pull/1394)).
26+
2327
#### Exceptions
2428

2529
- ✨ New exceptions `BlockException.SYSTEM_CONTRACT_EMPTY` and `BlockException.SYSTEM_CONTRACT_CALL_FAILED` to handle EIP updates [#9508](https://github.com/ethereum/EIPs/pull/9508) and [#9582](https://github.com/ethereum/EIPs/pull/9582) ([#1394](https://github.com/ethereum/execution-spec-tests/pull/1394)).
@@ -33,6 +37,7 @@ Test fixtures for use by clients are available for each release on the [Github r
3337
-[EIP-7251](https://eips.ethereum.org/EIPS/eip-7251): Add EIP-7251 test cases for modified consolidations contract that allows more consolidations ([#1465](https://github.com/ethereum/execution-spec-tests/pull/1465)).
3438
-[EIP-6110](https://eips.ethereum.org/EIPS/eip-6110): Add extra deposit request edge cases, sending eth to the deposit contract while sending a deposit request ([#1467](https://github.com/ethereum/execution-spec-tests/pull/1467)).
3539
-[EIP-7251](https://eips.ethereum.org/EIPS/eip-7251): Remove pytest skips for consolidation request cases ([#1449](https://github.com/ethereum/execution-spec-tests/pull/1449)).
40+
-[EIP-7002](https://eips.ethereum.org/EIPS/eip-7002), [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251): Add cases to verify behavior of contracts missing at fork ([#1394](https://github.com/ethereum/execution-spec-tests/pull/1394)).
3641

3742
### 📋 Misc
3843

src/ethereum_test_tools/utility/generators.py

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import pytest
99

1010
from ethereum_test_base_types import Account, Address, Hash
11+
from ethereum_test_exceptions import BlockException
1112
from ethereum_test_forks import Fork
1213
from ethereum_test_specs import BlockchainTestFiller
1314
from ethereum_test_specs.blockchain import Block
@@ -18,6 +19,7 @@ class DeploymentTestType(Enum):
1819
"""Represents the type of deployment test."""
1920

2021
DEPLOY_BEFORE_FORK = "deploy_before_fork"
22+
DEPLOY_ON_FORK_BLOCK = "deploy_on_fork_block"
2123
DEPLOY_AFTER_FORK = "deploy_after_fork"
2224

2325

@@ -63,29 +65,33 @@ def generate_system_contract_deploy_test(
6365
fork: Fork,
6466
tx_json_path: Path,
6567
expected_deploy_address: Address,
68+
fail_on_empty_code: bool,
6669
expected_system_contract_storage: Dict | None = None,
6770
):
6871
"""
6972
Generate a test that verifies the correct deployment of a system contract.
7073
71-
Generates four test cases:
74+
Generates following test cases:
7275
73-
| before/after fork | has balance |
74-
------------------------------------|-------------------|-------------|
75-
`deploy_before_fork-nonzero_balance`| before | True |
76-
`deploy_before_fork-zero_balance` | before | False |
77-
`deploy_after_fork-nonzero_balance` | after | True |
78-
`deploy_after_fork-zero_balance` | after | False |
76+
| before/after fork | fail on | invalid block |
77+
| | empty block | |
78+
--------------------------------------|-------------------|-------------|---------------|
79+
`deploy_before_fork-nonzero_balance` | before | False | False |
80+
`deploy_before_fork-zero_balance` | before | True | False |
81+
`deploy_on_fork_block-nonzero_balance`| on fork block | False | False |
82+
`deploy_on_fork_block-zero_balance` | on fork block | True | False |
83+
`deploy_after_fork-nonzero_balance` | after | False | False |
84+
`deploy_after_fork-zero_balance` | after | True | True |
7985
80-
where `has balance` refers to whether the contract address has a non-zero balance before
81-
deployment, or not.
86+
The `has balance` parametrization does not have an effect on the expectation of the test.
8287
8388
Args:
8489
fork (Fork): The fork to test.
8590
tx_json_path (Path): Path to the JSON file with the transaction to deploy the system
8691
contract.
8792
Providing a JSON file is useful to copy-paste the transaction from the EIP.
8893
expected_deploy_address (Address): The expected address of the deployed contract.
94+
fail_on_empty_code (bool): If True, the test is expected to fail on empty code.
8995
expected_system_contract_storage (Dict | None): The expected storage of the system
9096
contract.
9197
@@ -120,7 +126,11 @@ def decorator(func: SystemContractDeployTestFunction):
120126
"test_type",
121127
[
122128
pytest.param(DeploymentTestType.DEPLOY_BEFORE_FORK),
123-
pytest.param(DeploymentTestType.DEPLOY_AFTER_FORK),
129+
pytest.param(DeploymentTestType.DEPLOY_ON_FORK_BLOCK),
130+
pytest.param(
131+
DeploymentTestType.DEPLOY_AFTER_FORK,
132+
marks=[pytest.mark.exception_test] if fail_on_empty_code else [],
133+
),
124134
],
125135
ids=lambda x: x.name.lower(),
126136
)
@@ -148,17 +158,40 @@ def wrapper(
148158
timestamp=15_000,
149159
),
150160
]
151-
elif test_type == DeploymentTestType.DEPLOY_AFTER_FORK:
161+
elif test_type == DeploymentTestType.DEPLOY_ON_FORK_BLOCK:
152162
blocks = [
153-
Block( # Empty block on fork
154-
txs=[],
163+
Block( # Deployment on fork block
164+
txs=[deploy_tx],
155165
timestamp=15_000,
156166
),
157-
Block( # Deployment block
158-
txs=[deploy_tx],
167+
Block( # Empty block after fork
168+
txs=[],
159169
timestamp=15_001,
160170
),
161171
]
172+
elif test_type == DeploymentTestType.DEPLOY_AFTER_FORK:
173+
blocks = [
174+
Block( # Empty block on fork
175+
txs=[],
176+
timestamp=15_000,
177+
exception=BlockException.SYSTEM_CONTRACT_EMPTY
178+
if fail_on_empty_code
179+
else None,
180+
)
181+
]
182+
if not fail_on_empty_code:
183+
blocks.append(
184+
Block( # Deployment after fork block
185+
txs=[deploy_tx],
186+
timestamp=15_001,
187+
)
188+
)
189+
blocks.append(
190+
Block( # Empty block after deployment
191+
txs=[],
192+
timestamp=15_002,
193+
),
194+
)
162195
balance = 1 if has_balance == ContractAddressHasBalance.NONZERO_BALANCE else 0
163196
pre[expected_deploy_address] = Account(
164197
code=b"", # Remove the code that is automatically allocated on the fork
@@ -176,24 +209,23 @@ def wrapper(
176209
assert expected_deploy_address_int in fork_pre_allocation
177210
expected_code = fork_pre_allocation[expected_deploy_address_int]["code"]
178211
# Note: balance check is omitted; it may be modified by the underlying, decorated test
179-
if expected_system_contract_storage is None:
180-
post[expected_deploy_address] = Account(
181-
code=expected_code,
182-
nonce=1,
183-
)
184-
else:
185-
post[expected_deploy_address] = Account(
186-
storage=expected_system_contract_storage,
187-
code=expected_code,
212+
account_kwargs = {
213+
"code": expected_code,
214+
"nonce": 1,
215+
}
216+
if expected_system_contract_storage:
217+
account_kwargs["storage"] = expected_system_contract_storage
218+
if test_type != DeploymentTestType.DEPLOY_AFTER_FORK or not fail_on_empty_code:
219+
post[expected_deploy_address] = Account(**account_kwargs)
220+
post[deployer_address] = Account(
188221
nonce=1,
189222
)
190-
post[deployer_address] = Account(
191-
nonce=1,
192-
)
193223

194224
# Extra blocks (if any) returned by the decorated function to add after the
195225
# contract is deployed.
196-
blocks += list(func(fork=fork, pre=pre, post=post, test_type=test_type))
226+
if test_type != DeploymentTestType.DEPLOY_AFTER_FORK or not fail_on_empty_code:
227+
# Only fill more blocks if the deploy block does not fail.
228+
blocks += list(func(fork=fork, pre=pre, post=post, test_type=test_type))
197229

198230
blockchain_test(
199231
pre=pre,

tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
fork=Prague,
3030
tx_json_path=Path(realpath(__file__)).parent / "contract_deploy_tx.json",
3131
expected_deploy_address=Address(Spec.HISTORY_STORAGE_ADDRESS),
32+
fail_on_empty_code=False,
3233
)
3334
def test_system_contract_deployment(
3435
*,
@@ -50,11 +51,11 @@ def test_system_contract_deployment(
5051
address=Spec.HISTORY_STORAGE_ADDRESS,
5152
args_offset=0,
5253
args_size=32,
53-
ret_offset=0,
54+
ret_offset=32,
5455
ret_size=32,
5556
),
5657
)
57-
+ Op.SSTORE(block_number, Op.ISZERO(Op.ISZERO(Op.MLOAD(0))))
58+
+ Op.SSTORE(block_number, Op.ISZERO(Op.ISZERO(Op.MLOAD(32))))
5859
for block_number in range(1, 4)
5960
)
6061
+ Op.STOP
@@ -78,6 +79,13 @@ def test_system_contract_deployment(
7879
2: 1, # Fork block, hash should be there.
7980
3: 1, # Empty block added at the start of this function, hash should be there.
8081
}
82+
elif test_type == DeploymentTestType.DEPLOY_ON_FORK_BLOCK:
83+
# The contract should have the block hashes after contract deployment.
84+
storage = {
85+
1: 1, # Fork and deployment block, the first hash that gets added.
86+
2: 1, # Deployment block, hash should be there.
87+
3: 1, # Empty block added at the start of this function, hash should be there.
88+
}
8189
elif test_type == DeploymentTestType.DEPLOY_AFTER_FORK:
8290
# The contract should have the block hashes after contract deployment.
8391
storage = {

tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
fork=Prague,
3030
tx_json_path=Path(realpath(__file__)).parent / "contract_deploy_tx.json",
3131
expected_deploy_address=Address(Spec.WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS),
32+
fail_on_empty_code=True,
3233
)
3334
def test_system_contract_deployment(
3435
*,

tests/prague/eip7251_consolidations/test_contract_deployment.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
fork=Prague,
3030
tx_json_path=Path(realpath(__file__)).parent / "contract_deploy_tx.json",
3131
expected_deploy_address=Address(Spec.CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS),
32+
fail_on_empty_code=True,
3233
)
3334
def test_system_contract_deployment(
3435
*,

0 commit comments

Comments
 (0)