Skip to content

Commit cc74bbb

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 cc74bbb

File tree

3 files changed

+256
-0
lines changed

3 files changed

+256
-0
lines changed

.vscode/launch.json

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
// If the VS Code "Run and Debug" button, respecively launch selector are not visible, see this answer:
6+
// https://stackoverflow.com/a/74245823
7+
//
8+
"version": "0.2.0",
9+
"configurations": [
10+
{
11+
"name": "Launch fill --until choose fork",
12+
"type": "python",
13+
"request": "launch",
14+
"module": "pytest",
15+
"args": [
16+
"-c",
17+
"pytest.ini",
18+
"--until",
19+
"${input:fork}",
20+
"--evm-bin",
21+
"${input:evmBinary}",
22+
"-v"
23+
],
24+
"cwd": "${workspaceFolder}"
25+
},
26+
{
27+
"name": "Launch fill -k choose test path",
28+
"type": "python",
29+
"request": "launch",
30+
"module": "pytest",
31+
"args": [
32+
"-c",
33+
"pytest.ini",
34+
"--until",
35+
"Cancun",
36+
"--evm-bin",
37+
"${input:evmBinary}",
38+
"-v",
39+
"-k",
40+
"${input:testPathOrId}"
41+
],
42+
"cwd": "${workspaceFolder}"
43+
},
44+
{
45+
"name": "Launch fill --until Cancun",
46+
"type": "python",
47+
"request": "launch",
48+
"module": "pytest",
49+
"args": [
50+
"-c",
51+
"pytest.ini",
52+
"--until",
53+
"Cancun",
54+
"--evm-bin",
55+
"${input:evmBinary}",
56+
"-v"
57+
],
58+
"cwd": "${workspaceFolder}"
59+
},
60+
{
61+
"name": "Launch fill --until Prague",
62+
"type": "python",
63+
"request": "launch",
64+
"module": "pytest",
65+
"args": [
66+
"-c",
67+
"pytest.ini",
68+
"--until",
69+
"Prague",
70+
"--evm-bin",
71+
"${input:evmBinary}",
72+
"-v"
73+
],
74+
"cwd": "${workspaceFolder}"
75+
}
76+
],
77+
"inputs": [
78+
{
79+
"type": "pickString",
80+
"id": "evmBinary",
81+
"description": "Which evm binary to you want to run?",
82+
"options": [
83+
{
84+
"label": "First evm binary in PATH",
85+
"value": "evm",
86+
},
87+
{
88+
"label": "Geth, mario's repo",
89+
"value": "~/code/github/marioevz/go-ethereum/build/bin/evm",
90+
},
91+
{
92+
"label": "Geth, danceratopz's repo",
93+
"value": "~/code/github/danceratopz/go-ethereum/build/bin/evm",
94+
},
95+
{
96+
"label": "evmone",
97+
"value": "~/code/github/ethereum/evmone/build/bin/evmone-t8n",
98+
},
99+
{
100+
"label": "besu",
101+
"value": "~/code/github/danceratopz/besu/ethereum/evmtool/build/install/evmtool/bin/evm",
102+
}
103+
],
104+
"default": "evm"
105+
},
106+
{
107+
"type": "pickString",
108+
"id": "fork",
109+
"description": "Which fork do you want to use?",
110+
"options": [
111+
"Frontier",
112+
"Homestead",
113+
"Byzantium",
114+
"Constantinople",
115+
"ConstantinopleFix",
116+
"Istanbul",
117+
"Berlin",
118+
"London",
119+
"Paris",
120+
"Shanghai",
121+
"Cancun",
122+
"Prague",
123+
],
124+
"default": "Cancun"
125+
},
126+
{
127+
"type": "promptString",
128+
"id": "testPathOrId",
129+
"description": "Enter a test path string or id to provide to pytest -k",
130+
"default": "test_"
131+
}
132+
]
133+
}

