Skip to content

Commit 474d1c1

Browse files
committed
Check each fork for supported precompiles.
Contract compares gas cost for each precompile. Only checks a single unsupported precompile address.
1 parent b22e901 commit 474d1c1

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""Tests supported precompiled contracts."""
2+
3+
from typing import Iterator, Tuple
4+
5+
import pytest
6+
7+
from ethereum_test_forks import Fork
8+
from ethereum_test_tools import (
9+
Account,
10+
Alloc,
11+
Environment,
12+
StateTestFiller,
13+
Transaction,
14+
)
15+
from ethereum_test_tools.code.generators import Conditional
16+
from ethereum_test_tools.vm.opcode import Opcodes as Op
17+
18+
UPPER_BOUND = 0x101
19+
NUM_UNSUPPORTED_PRECOMPILES = 1
20+
21+
22+
def precompile_addresses(fork: Fork) -> Iterator[Tuple[str, bool]]:
23+
"""
24+
Yield the addresses of precompiled contracts and their support status for a given fork.
25+
26+
Args:
27+
fork (Fork): The fork instance containing precompiled contract information.
28+
29+
Yields:
30+
Iterator[Tuple[str, bool]]: A tuple containing the address in hexadecimal format and a
31+
boolean indicating whether the address is a supported precompile.
32+
33+
"""
34+
supported_precompiles = fork.precompiles()
35+
36+
num_unsupported = NUM_UNSUPPORTED_PRECOMPILES
37+
for address in range(1, UPPER_BOUND + 1):
38+
if address in supported_precompiles:
39+
yield (hex(address), True)
40+
elif num_unsupported > 0:
41+
# Check unsupported precompiles up to NUM_UNSUPPORTED_PRECOMPILES
42+
yield (hex(address), False)
43+
num_unsupported -= 1
44+
45+
46+
@pytest.mark.valid_from("Berlin")
47+
@pytest.mark.parametrize_by_fork("address,precompile_exists", precompile_addresses)
48+
def test_precompiles(
49+
state_test: StateTestFiller, address: str, precompile_exists: bool, pre: Alloc
50+
):
51+
"""
52+
Tests the behavior of precompiled contracts in the Ethereum state test.
53+
54+
Args:
55+
state_test (StateTestFiller): The state test filler object used to run the test.
56+
address (str): The address of the precompiled contract to test.
57+
precompile_exists (bool): A flag indicating whether the precompiled contract exists at the
58+
given address.
59+
pre (Alloc): The allocation object used to deploy the contract and set up the initial
60+
state.
61+
62+
This test deploys a contract that performs two CALL operations to the specified address and a
63+
fixed address (0x10000), measuring the gas used for each call. It then stores the difference
64+
in gas usage in storage slot 0. The test verifies the expected storage value based on
65+
whether the precompiled contract exists at the given address.
66+
67+
"""
68+
env = Environment()
69+
70+
account = pre.deploy_contract(
71+
Op.MSTORE(0x00, Op.GAS)
72+
+ Op.CALL(address=address)
73+
+ Op.MSTORE(0x00, Op.SUB(Op.MLOAD(0x00), Op.GAS))
74+
+ Op.MSTORE(0x20, Op.GAS)
75+
+ Op.CALL(address=0x10000)
76+
+ Op.MSTORE(0x20, Op.SUB(Op.MLOAD(0x20), Op.GAS))
77+
+ Op.SSTORE(
78+
0,
79+
Op.LT(
80+
Conditional(
81+
condition=Op.GT(Op.MLOAD(0x00), Op.MLOAD(0x20)),
82+
if_true=Op.SUB(Op.MLOAD(0x00), Op.MLOAD(0x20)),
83+
if_false=Op.SUB(Op.MLOAD(0x20), Op.MLOAD(0x00)),
84+
),
85+
0x1A4,
86+
),
87+
)
88+
+ Op.STOP,
89+
storage={0: 0xDEADBEEF},
90+
)
91+
92+
tx = Transaction(
93+
to=account,
94+
sender=pre.fund_eoa(),
95+
gas_limit=1_000_000,
96+
protected=True,
97+
)
98+
99+
# A high gas cost will result from calling a precompile
100+
# Expect 0x00 when a precompile exists at the address, 0x01 otherwise
101+
post = {account: Account(storage={0: "0x00" if precompile_exists else "0x01"})}
102+
103+
state_test(env=env, pre=pre, post=post, tx=tx)

0 commit comments

Comments
 (0)