Skip to content

Commit 76069fe

Browse files
feat(forks,tests): EIP-2935: Contract update (#1046)
* feat(forks,tests): EIP-2935: Contract update * Update tests/prague/eip2935_historical_block_hashes_from_state/test_contract_deployment.py Co-authored-by: danceratopz <danceratopz@gmail.com> * fix: Add storage hint * refactor(tools): Use `Block` instead of `Tuple` * fix(tools): pass test_type * new(tests): Add block hash checking on deployment test --------- Co-authored-by: danceratopz <danceratopz@gmail.com>
1 parent 3c60c4f commit 76069fe

File tree

12 files changed

+240
-62
lines changed

12 files changed

+240
-62
lines changed

docs/CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ Test fixtures for use by clients are available for each release on the [Github r
2121
-[EIP-7623](https://eips.ethereum.org/EIPS/eip-7623) Increase calldata cost ([#1004](https://github.com/ethereum/execution-spec-tests/pull/1004))
2222
- ✨ Add generic precompile-absence test ([#1036](https://github.com/ethereum/execution-spec-tests/pull/1036))
2323
- ✨ Add test for [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) which uses the full discount table of G2 MSM ([#1038](https://github.com/ethereum/execution-spec-tests/pull/1038))
24+
- 🔀 Update EIP-7251 according to [spec updates](https://github.com/ethereum/EIPs/pull/9127) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
25+
- 🔀 Update EIP-7002 according to [spec updates](https://github.com/ethereum/EIPs/pull/9119) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
26+
- 🔀 Update EIP-2935 according to [spec updates](https://github.com/ethereum/EIPs/pull/9144) ([#1046](https://github.com/ethereum/execution-spec-tests/pull/1046))
2427

2528
### 🛠️ Framework
2629

@@ -67,8 +70,6 @@ Test fixtures for use by clients are available for each release on the [Github r
6770
- ✨ Add a default location for evm logs (`--evm-dump-dir`) when filling tests ([#999](https://github.com/ethereum/execution-spec-tests/pull/999)).
6871
- ✨ Slow tests now have greater timeout when making a request to the T8N server ([#1037](https://github.com/ethereum/execution-spec-tests/pull/1037)).
6972
- ✨ Introduce [`pytest.mark.parametrize_by_fork`](https://ethereum.github.io/execution-spec-tests/main/writing_tests/test_markers/#pytestmarkfork_parametrize) helper marker ([#1019](https://github.com/ethereum/execution-spec-tests/pull/1019), [#1057](https://github.com/ethereum/execution-spec-tests/pull/1057)).
70-
- 🔀 Update EIP-7251 according to [spec updates](https://github.com/ethereum/EIPs/pull/9127) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
71-
- 🔀 Update EIP-7002 according to [spec updates](https://github.com/ethereum/EIPs/pull/9119) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
7273
- 🐞 fix(consume): allow absolute paths with `--evm-bin` ([#1052](https://github.com/ethereum/execution-spec-tests/pull/1052)).
7374

7475
### 🔧 EVM Tools
Binary file not shown.

src/ethereum_test_forks/forks/forks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,7 @@ def system_contracts(cls, block_number: int = 0, timestamp: int = 0) -> List[Add
986986
Address(0x00000000219AB540356CBB839CBE05303D7705FA),
987987
Address(0x0C15F14308530B7CDB8460094BBB9CC28B9AAAAA),
988988
Address(0x00431F263CE400F4455C2DCF564E53007CA4BBBB),
989-
Address(0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E),
989+
Address(0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC),
990990
] + super(Prague, cls).system_contracts(block_number, timestamp)
991991

992992
@classmethod
@@ -1126,7 +1126,7 @@ def pre_allocation_blockchain(cls) -> Mapping:
11261126
with open(CURRENT_FOLDER / "contracts" / "history_contract.bin", mode="rb") as f:
11271127
new_allocation.update(
11281128
{
1129-
0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E: {
1129+
0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC: {
11301130
"nonce": 1,
11311131
"code": f.read(),
11321132
}

src/ethereum_test_tools/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
Yul,
8383
YulCompiler,
8484
)
85-
from .utility.generators import generate_system_contract_deploy_test
85+
from .utility.generators import DeploymentTestType, generate_system_contract_deploy_test
8686
from .utility.pytest import extend_with_defaults
8787

8888
__all__ = (
@@ -108,6 +108,7 @@
108108
"Conditional",
109109
"ConsolidationRequest",
110110
"DepositRequest",
111+
"DeploymentTestType",
111112
"EngineAPIError",
112113
"Environment",
113114
"EOFException",

src/ethereum_test_tools/utility/generators.py

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,24 @@
33
import json
44
from enum import Enum
55
from pathlib import Path
6-
from typing import Dict, Generator, List, Protocol, Tuple
6+
from typing import Dict, Generator, List, Protocol
77

88
import pytest
99

1010
from ethereum_test_base_types import Account, Address
1111
from ethereum_test_forks import Fork
1212
from ethereum_test_specs import BlockchainTestFiller
13-
from ethereum_test_specs.blockchain import Block, Header
13+
from ethereum_test_specs.blockchain import Block
1414
from ethereum_test_types import Alloc, Transaction
1515

1616

17+
class DeploymentTestType(Enum):
18+
"""Represents the type of deployment test."""
19+
20+
DEPLOY_BEFORE_FORK = "deploy_before_fork"
21+
DEPLOY_AFTER_FORK = "deploy_after_fork"
22+
23+
1724
class SystemContractDeployTestFunction(Protocol):
1825
"""
1926
Represents a function to be decorated with the `generate_system_contract_deploy_test`
@@ -25,33 +32,31 @@ def __call__(
2532
*,
2633
fork: Fork,
2734
pre: Alloc,
28-
) -> Generator[Tuple[Transaction | None, Header | None], None, None]:
35+
post: Alloc,
36+
test_type: DeploymentTestType,
37+
) -> Generator[Block, None, None]:
2938
"""
3039
Args:
3140
fork (Fork): The fork to test.
3241
pre (Alloc): The pre state of the blockchain.
42+
post (Alloc): The post state of the blockchain.
43+
test_type (DeploymentTestType): The type of deployment test currently being filled.
3344
3445
Yields:
35-
Tuple[Transaction | None, Header | None]: Once per block to add after the contract is
36-
deployed, with a single transaction to execute and the header object used to
37-
verify the block.
46+
Block: To add after the block where the contract was deployed (e.g. can contain extra
47+
transactions to execute after the system contract has been deployed, and/or a header
48+
object to verify that the headers are correct).
3849
3950
"""
4051
...
4152

4253

43-
class DeploymentTestType(Enum):
44-
"""Represents the type of deployment test."""
45-
46-
DEPLOY_BEFORE_FORK = "deploy_before_fork"
47-
DEPLOY_AFTER_FORK = "deploy_after_fork"
48-
49-
5054
def generate_system_contract_deploy_test(
55+
*,
5156
fork: Fork,
5257
tx_json_path: Path,
5358
expected_deploy_address: Address,
54-
expected_system_contract_storage: Dict | None,
59+
expected_system_contract_storage: Dict | None = None,
5560
):
5661
"""
5762
Generate a test that verifies the correct deployment of a system contract.
@@ -66,7 +71,8 @@ def generate_system_contract_deploy_test(
6671
contract.
6772
Providing a JSON file is useful to copy-paste the transaction from the EIP.
6873
expected_deploy_address (Address): The expected address of the deployed contract.
69-
expected_system_contract_storage (Dict): The expected storage of the system contract.
74+
expected_system_contract_storage (Dict | None): The expected storage of the system
75+
contract.
7076
7177
"""
7278
with open(tx_json_path, mode="r") as f:
@@ -126,18 +132,6 @@ def wrapper(
126132
),
127133
]
128134

129-
for tx_header_verify in func(fork=fork, pre=pre):
130-
txs = []
131-
if tx_header_verify[0] is not None:
132-
txs.append(tx_header_verify[0])
133-
header_verify = tx_header_verify[1]
134-
blocks.append(
135-
Block(
136-
txs=txs,
137-
header_verify=header_verify,
138-
)
139-
)
140-
141135
pre[expected_deploy_address] = Account(
142136
code=b"", # Remove the code that is automatically allocated on the fork
143137
nonce=0,
@@ -149,7 +143,7 @@ def wrapper(
149143

150144
expected_deploy_address_int = int.from_bytes(expected_deploy_address, "big")
151145

152-
post = {}
146+
post = Alloc()
153147
fork_pre_allocation = fork.pre_allocation_blockchain()
154148
assert expected_deploy_address_int in fork_pre_allocation
155149
expected_code = fork_pre_allocation[expected_deploy_address_int]["code"]
@@ -167,6 +161,11 @@ def wrapper(
167161
post[deployer_address] = Account(
168162
nonce=1,
169163
)
164+
165+
# Extra blocks (if any) returned by the decorated function to add after the
166+
# contract is deployed.
167+
blocks += list(func(fork=fork, pre=pre, post=post, test_type=test_type))
168+
170169
blockchain_test(
171170
pre=pre,
172171
blocks=blocks,
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
@@ -11,7 +11,7 @@ class ReferenceSpec:
1111
version: str
1212

1313

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

1616

1717
@dataclass(frozen=True)
@@ -22,6 +22,6 @@ class Spec:
2222
"""
2323

2424
FORK_TIMESTAMP = 15_000
25-
HISTORY_STORAGE_ADDRESS = 0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E
26-
HISTORY_SERVE_WINDOW = 8192
25+
HISTORY_STORAGE_ADDRESS = 0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC
26+
HISTORY_SERVE_WINDOW = 8191
2727
BLOCKHASH_OLD_WINDOW = 256

tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,9 @@ def test_block_hashes_history(
311311
@pytest.mark.parametrize(
312312
"block_number,reverts",
313313
[
314-
pytest.param(1, False, id="current_block"),
315-
pytest.param(2, False, id="future_block"),
316-
pytest.param(2**64 - 1, False, id="2**64-1"),
314+
pytest.param(1, True, id="current_block"),
315+
pytest.param(2, True, id="future_block"),
316+
pytest.param(2**64 - 1, True, id="2**64-1"),
317317
pytest.param(2**64, True, id="2**64"),
318318
],
319319
)
@@ -337,13 +337,24 @@ def test_invalid_history_contract_calls(
337337
returned_block_hash_slot = storage.store_next(0)
338338
block_hash_opcode_slot = storage.store_next(0)
339339

340+
return_offset = 64
341+
return_size = 32
342+
args_size = 32
343+
340344
# Check the first block outside of the window if any
341345
code = (
342346
Op.MSTORE(0, block_number)
343347
+ Op.SSTORE(
344-
return_code_slot, Op.CALL(Op.GAS, Spec.HISTORY_STORAGE_ADDRESS, 0, 0, 32, 32, 64)
348+
return_code_slot,
349+
Op.CALL(
350+
address=Spec.HISTORY_STORAGE_ADDRESS,
351+
args_offset=0,
352+
args_size=args_size,
353+
ret_offset=return_offset,
354+
ret_size=return_size,
355+
),
345356
)
346-
+ Op.SSTORE(returned_block_hash_slot, Op.MLOAD(32))
357+
+ Op.SSTORE(returned_block_hash_slot, Op.MLOAD(return_offset))
347358
+ Op.SSTORE(block_hash_opcode_slot, Op.BLOCKHASH(block_number))
348359
)
349360
check_contract_address = pre.deploy_contract(code, storage=storage.canary())
@@ -364,3 +375,63 @@ def test_invalid_history_contract_calls(
364375
post=post,
365376
reverts=reverts,
366377
)
378+
379+
380+
@pytest.mark.parametrize(
381+
"args_size,reverts",
382+
[
383+
pytest.param(0, True, id="zero_size"),
384+
pytest.param(33, True, id="too_large"),
385+
pytest.param(31, True, id="too_small"),
386+
],
387+
)
388+
@pytest.mark.valid_from("Prague")
389+
def test_invalid_history_contract_calls_input_size(
390+
blockchain_test: BlockchainTestFiller,
391+
pre: Alloc,
392+
reverts: bool,
393+
args_size: int,
394+
):
395+
"""Test calling the history contract with invalid input sizes."""
396+
storage = Storage()
397+
398+
return_code_slot = storage.store_next(not reverts, "history storage call result")
399+
returned_block_hash_slot = storage.store_next(0)
400+
401+
return_offset = 64
402+
return_size = 32
403+
block_number = 0
404+
405+
# Check the first block outside of the window if any
406+
code = (
407+
Op.MSTORE(0, block_number)
408+
+ Op.SSTORE(
409+
return_code_slot,
410+
Op.CALL(
411+
address=Spec.HISTORY_STORAGE_ADDRESS,
412+
args_offset=0,
413+
args_size=args_size,
414+
ret_offset=return_offset,
415+
ret_size=return_size,
416+
),
417+
)
418+
+ Op.SSTORE(returned_block_hash_slot, Op.MLOAD(return_offset))
419+
)
420+
check_contract_address = pre.deploy_contract(code, storage=storage.canary())
421+
422+
txs = [
423+
Transaction(
424+
to=check_contract_address,
425+
gas_limit=10_000_000,
426+
sender=pre.fund_eoa(),
427+
)
428+
]
429+
post = {check_contract_address: Account(storage=storage)}
430+
431+
blocks = [Block(txs=txs)]
432+
blockchain_test(
433+
pre=pre,
434+
blocks=blocks,
435+
post=post,
436+
reverts=reverts,
437+
)

0 commit comments

Comments
 (0)