.vscode/settings.local.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"_comment": [
3+
"Dynamic python testing settings for the vscode testing extension."
4+
],
5+
"python.testing.promptToConfigure": false,
6+
"python.testing.unittestEnabled": false,
7+
"python.testing.pytestEnabled": true,
8+
"python.testing.pytestArgs": ["-c", "pytest.ini"]
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
19+
def precompile_addresses(fork: Fork) -> Iterator[Tuple[str, bool]]:
20+
"""
21+
Yield the addresses of precompiled contracts and their support status for a given fork.
22+
23+
Args:
24+
fork (Fork): The fork instance containing precompiled contract information.
25+
26+
Yields:
27+
Iterator[Tuple[str, bool]]: A tuple containing the address in hexadecimal format and a
28+
boolean indicating whether the address is a supported precompile.
29+
30+
"""
31+
supported_precompiles = fork.precompiles()
32+
33+
for address in range(1, len(supported_precompiles) + 2):
34+
yield (hex(address), address in supported_precompiles)
35+
36+
37+
@pytest.mark.valid_from("Berlin")
38+
@pytest.mark.parametrize_by_fork("address,precompile_exists", precompile_addresses)
39+
def test_precompiles(
40+
state_test: StateTestFiller, address: str, precompile_exists: bool, pre: Alloc
41+
):
42+
"""
43+
Tests the behavior of precompiled contracts in the Ethereum state test.
44+
45+
Args:
46+
state_test (StateTestFiller): The state test filler object used to run the test.
47+
address (str): The address of the precompiled contract to test.
48+
precompile_exists (bool): A flag indicating whether the precompiled contract exists at the
49+
given address.
50+
pre (Alloc): The allocation object used to deploy the contract and set up the initial
51+
state.
52+
53+
This test deploys a contract that performs two CALL operations to the specified address and a
54+
fixed address (0x10000), measuring the gas used for each call. It then stores the difference
55+
in gas usage in storage slot 0. The test verifies the expected storage value based on
56+
whether the precompiled contract exists at the given address.
57+
58+
"""
59+
env = Environment()
60+
61+
args_offset = 0x1000
62+
args_size = 0x20
63+
output_offset = 0x2000
64+
output_size = 0x20
65+
66+
gas_test = 0x00
67+
gas_10000 = 0x20
68+
69+
account = pre.deploy_contract(
70+
Op.MSTORE(gas_test, Op.GAS)
71+
+ Op.CALL(
72+
address=address,
73+
args_offset=args_offset,
74+
args_size=args_size,
75+
output_offset=output_offset,
76+
output_size=output_size,
77+
)
78+
+ Op.MSTORE(gas_test, Op.SUB(Op.GAS, Op.MLOAD(gas_test)))
79+
+ Op.MSTORE(gas_10000, Op.GAS)
80+
+ Op.CALL(
81+
address=0x10000,
82+
args_offset=args_offset,
83+
args_size=args_size,
84+
output_offset=output_offset,
85+
output_size=output_size,
86+
)
87+
+ Op.MSTORE(gas_10000, Op.SUB(Op.GAS, Op.MLOAD(gas_10000)))
88+
+ Op.SSTORE(
89+
0,
90+
Op.LT(
91+
Conditional(
92+
condition=Op.GT(Op.MLOAD(gas_test), Op.MLOAD(gas_10000)),
93+
if_true=Op.SUB(Op.MLOAD(gas_test), Op.MLOAD(gas_10000)),
94+
if_false=Op.SUB(Op.MLOAD(gas_10000), Op.MLOAD(gas_test)),
95+
),
96+
0x1A4,
97+
),
98+
)
99+
+ Op.STOP,
100+
storage={0: 0xDEADBEEF},
101+
)
102+
103+
tx = Transaction(
104+
to=account,
105+
sender=pre.fund_eoa(),
106+
gas_limit=1_000_000,
107+
protected=True,
108+
)
109+
110+
# A high gas cost will result from calling a precompile
111+
# Expect 0x00 when a precompile exists at the address, 0x01 otherwise
112+
post = {account: Account(storage={0: "0x00" if precompile_exists else "0x01"})}
113+
114+
state_test(env=env, pre=pre, post=post, tx=tx)

0 commit comments

Comments
 (0)