Skip to content

Commit e43edba

Browse files
committed
Contract opcodes to compare gas cost for each precompile
Check each fork for valid precompiles Only verify absence of precompile for one address
1 parent b22e901 commit e43edba

File tree

3 files changed

+239
-0
lines changed

3 files changed

+239
-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,97 @@
1+
"""abstract: Test Calling Valid and Invalid Precompile Addresses."""
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+
Return the precompile addresses for the given fork.
22+
23+
Args:
24+
fork (str): The fork name.
25+
26+
Returns:
27+
list[tuple[str, bool]]: The precompile addresses and whether they exist.
28+
29+
"""
30+
valid_precompiles = fork.precompiles()
31+
32+
for address in range(1, len(valid_precompiles) + 2):
33+
yield (hex(address), address in valid_precompiles)
34+
35+
36+
@pytest.mark.valid_from("Byzantium")
37+
@pytest.mark.parametrize_by_fork("address,precompile_exists", precompile_addresses)
38+
def test_precompiles(
39+
state_test: StateTestFiller, address: str, precompile_exists: bool, pre: Alloc
40+
):
41+
"""Test the MODEXP precompile."""
42+
env = Environment()
43+
44+
args_offset = 0x1000
45+
args_size = 0x20
46+
output_offset = 0x2000
47+
output_size = 0x20
48+
49+
gas_test = 0x00
50+
gas_10000 = 0x20
51+
52+
account = pre.deploy_contract(
53+
Op.MSTORE(gas_test, Op.GAS)
54+
+ Op.CALL(
55+
address=address,
56+
args_offset=args_offset,
57+
args_size=args_size,
58+
output_offset=output_offset,
59+
output_size=output_size,
60+
)
61+
+ Op.MSTORE(gas_test, Op.SUB(Op.GAS, Op.MLOAD(gas_test)))
62+
+ Op.MSTORE(gas_10000, Op.GAS)
63+
+ Op.CALL(
64+
address=0x10000,
65+
args_offset=args_offset,
66+
args_size=args_size,
67+
output_offset=output_offset,
68+
output_size=output_size,
69+
)
70+
+ Op.MSTORE(gas_10000, Op.SUB(Op.GAS, Op.MLOAD(gas_10000)))
71+
+ Op.SSTORE(
72+
0,
73+
Op.LT(
74+
Conditional(
75+
condition=Op.GT(Op.MLOAD(gas_test), Op.MLOAD(gas_10000)),
76+
if_true=Op.SUB(Op.MLOAD(gas_test), Op.MLOAD(gas_10000)),
77+
if_false=Op.SUB(Op.MLOAD(gas_10000), Op.MLOAD(gas_test)),
78+
),
79+
0x1A4,
80+
),
81+
)
82+
+ Op.STOP,
83+
storage={0: 0xDEADBEEF},
84+
)
85+
86+
tx = Transaction(
87+
to=account,
88+
sender=pre.fund_eoa(),
89+
gas_limit=1_000_000,
90+
protected=True,
91+
)
92+
93+
# A high gas cost will result from calling a precompile
94+
# Expect 0x00 when a precompile exists at the address, 0x01 otherwise
95+
post = {account: Account(storage={0: "0x00" if precompile_exists else "0x01"})}
96+
97+
state_test(env=env, pre=pre, post=post, tx=tx)

0 commit comments

Comments
 (0)