Skip to content

Commit 473e19f

Browse files
committed
Contract opcodes to compare gas cost for each precompile
Include all test cases and expect precompiles at addresses 01-09 Only include case 0A and 0B to verify no precompile in all forks (except cancun)
1 parent be1d1c9 commit 473e19f

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import pytest
2+
3+
from ethereum_test_tools import (
4+
Account,
5+
Address,
6+
Alloc,
7+
Environment,
8+
StateTestFiller,
9+
Transaction,
10+
)
11+
from ethereum_test_tools.vm.opcode import Opcodes as Op
12+
13+
14+
def precompile_addresses(fork):
15+
"""
16+
Return the precompile addresses for the given fork.
17+
18+
Args:
19+
fork (str): The fork name.
20+
21+
Returns:
22+
list[tuple[str, bool]]: The precompile addresses and whether they exist.
23+
24+
"""
25+
return [
26+
("01", True),
27+
("02", True),
28+
("03", True),
29+
("04", True),
30+
("05", True),
31+
("06", True),
32+
("07", True),
33+
("08", True),
34+
("09", True),
35+
("0A", True) if fork == "cancun" else ("0A", False),
36+
("0B", False),
37+
]
38+
39+
40+
@pytest.mark.parametrize_by_fork("address,precompile_exists", precompile_addresses)
41+
def test_precompiles(
42+
state_test: StateTestFiller, pre: Alloc, address: str, precompile_exists: bool
43+
):
44+
"""Test the MODEXP precompile."""
45+
env = Environment()
46+
47+
address_10000 = 0x10000
48+
49+
input_buf = 0x1000
50+
output_buf = 0x2000
51+
52+
args_offset = 0x1000
53+
args_size = 0x20
54+
output_offset = 0x2000
55+
output_size = 0x20
56+
57+
gas_test = 0x00
58+
gas_10000 = 0x20
59+
60+
sender = pre.fund_eoa()
61+
account = pre.deploy_contract(
62+
Op.MSTORE(args_size, 0x20)
63+
+ Op.MSTORE(input_buf, 0xFF)
64+
+ Op.MSTORE(output_buf, 0xFF)
65+
+ Op.MSTORE8(input_buf, 0xFF)
66+
+ Op.MSTORE8(output_buf, 0xFF)
67+
+ Op.MSTORE(gas_test, Op.GAS())
68+
# Setup stack to CALL into precompile with the CALLDATA and CALL into it (+ pop value)
69+
+ Op.CALL(
70+
address=Op.CALLDATALOAD(0),
71+
args_offset=args_offset,
72+
args_size=args_size,
73+
output_offset=output_offset,
74+
output_size=output_size,
75+
)
76+
+ Op.MSTORE(gas_test, (Op.SUB(Op.GAS(), Op.MLOAD(gas_test))))
77+
+ Op.MSTORE(gas_10000, Op.GAS())
78+
+ Op.CALL(
79+
address=address_10000,
80+
args_offset=args_offset,
81+
args_size=args_size,
82+
output_offset=output_offset,
83+
output_size=output_size,
84+
)
85+
+ Op.MSTORE(gas_10000, (Op.SUB(Op.GAS(), Op.MLOAD(gas_10000))))
86+
+ Op.JUMPI(0x7D, Op.GT(Op.MLOAD(gas_test), Op.MLOAD(gas_10000)))
87+
+ Op.JUMPDEST()
88+
+ Op.SSTORE(0, Op.SUB(Op.MLOAD(gas_test), Op.MLOAD(gas_10000)))
89+
+ Op.SSTORE(0, Op.LT(Op.SLOAD(gas_test), 0x10))
90+
+ Op.STOP(),
91+
)
92+
93+
tx = Transaction(
94+
ty=0x0,
95+
to=account,
96+
data=int(address, 16).to_bytes(32),
97+
gas_limit=100000,
98+
gas_price=10,
99+
protected=True,
100+
sender=sender,
101+
)
102+
103+
# A high gas cost will result from calling a precompile
104+
# Expect 0x00 when a precompile exists at the address, 0x01 otherwise
105+
post = {account: Account(storage={0: "0x00" if precompile_exists else "0x01"})}
106+
107+
state_test(env=env, pre=pre, post=post, tx=tx)

0 commit comments

Comments
 (0)