Skip to content

Commit b91ca05

Browse files
committed
Merge branch 'eip-7251-eip-2935-contracts-update' into pectra-devnet-5
2 parents c75deff + 1a2722a commit b91ca05

File tree

18 files changed

+470
-30
lines changed

18 files changed

+470
-30
lines changed

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Test fixtures for use by clients are available for each release on the [Github r
6767
- ✨ Add a default location for evm logs (`--evm-dump-dir`) when filling tests ([#999](https://github.com/ethereum/execution-spec-tests/pull/999)).
6868
- ✨ Slow tests now have greater timeout when making a request to the T8N server ([#1037](https://github.com/ethereum/execution-spec-tests/pull/1037)).
6969
- ✨ Disable EIP-7742 framework changes for Prague ([#1023](https://github.com/ethereum/execution-spec-tests/pull/1023)).
70+
- 🔀 Update EIP-7251, EIP-7002 and EIP-2935 according to spec updates ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024))
7071

7172
### 🔧 EVM Tools
7273

src/ethereum_test_base_types/base_types.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,12 @@ def sha256(self) -> "Hash":
211211
"""
212212
return Hash(sha256(self).digest())
213213

214+
def int(self) -> int:
215+
"""
216+
Returns the integer representation of the bytes.
217+
"""
218+
return int.from_bytes(self, "big")
219+
214220

215221
S = TypeVar("S", bound="FixedSizeHexNumber")
216222

Binary file not shown.
-17 Bytes
Binary file not shown.

src/ethereum_test_forks/forks/forks.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,9 +1253,9 @@ def system_contracts(cls, block_number: int = 0, timestamp: int = 0) -> List[Add
12531253
"""
12541254
return [
12551255
Address(0x00000000219AB540356CBB839CBE05303D7705FA),
1256-
Address(0x09FC772D0857550724B07B850A4323F39112AAAA),
1257-
Address(0x01ABEA29659E5E97C95107F20BB753CD3E09BBBB),
1258-
Address(0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E),
1256+
Address(0x0C15F14308530B7CDB8460094BBB9CC28B9AAAAA),
1257+
Address(0x00431F263CE400F4455C2DCF564E53007CA4BBBB),
1258+
Address(0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC),
12591259
] + super(Prague, cls).system_contracts(block_number, timestamp)
12601260

12611261
@classmethod
@@ -1397,7 +1397,7 @@ def pre_allocation_blockchain(cls) -> Mapping:
13971397
with open(CURRENT_FOLDER / "contracts" / "withdrawal_request.bin", mode="rb") as f:
13981398
new_allocation.update(
13991399
{
1400-
0x09FC772D0857550724B07B850A4323F39112AAAA: {
1400+
0x0C15F14308530B7CDB8460094BBB9CC28B9AAAAA: {
14011401
"nonce": 1,
14021402
"code": f.read(),
14031403
},
@@ -1408,7 +1408,7 @@ def pre_allocation_blockchain(cls) -> Mapping:
14081408
with open(CURRENT_FOLDER / "contracts" / "consolidation_request.bin", mode="rb") as f:
14091409
new_allocation.update(
14101410
{
1411-
0x01ABEA29659E5E97C95107F20BB753CD3E09BBBB: {
1411+
0x00431F263CE400F4455C2DCF564E53007CA4BBBB: {
14121412
"nonce": 1,
14131413
"code": f.read(),
14141414
},
@@ -1419,7 +1419,7 @@ def pre_allocation_blockchain(cls) -> Mapping:
14191419
with open(CURRENT_FOLDER / "contracts" / "history_contract.bin", mode="rb") as f:
14201420
new_allocation.update(
14211421
{
1422-
0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E: {
1422+
0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC: {
14231423
"nonce": 1,
14241424
"code": f.read(),
14251425
}

src/ethereum_test_tools/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
Yul,
8383
YulCompiler,
8484
)
85+
from .utility.generators import generate_system_contract_deploy_test
8586
from .utility.pytest import extend_with_defaults
8687

8788
__all__ = (
@@ -155,6 +156,7 @@
155156
"compute_create2_address",
156157
"compute_eofcreate_address",
157158
"extend_with_defaults",
159+
"generate_system_contract_deploy_test",
158160
"keccak256",
159161
"vm",
160162
)
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"""
2+
Test generator decorators.
3+
"""
4+
5+
import json
6+
from enum import Enum
7+
from pathlib import Path
8+
from typing import Dict, Generator, List, Protocol, Tuple
9+
10+
import pytest
11+
12+
from ethereum_test_base_types import Account, Address
13+
from ethereum_test_forks import Fork
14+
from ethereum_test_specs import BlockchainTestFiller
15+
from ethereum_test_specs.blockchain import Block, Header
16+
from ethereum_test_types import Alloc, Transaction
17+
18+
19+
class SystemContractDeployTestFunction(Protocol):
20+
"""
21+
Represents a function to be decorated with the `generate_system_contract_deploy_test`
22+
decorator.
23+
"""
24+
25+
def __call__(
26+
self,
27+
*,
28+
fork: Fork,
29+
pre: Alloc,
30+
) -> Generator[Tuple[Transaction | None, Header | None], None, None]:
31+
"""
32+
Args:
33+
fork (Fork): The fork to test.
34+
pre (Alloc): The pre state of the blockchain.
35+
36+
Yields:
37+
Tuple[Transaction | None, Header | None]: Once per block to add after the contract is
38+
deployed, with a single transaction to execute and the header object used to
39+
verify the block.
40+
"""
41+
...
42+
43+
44+
def generate_system_contract_deploy_test(
45+
fork: Fork,
46+
tx_json_path: Path,
47+
expected_deploy_address: Address,
48+
expected_system_contract_storage: Dict | None,
49+
):
50+
"""
51+
Decorator to generate a test that verifies the correct deployment of a system contract.
52+
53+
Generates two tests:
54+
- One that deploys the contract before the fork.
55+
- One that deploys the contract after the fork.
56+
57+
Args:
58+
fork (Fork): The fork to test.
59+
tx_json_path (Path): Path to the JSON file with the transaction to deploy the system
60+
contract.
61+
Providing a JSON file is useful to copy-paste the transaction from the EIP.
62+
expected_deploy_address (Address): The expected address of the deployed contract.
63+
expected_system_contract_storage (Dict): The expected storage of the system contract.
64+
"""
65+
DeploymentTestType = Enum(
66+
"DeploymentTestType",
67+
[
68+
"DEPLOY_BEFORE_FORK",
69+
"DEPLOY_AFTER_FORK",
70+
],
71+
)
72+
73+
with open(tx_json_path, mode="r") as f:
74+
tx_json = json.loads(f.read())
75+
if "gasLimit" not in tx_json and "gas" in tx_json:
76+
tx_json["gasLimit"] = tx_json["gas"]
77+
del tx_json["gas"]
78+
if "protected" not in tx_json:
79+
tx_json["protected"] = False
80+
deploy_tx = Transaction.model_validate(tx_json).with_signature_and_sender() # type: ignore
81+
gas_price = deploy_tx.gas_price
82+
assert gas_price is not None
83+
deployer_required_balance = deploy_tx.gas_limit * gas_price
84+
deployer_address = deploy_tx.sender
85+
86+
def decorator(func: SystemContractDeployTestFunction):
87+
@pytest.mark.parametrize(
88+
"test_type",
89+
[
90+
pytest.param(DeploymentTestType.DEPLOY_BEFORE_FORK),
91+
pytest.param(DeploymentTestType.DEPLOY_AFTER_FORK),
92+
],
93+
ids=lambda x: x.name.lower(),
94+
)
95+
@pytest.mark.execute(pytest.mark.skip(reason="modifies pre-alloc"))
96+
@pytest.mark.valid_at_transition_to(fork.name())
97+
def wrapper(
98+
blockchain_test: BlockchainTestFiller,
99+
pre: Alloc,
100+
test_type: DeploymentTestType,
101+
fork: Fork,
102+
):
103+
104+
assert deployer_address is not None
105+
assert deploy_tx.created_contract == expected_deploy_address
106+
blocks: List[Block] = []
107+
108+
if test_type == DeploymentTestType.DEPLOY_BEFORE_FORK:
109+
blocks = [
110+
Block( # Deployment block
111+
txs=[deploy_tx],
112+
timestamp=14_999,
113+
),
114+
Block( # Empty block on fork
115+
txs=[],
116+
timestamp=15_000,
117+
),
118+
]
119+
elif test_type == DeploymentTestType.DEPLOY_AFTER_FORK:
120+
blocks = [
121+
Block( # Empty block on fork
122+
txs=[],
123+
timestamp=15_000,
124+
),
125+
Block( # Deployment block
126+
txs=[deploy_tx],
127+
timestamp=15_001,
128+
),
129+
]
130+
131+
for tx_header_verify in func(fork=fork, pre=pre):
132+
txs = []
133+
if tx_header_verify[0] is not None:
134+
txs.append(tx_header_verify[0])
135+
header_verify = tx_header_verify[1]
136+
blocks.append(
137+
Block(
138+
txs=txs,
139+
header_verify=header_verify,
140+
)
141+
)
142+
143+
pre[expected_deploy_address] = Account(
144+
code=b"", # Remove the code that is automatically allocated on the fork
145+
nonce=0,
146+
balance=0,
147+
)
148+
pre[deployer_address] = Account(
149+
balance=deployer_required_balance,
150+
)
151+
152+
post = {}
153+
fork_pre_allocation = fork.pre_allocation_blockchain()
154+
assert expected_deploy_address.int() in fork_pre_allocation
155+
expected_code = fork_pre_allocation[expected_deploy_address.int()]["code"]
156+
if expected_system_contract_storage is None:
157+
post[expected_deploy_address] = Account(
158+
code=expected_code,
159+
nonce=1,
160+
)
161+
else:
162+
post[expected_deploy_address] = Account(
163+
storage=expected_system_contract_storage,
164+
code=expected_code,
165+
nonce=1,
166+
)
167+
post[deployer_address] = Account(
168+
nonce=1,
169+
)
170+
blockchain_test(
171+
pre=pre,
172+
blocks=blocks,
173+
post=post,
174+
)
175+
176+
wrapper.__name__ = func.__name__ # type: ignore
177+
wrapper.__doc__ = func.__doc__ # type: ignore
178+
179+
return wrapper
180+
181+
return decorator
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"type": "0x0",
3+
"nonce": "0x0",
4+
"to": null,
5+
"gas": "0x3d090",
6+
"gasPrice": "0xe8d4a51000",
7+
"maxPriorityFeePerGas": null,
8+
"maxFeePerGas": null,
9+
"value": "0x0",
10+
"input": "0x60538060095f395ff33373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
11+
"v": "0x1b",
12+
"r": "0x539",
13+
"s": "0xbaefe09f0109759",
14+
"hash": "0x8c7bd2d3713a0b2bb693463d2a78c4d612ac47dd38ecb74f8996a4b6fc96f03c"
15+
}

tests/prague/eip2935_historical_block_hashes_from_state/spec.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class ReferenceSpec:
1515
version: str
1616

1717

18-
ref_spec_2935 = ReferenceSpec("EIPS/eip-2935.md", "68d54a80a4f5b9c0cf4ae3a10586d63ef221de36")
18+
ref_spec_2935 = ReferenceSpec("EIPS/eip-2935.md", "a04da454a5a6ba86a87bb9e15f811feaff3c849a")
1919

2020

2121
@dataclass(frozen=True)
@@ -26,6 +26,6 @@ class Spec:
2626
"""
2727

2828
FORK_TIMESTAMP = 15_000
29-
HISTORY_STORAGE_ADDRESS = 0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E
30-
HISTORY_SERVE_WINDOW = 8192
29+
HISTORY_STORAGE_ADDRESS = 0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC
30+
HISTORY_SERVE_WINDOW = 8191
3131
BLOCKHASH_OLD_WINDOW = 256

tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,9 @@ def test_block_hashes_history(
302302
@pytest.mark.parametrize(
303303
"block_number,reverts",
304304
[
305-
pytest.param(1, False, id="current_block"),
306-
pytest.param(2, False, id="future_block"),
307-
pytest.param(2**64 - 1, False, id="2**64-1"),
305+
pytest.param(1, True, id="current_block"),
306+
pytest.param(2, True, id="future_block"),
307+
pytest.param(2**64 - 1, True, id="2**64-1"),
308308
pytest.param(2**64, True, id="2**64"),
309309
],
310310
)
@@ -328,13 +328,24 @@ def test_invalid_history_contract_calls(
328328
returned_block_hash_slot = storage.store_next(0)
329329
block_hash_opcode_slot = storage.store_next(0)
330330

331+
return_offset = 64
332+
return_size = 32
333+
args_size = 32
334+
331335
# Check the first block outside of the window if any
332336
code = (
333337
Op.MSTORE(0, block_number)
334338
+ Op.SSTORE(
335-
return_code_slot, Op.CALL(Op.GAS, Spec.HISTORY_STORAGE_ADDRESS, 0, 0, 32, 32, 64)
339+
return_code_slot,
340+
Op.CALL(
341+
address=Spec.HISTORY_STORAGE_ADDRESS,
342+
args_offset=0,
343+
args_size=args_size,
344+
ret_offset=return_offset,
345+
ret_size=return_size,
346+
),
336347
)
337-
+ Op.SSTORE(returned_block_hash_slot, Op.MLOAD(32))
348+
+ Op.SSTORE(returned_block_hash_slot, Op.MLOAD(return_offset))
338349
+ Op.SSTORE(block_hash_opcode_slot, Op.BLOCKHASH(block_number))
339350
)
340351
check_contract_address = pre.deploy_contract(code, storage=storage.canary())
@@ -355,3 +366,65 @@ def test_invalid_history_contract_calls(
355366
post=post,
356367
reverts=reverts,
357368
)
369+
370+
371+
@pytest.mark.parametrize(
372+
"args_size,reverts",
373+
[
374+
pytest.param(0, True, id="zero_size"),
375+
pytest.param(33, True, id="too_large"),
376+
pytest.param(31, True, id="too_small"),
377+
],
378+
)
379+
@pytest.mark.valid_from("Prague")
380+
def test_invalid_history_contract_calls_input_size(
381+
blockchain_test: BlockchainTestFiller,
382+
pre: Alloc,
383+
reverts: bool,
384+
args_size: int,
385+
):
386+
"""
387+
Test calling the history contract with invalid input sizes.
388+
"""
389+
storage = Storage()
390+
391+
return_code_slot = storage.store_next(not reverts)
392+
returned_block_hash_slot = storage.store_next(0)
393+
394+
return_offset = 64
395+
return_size = 32
396+
block_number = 0
397+
398+
# Check the first block outside of the window if any
399+
code = (
400+
Op.MSTORE(0, block_number)
401+
+ Op.SSTORE(
402+
return_code_slot,
403+
Op.CALL(
404+
address=Spec.HISTORY_STORAGE_ADDRESS,
405+
args_offset=0,
406+
args_size=args_size,
407+
ret_offset=return_offset,
408+
ret_size=return_size,
409+
),
410+
)
411+
+ Op.SSTORE(returned_block_hash_slot, Op.MLOAD(return_offset))
412+
)
413+
check_contract_address = pre.deploy_contract(code, storage=storage.canary())
414+
415+
txs = [
416+
Transaction(
417+
to=check_contract_address,
418+
gas_limit=10_000_000,
419+
sender=pre.fund_eoa(),
420+
)
421+
]
422+
post = {check_contract_address: Account(storage=storage)}
423+
424+
blocks = [Block(txs=txs)]
425+
blockchain_test(
426+
pre=pre,
427+
blocks=blocks,
428+
post=post,
429+
reverts=reverts,
430+
)

0 commit comments

Comments
 (0)