Skip to content

Commit 0157a4b

Browse files
Stuart Reedmarioevz
andauthored
feat(tests): Identity precompile test conversions (#1344)
* Identity precompile test conversions Add returndatasize test cases Add tests for CALL Add tests for large inputs/outputs Add CALLCODE tests * Updates based on feedback * Add identity precompile ported_from markers * fix ported_from paths * fix ported_from paths --------- Co-authored-by: Mario Vega <[email protected]>
1 parent 2556e6a commit 0157a4b

27 files changed

+360
-1271
lines changed

docs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ consume cache --help
243243
- ✨ Add EIP-7702 incorrect-rlp-encoding tests ([#1347](https://github.com/ethereum/execution-spec-tests/pull/1347)).
244244
- ✨ Add EIP-2935 tests for all call opcodes ([#1379](https://github.com/ethereum/execution-spec-tests/pull/1379)).
245245
- ✨ Add more tests for EIP-7702: max-fee-per-gas verification, delegation-designation as initcode tests ([#1372](https://github.com/ethereum/execution-spec-tests/pull/1372)).
246+
- ✨ Add converted Identity precompile tests ([#1344](https://github.com/ethereum/execution-spec-tests/pull/1344)).
246247

247248
## [v4.1.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v4.1.0) - 2025-03-11
248249

@@ -282,6 +283,7 @@ The following changes may be potentially breaking (all clients were tested with
282283
- ✨ Add EIP-7698 failed nonce and short data tests ([#1211](https://github.com/ethereum/execution-spec-tests/pull/1211)).
283284
- ✨ Add EIP-2537 additional pairing precompile tests cases, and then update all BLS12 test vectors ([#1275](https://github.com/ethereum/execution-spec-tests/pull/1275), [#1289](https://github.com/ethereum/execution-spec-tests/pull/1289)).
284285
- ✨ Add EIP-7685 and EIP-7002 test cases for additional request type combinations and modified withdrawal contract that allows more withdrawals ([#1340](https://github.com/ethereum/execution-spec-tests/pull/1340)).
286+
- ✨ Add test cases for EIP-152 Blake2 and Identity precompiles ([#1244](https://github.com/ethereum/execution-spec-tests/pull/1244)).
285287

286288
## [v4.0.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v4.0.0) - 2025-02-14 - 💕
287289

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Test for precompiles that apply for all forks starting from Frontier."""
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""Common utilities for the Identity Precompile tests."""
2+
3+
from dataclasses import dataclass
4+
from typing import Tuple
5+
6+
from pydantic import BaseModel
7+
8+
from ethereum_test_base_types.composite_types import Storage
9+
from ethereum_test_tools import (
10+
Bytecode,
11+
)
12+
from ethereum_test_tools import Opcodes as Op
13+
14+
15+
@dataclass(frozen=True)
16+
class Constants:
17+
"""Constants for the Identity Precompile."""
18+
19+
IDENTITY_PRECOMPILE_ADDRESS = 0x04
20+
21+
22+
class CallArgs(BaseModel):
23+
"""Defines inputs to CALL for the Identity precompile."""
24+
25+
address: int = Constants.IDENTITY_PRECOMPILE_ADDRESS
26+
gas: int = 0x1F4
27+
value: int = 0x0
28+
args_offset: int = 0x0
29+
args_size: int = 0x20
30+
ret_offset: int = 0x0
31+
ret_size: int = 0x20
32+
33+
34+
def generate_identity_call_bytecode(
35+
storage: Storage,
36+
call_type: Op,
37+
memory_values: Tuple[int, ...],
38+
call_args: CallArgs,
39+
call_succeeds: bool,
40+
) -> Bytecode:
41+
"""
42+
Generate bytecode for calling the identity precompile with given memory values.
43+
44+
Args:
45+
storage (Storage): The storage object to use for storing values.
46+
call_type (Op): The type of call opcode (CALL or CALLCODE).
47+
memory_values (Tuple[int, ...]): Values to store in memory before the call.
48+
call_args (CallArgs): Arguments for the CALL opcode.
49+
call_succeeds (bool): Whether the call should succeed or not.
50+
51+
Returns:
52+
Bytecode: The generated bytecode for the identity precompile call.
53+
54+
"""
55+
code = Bytecode()
56+
57+
# Store provided values in memory
58+
mstore_count = len(memory_values) if memory_values else 0
59+
mstore_offset = 0
60+
mstore_value = 0
61+
if mstore_count:
62+
for i, value in enumerate(memory_values):
63+
mstore_value = value
64+
code += Op.MSTORE(mstore_offset, mstore_value)
65+
if mstore_count > i + 1:
66+
mstore_offset += 0x20
67+
68+
# Call the identity precompile, then check that the last value in memory has not changed
69+
code += (
70+
Op.SSTORE(
71+
storage.store_next(call_succeeds),
72+
call_type(**call_args.model_dump()),
73+
)
74+
+ Op.SSTORE(storage.store_next(mstore_value), Op.MLOAD(mstore_offset))
75+
+ Op.STOP
76+
)
77+
78+
return code
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""Pytest (plugin) definitions local to Identity precompile tests."""
2+
3+
import pytest
4+
5+
6+
@pytest.fixture
7+
def tx_gas_limit() -> int:
8+
"""Return the gas limit for transactions."""
9+
return 365_224
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
"""abstract: Test identity precompile output size."""
2+
3+
from typing import Tuple
4+
5+
import pytest
6+
7+
from ethereum_test_base_types.composite_types import Storage
8+
from ethereum_test_tools import (
9+
Account,
10+
Alloc,
11+
Environment,
12+
StateTestFiller,
13+
Transaction,
14+
)
15+
from ethereum_test_tools import Opcodes as Op
16+
17+
from .common import CallArgs, generate_identity_call_bytecode
18+
19+
20+
@pytest.mark.ported_from(
21+
[
22+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CALLCODEIdentitiy_0Filler.json",
23+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CALLCODEIdentitiy_1Filler.json",
24+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CALLCODEIdentity_1_nonzeroValueFiller.json",
25+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CALLCODEIdentity_2Filler.json",
26+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CALLCODEIdentity_3Filler.json",
27+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CALLCODEIdentity_4Filler.json",
28+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CALLCODEIdentity_4_gas17Filler.json",
29+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CALLCODEIdentity_4_gas18Filler.json",
30+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CallIdentitiy_0Filler.json",
31+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CallIdentitiy_1Filler.json",
32+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CallIdentity_1_nonzeroValueFiller.json",
33+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CallIdentity_2Filler.json",
34+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CallIdentity_3Filler.json",
35+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CallIdentity_4Filler.json",
36+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CallIdentity_4_gas17Filler.json",
37+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CallIdentity_4_gas18Filler.json",
38+
],
39+
pr=["https://github.com/ethereum/execution-spec-tests/pull/1344"],
40+
)
41+
@pytest.mark.valid_from("Byzantium")
42+
@pytest.mark.parametrize("call_type", [Op.CALL, Op.CALLCODE])
43+
@pytest.mark.parametrize(
44+
[
45+
"call_args",
46+
"memory_values",
47+
"call_succeeds",
48+
],
49+
[
50+
pytest.param(CallArgs(gas=0xFF), (0x1,), True, id="identity_0"),
51+
pytest.param(
52+
CallArgs(args_size=0x0),
53+
(0x0,),
54+
True,
55+
id="identity_1",
56+
),
57+
pytest.param(
58+
CallArgs(gas=0x30D40, value=0x1, args_size=0x0),
59+
None,
60+
False,
61+
id="identity_1_nonzerovalue",
62+
),
63+
pytest.param(
64+
CallArgs(args_size=0x25),
65+
(0xF34578907F,),
66+
True,
67+
id="identity_2",
68+
),
69+
pytest.param(
70+
CallArgs(args_size=0x25),
71+
(0xF34578907F,),
72+
True,
73+
id="identity_3",
74+
),
75+
pytest.param(
76+
CallArgs(gas=0x64),
77+
(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,),
78+
True,
79+
id="identity_4",
80+
),
81+
pytest.param(
82+
CallArgs(gas=0x11),
83+
(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,),
84+
False,
85+
id="identity_4_insufficient_gas",
86+
),
87+
pytest.param(
88+
CallArgs(gas=0x12),
89+
(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,),
90+
True,
91+
id="identity_4_exact_gas",
92+
),
93+
],
94+
)
95+
def test_call_identity_precompile(
96+
state_test: StateTestFiller,
97+
pre: Alloc,
98+
call_type: Op,
99+
call_args: CallArgs,
100+
memory_values: Tuple[int, ...],
101+
call_succeeds: bool,
102+
tx_gas_limit: int,
103+
):
104+
"""Test identity precompile RETURNDATA is sized correctly based on the input size."""
105+
env = Environment()
106+
storage = Storage()
107+
108+
contract_bytecode = generate_identity_call_bytecode(
109+
storage,
110+
call_type,
111+
memory_values,
112+
call_args,
113+
call_succeeds,
114+
)
115+
116+
account = pre.deploy_contract(
117+
contract_bytecode,
118+
storage=storage.canary(),
119+
)
120+
121+
tx = Transaction(
122+
to=account,
123+
sender=pre.fund_eoa(),
124+
gas_limit=tx_gas_limit,
125+
)
126+
127+
post = {account: Account(storage=storage)}
128+
129+
state_test(env=env, pre=pre, post=post, tx=tx)
130+
131+
132+
@pytest.mark.ported_from(
133+
[
134+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CALLCODEIdentity_5Filler.json",
135+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts2/CallIdentity_5Filler.json",
136+
],
137+
pr=["https://github.com/ethereum/execution-spec-tests/pull/1344"],
138+
)
139+
@pytest.mark.valid_from("Byzantium")
140+
@pytest.mark.parametrize("call_type", [Op.CALL, Op.CALLCODE])
141+
@pytest.mark.parametrize(
142+
[
143+
"call_args",
144+
"memory_values",
145+
"call_succeeds",
146+
],
147+
[
148+
pytest.param(
149+
CallArgs(gas=0x258, args_size=0xF4240),
150+
(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,),
151+
False,
152+
id="identity_5",
153+
),
154+
pytest.param(
155+
CallArgs(gas=0x258, ret_size=0x40),
156+
(
157+
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
158+
0x1234,
159+
),
160+
True,
161+
id="identity_6",
162+
),
163+
],
164+
)
165+
@pytest.mark.parametrize("tx_gas_limit", [10_000_000])
166+
def test_call_identity_precompile_large_params(
167+
state_test: StateTestFiller,
168+
pre: Alloc,
169+
call_type: Op,
170+
call_args: CallArgs,
171+
memory_values: Tuple[int, ...],
172+
call_succeeds: bool,
173+
tx_gas_limit: int,
174+
):
175+
"""Test identity precompile when out of gas occurs."""
176+
env = Environment()
177+
storage = Storage()
178+
179+
contract_bytecode = generate_identity_call_bytecode(
180+
storage,
181+
call_type,
182+
memory_values,
183+
call_args,
184+
call_succeeds,
185+
)
186+
187+
account = pre.deploy_contract(
188+
contract_bytecode,
189+
storage=storage.canary(),
190+
)
191+
192+
tx = Transaction(
193+
to=account,
194+
sender=pre.fund_eoa(),
195+
gas_limit=tx_gas_limit,
196+
)
197+
198+
post = {account: Account(storage=storage)}
199+
200+
state_test(env=env, pre=pre, post=post, tx=tx)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""abstract: Test identity precompile output size."""
2+
3+
import pytest
4+
5+
from ethereum_test_base_types.composite_types import Storage
6+
from ethereum_test_tools import (
7+
Account,
8+
Alloc,
9+
Environment,
10+
StateTestFiller,
11+
Transaction,
12+
)
13+
from ethereum_test_tools import Opcodes as Op
14+
from tests.frontier.identity_precompile.common import Constants
15+
16+
17+
@pytest.mark.ported_from(
18+
[
19+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts/identity_to_biggerFiller.json",
20+
"https://github.com/ethereum/tests/blob/v17.1/src/GeneralStateTestsFiller/stPreCompiledContracts/identity_to_smallerFiller.json",
21+
],
22+
pr=["https://github.com/ethereum/execution-spec-tests/pull/1344"],
23+
)
24+
@pytest.mark.valid_from("Byzantium")
25+
@pytest.mark.parametrize(
26+
["args_size", "output_size", "expected_returndatasize"],
27+
[
28+
pytest.param(16, 32, 16, id="output_size_greater_than_input"),
29+
pytest.param(32, 16, 32, id="output_size_less_than_input"),
30+
],
31+
)
32+
def test_identity_precompile_returndata(
33+
state_test: StateTestFiller,
34+
pre: Alloc,
35+
args_size: int,
36+
output_size: int,
37+
expected_returndatasize: int,
38+
):
39+
"""Test identity precompile RETURNDATA is sized correctly based on the input size."""
40+
env = Environment()
41+
storage = Storage()
42+
43+
account = pre.deploy_contract(
44+
Op.MSTORE(0, 0)
45+
+ Op.GAS
46+
+ Op.MSTORE(0, 0x112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF00)
47+
+ Op.POP(
48+
Op.CALL(
49+
address=Constants.IDENTITY_PRECOMPILE_ADDRESS,
50+
args_offset=0,
51+
args_size=args_size,
52+
output_offset=0x10,
53+
output_size=output_size,
54+
)
55+
)
56+
+ Op.SSTORE(storage.store_next(expected_returndatasize), Op.RETURNDATASIZE)
57+
+ Op.STOP,
58+
storage=storage.canary(),
59+
)
60+
61+
tx = Transaction(
62+
to=account,
63+
sender=pre.fund_eoa(),
64+
gas_limit=200_000,
65+
protected=True,
66+
)
67+
68+
post = {account: Account(storage=storage)}
69+
70+
state_test(env=env, pre=pre, post=post, tx=tx)

0 commit comments

Comments
 (0)