Skip to content

Commit 1ef4d38

Browse files
committed
Identity precompile test conversions
Add returndatasize test cases Add tests for CALL Add tests for large inputs/outputs Add CALLCODE tests
1 parent 823718a commit 1ef4d38

File tree

8 files changed

+356
-2
lines changed

8 files changed

+356
-2
lines changed

Diff for: converted-ethereum-tests.txt

+16-1
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,25 @@ GeneralStateTests/VMTests/vmTests/push.json
100100
([#1067](https://github.com/ethereum/execution-spec-tests/pull/1067))
101101
GeneralStateTests/stPreCompiledContracts/blake2B.json
102102

103-
([#1067](https://github.com/ethereum/execution-spec-tests/pull/1120))
103+
([#1120](https://github.com/ethereum/execution-spec-tests/pull/1120))
104104
GeneralStateTests/stPreCompiledContracts/idPrecomps.json
105105

106106
([#1244](https://github.com/ethereum/execution-spec-tests/pull/1244))
107107
GeneralStateTests/stPreCompiledContracts/delegatecall09Undefined.json
108108
GeneralStateTests/stPreCompiledContracts2/CALLBlake2f.json
109109
GeneralStateTests/stPreCompiledContracts2/CALLCODEBlake2f.json
110+
111+
([#1344](https://github.com/ethereum/execution-spec-tests/pull/1344))
112+
GeneralStateTests/stPreCompiledContracts/identity_to_bigger.json
113+
GeneralStateTests/stPreCompiledContracts/identity_to_smaller.json
114+
GeneralStateTests/stPreCompiledContracts2/CallIdentitiy_0.json
115+
GeneralStateTests/stPreCompiledContracts2/CallIdentitiy_1.json
116+
GeneralStateTests/stPreCompiledContracts2/CallIdentity_1_nonzeroValue.json
117+
GeneralStateTests/stPreCompiledContracts2/CallIdentity_2.json
118+
GeneralStateTests/stPreCompiledContracts2/CallIdentity_3.json
119+
GeneralStateTests/stPreCompiledContracts2/CallIdentity_4.json
120+
GeneralStateTests/stPreCompiledContracts2/CallIdentity_4_gas17.json
121+
GeneralStateTests/stPreCompiledContracts2/CallIdentity_4_gas18.json
122+
GeneralStateTests/stPreCompiledContracts2/CallIdentity_5.json
123+
GeneralStateTests/stPreCompiledContracts2/CallIdentity_6_inputShorterThanOutput.json
124+

Diff for: docs/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ consume cache --help
6868
- ✨ Add EIP-7702 incorrect-rlp-encoding tests ([#1347](https://github.com/ethereum/execution-spec-tests/pull/1347)).
6969
- ✨ Add EIP-2935 tests for all call opcodes ([#1379](https://github.com/ethereum/execution-spec-tests/pull/1379)).
7070
- ✨ 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)).
71+
- ✨ Add test cases Identity precompiles ([#1344](https://github.com/ethereum/execution-spec-tests/pull/1344)).
7172

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

@@ -107,6 +108,7 @@ The following changes may be potentially breaking (all clients were tested with
107108
- ✨ Add EIP-7698 failed nonce and short data tests ([#1211](https://github.com/ethereum/execution-spec-tests/pull/1211)).
108109
- ✨ 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)).
109110
- ✨ 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)).
111+
- ✨ Add test cases for EIP-152 Blake2 and Identity precompiles ([#1244](https://github.com/ethereum/execution-spec-tests/pull/1244)).
110112

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

Diff for: tests/frontier/identity_precompile/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Test for precompiles that apply for all forks starting from Frontier."""

Diff for: tests/frontier/identity_precompile/common.py

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""Common utilities for the Identity Precompile tests."""
2+
3+
from dataclasses import dataclass
4+
from typing import Tuple
5+
6+
from ethereum_test_base_types.composite_types import Storage
7+
from ethereum_test_tools import (
8+
Bytecode,
9+
)
10+
from ethereum_test_tools import Opcodes as Op
11+
12+
13+
@dataclass(frozen=True)
14+
class Constants:
15+
"""Constants for the Identity Precompile."""
16+
17+
IDENTITY_PRECOMPILE_ADDRESS = 0x04
18+
19+
20+
class CallArgs:
21+
"""Defines inputs to CALL for the Identity precompile."""
22+
23+
def __init__(
24+
self,
25+
gas=0x1F4,
26+
value=0x0,
27+
args_offset=0x0,
28+
args_size=0x20,
29+
ret_offset=0x0,
30+
ret_size=0x20,
31+
):
32+
"""Create a new instance with the provided values."""
33+
self.gas = gas
34+
self.value = value
35+
self.args_offset = args_offset
36+
self.args_size = args_size
37+
self.ret_offset = ret_offset
38+
self.ret_size = ret_size
39+
40+
41+
def generate_identity_call_bytecode(
42+
storage: Storage,
43+
call_type: Op,
44+
memory_values: Tuple[int, ...],
45+
call_args: CallArgs,
46+
call_succeeds: bool,
47+
) -> Bytecode:
48+
"""
49+
Generate bytecode for calling the identity precompile with given memory values.
50+
51+
Args:
52+
storage (Storage): The storage object to use for storing values.
53+
call_type (Op): The type of call opcode (CALL or CALLCODE).
54+
memory_values (Tuple[int, ...]): Values to store in memory before the call.
55+
call_args (CallArgs): Arguments for the CALL opcode.
56+
call_succeeds (bool): Whether the call should succeed or not.
57+
58+
Returns:
59+
Bytecode: The generated bytecode for the identity precompile call.
60+
61+
"""
62+
code = Bytecode()
63+
64+
# Store provided values in memory
65+
mstore_count = len(memory_values) if memory_values else 0
66+
mstore_offset = 0
67+
mstore_value = 0
68+
if mstore_count:
69+
for i, value in enumerate(memory_values):
70+
mstore_value = value
71+
code += Op.MSTORE(mstore_offset, mstore_value)
72+
if mstore_count > i + 1:
73+
mstore_offset += 0x20
74+
75+
# Call the identity precompile, then check that the last value in memory has not changed
76+
code += (
77+
Op.SSTORE(
78+
storage.store_next(call_succeeds),
79+
call_type(
80+
gas=call_args.gas,
81+
address=Constants.IDENTITY_PRECOMPILE_ADDRESS,
82+
value=call_args.value,
83+
args_offset=call_args.args_offset,
84+
args_size=call_args.args_size,
85+
ret_offset=call_args.ret_offset,
86+
ret_size=call_args.ret_size,
87+
),
88+
)
89+
+ Op.SSTORE(storage.store_next(mstore_value), Op.MLOAD(mstore_offset))
90+
+ Op.STOP
91+
)
92+
93+
return code

Diff for: tests/frontier/identity_precompile/conftest.py

+9
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

Diff for: tests/frontier/identity_precompile/test_identity.py

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
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.valid_from("Byzantium")
21+
@pytest.mark.parametrize("call_type", [Op.CALL, Op.CALLCODE])
22+
@pytest.mark.parametrize(
23+
[
24+
"call_args",
25+
"memory_values",
26+
"call_succeeds",
27+
],
28+
[
29+
pytest.param(CallArgs(gas=0xFF), (0x1,), True, id="identity_0"),
30+
pytest.param(
31+
CallArgs(args_size=0x0),
32+
(0x0,),
33+
True,
34+
id="identity_1",
35+
),
36+
pytest.param(
37+
CallArgs(gas=0x30D40, value=0x13, args_size=0x0),
38+
None,
39+
False,
40+
id="identity_1_nonzero",
41+
),
42+
pytest.param(
43+
CallArgs(args_size=0x25),
44+
(0xF34578907F,),
45+
True,
46+
id="identity_2",
47+
),
48+
pytest.param(
49+
CallArgs(args_size=0x25),
50+
(0xF34578907F,),
51+
True,
52+
id="identity_3",
53+
),
54+
pytest.param(
55+
CallArgs(gas=0x64),
56+
(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,),
57+
True,
58+
id="identity_4",
59+
),
60+
pytest.param(
61+
CallArgs(gas=0x11),
62+
(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,),
63+
False,
64+
id="identity_4_gas17",
65+
),
66+
pytest.param(
67+
CallArgs(gas=0x12),
68+
(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,),
69+
True,
70+
id="identity_4_gas18",
71+
),
72+
],
73+
)
74+
def test_call_identity_precompile(
75+
state_test: StateTestFiller,
76+
pre: Alloc,
77+
call_type: Op,
78+
call_args: CallArgs,
79+
memory_values: Tuple[int, ...],
80+
call_succeeds: bool,
81+
tx_gas_limit: int,
82+
):
83+
"""Test identity precompile RETURNDATA is sized correctly based on the input size."""
84+
env = Environment()
85+
storage = Storage()
86+
87+
contract_bytecode = generate_identity_call_bytecode(
88+
storage,
89+
call_type,
90+
memory_values,
91+
call_args,
92+
call_succeeds,
93+
)
94+
95+
account = pre.deploy_contract(
96+
contract_bytecode,
97+
storage=storage.canary(),
98+
)
99+
100+
tx = Transaction(
101+
to=account,
102+
sender=pre.fund_eoa(),
103+
gas_limit=tx_gas_limit,
104+
)
105+
106+
post = {account: Account(storage=storage)}
107+
108+
state_test(env=env, pre=pre, post=post, tx=tx)
109+
110+
111+
@pytest.mark.valid_from("Byzantium")
112+
@pytest.mark.parametrize("call_type", [Op.CALL, Op.CALLCODE])
113+
@pytest.mark.parametrize(
114+
[
115+
"call_args",
116+
"memory_values",
117+
"call_succeeds",
118+
],
119+
[
120+
pytest.param(
121+
CallArgs(gas=0x258, args_size=0xF4240),
122+
(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,),
123+
False,
124+
id="identity_5",
125+
),
126+
pytest.param(
127+
CallArgs(gas=0x258, ret_size=0x40),
128+
(
129+
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
130+
0x1234,
131+
),
132+
True,
133+
id="identity_6",
134+
),
135+
],
136+
)
137+
@pytest.mark.parametrize("tx_gas_limit", [10_000_000])
138+
def test_call_identity_precompile_large_params(
139+
state_test: StateTestFiller,
140+
pre: Alloc,
141+
call_type: Op,
142+
call_args: CallArgs,
143+
memory_values: Tuple[int, ...],
144+
call_succeeds: bool,
145+
tx_gas_limit: int,
146+
):
147+
"""Test identity precompile when out of gas occurs."""
148+
env = Environment()
149+
storage = Storage()
150+
151+
contract_bytecode = generate_identity_call_bytecode(
152+
storage,
153+
call_type,
154+
memory_values,
155+
call_args,
156+
call_succeeds,
157+
)
158+
159+
account = pre.deploy_contract(
160+
contract_bytecode,
161+
storage=storage.canary(),
162+
)
163+
164+
tx = Transaction(
165+
to=account,
166+
sender=pre.fund_eoa(),
167+
gas_limit=tx_gas_limit,
168+
)
169+
170+
post = {account: Account(storage=storage)}
171+
172+
state_test(env=env, pre=pre, post=post, tx=tx)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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.valid_from("Byzantium")
18+
@pytest.mark.parametrize(
19+
["args_size", "output_size", "expected_returndatasize"],
20+
[
21+
pytest.param(16, 32, 16, id="output_16"),
22+
pytest.param(32, 16, 32, id="output_32"),
23+
],
24+
)
25+
def test_identity_precompile_returndata(
26+
state_test: StateTestFiller,
27+
pre: Alloc,
28+
args_size: int,
29+
output_size: int,
30+
expected_returndatasize: int,
31+
):
32+
"""Test identity precompile RETURNDATA is sized correctly based on the input size."""
33+
env = Environment()
34+
storage = Storage()
35+
36+
account = pre.deploy_contract(
37+
Op.MSTORE(0, 0)
38+
+ Op.GAS
39+
+ Op.MSTORE(0, 0x112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF00)
40+
+ Op.POP(
41+
Op.CALL(
42+
address=Constants.IDENTITY_PRECOMPILE_ADDRESS,
43+
args_offset=0,
44+
args_size=args_size,
45+
output_offset=0x10,
46+
output_size=output_size,
47+
)
48+
)
49+
+ Op.SSTORE(storage.store_next(expected_returndatasize), Op.RETURNDATASIZE)
50+
+ Op.STOP,
51+
storage=storage.canary(),
52+
)
53+
54+
tx = Transaction(
55+
to=account,
56+
sender=pre.fund_eoa(),
57+
gas_limit=200_000,
58+
protected=True,
59+
)
60+
61+
post = {account: Account(storage=storage)}
62+
63+
state_test(env=env, pre=pre, post=post, tx=tx)

Diff for: tests/istanbul/eip152_blake2/test_blake2.py

-1
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,6 @@ def test_blake2b(
406406
"""Test BLAKE2b precompile."""
407407
env = Environment()
408408

409-
account = pre.deploy_contract(blake2b_contract_bytecode, storage={0: 0xDEADBEEF})
410409
account = pre.deploy_contract(blake2b_contract_bytecode, storage={0: 0xDEADBEEF})
411410
sender = pre.fund_eoa()
412411

0 commit comments

Comments
 (0)