Skip to content

Commit 7cafd0b

Browse files
committed
Merge eof/omnibus into eof/main
2 parents c47ae22 + c7d034e commit 7cafd0b

19 files changed

Lines changed: 1560 additions & 14 deletions

src/ethereum_test_tools/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
add_kzg_version,
3535
ceiling_division,
3636
compute_create2_address,
37-
compute_create3_address,
3837
compute_create_address,
38+
compute_eofcreate_address,
3939
copy_opcode_cost,
4040
cost_memory_bytes,
4141
eip_2028_transaction_data_cost,
@@ -110,7 +110,7 @@
110110
"ceiling_division",
111111
"compute_create_address",
112112
"compute_create2_address",
113-
"compute_create3_address",
113+
"compute_eofcreate_address",
114114
"copy_opcode_cost",
115115
"cost_memory_bytes",
116116
"eip_2028_transaction_data_cost",

src/ethereum_test_tools/common/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
add_kzg_version,
2929
ceiling_division,
3030
compute_create2_address,
31-
compute_create3_address,
3231
compute_create_address,
32+
compute_eofcreate_address,
3333
copy_opcode_cost,
3434
cost_memory_bytes,
3535
eip_2028_transaction_data_cost,
@@ -77,7 +77,7 @@
7777
"ceiling_division",
7878
"compute_create_address",
7979
"compute_create2_address",
80-
"compute_create3_address",
80+
"compute_eofcreate_address",
8181
"copy_opcode_cost",
8282
"cost_memory_bytes",
8383
"eip_2028_transaction_data_cost",

src/ethereum_test_tools/common/helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def copy_opcode_cost(length: int) -> int:
7171
return 3 + (ceiling_division(length, 32) * 3) + cost_memory_bytes(length, 0)
7272

7373

74-
def compute_create3_address(
74+
def compute_eofcreate_address(
7575
address: FixedSizeBytesConvertible,
7676
salt: FixedSizeBytesConvertible,
7777
init_container: BytesConvertible,

src/ethereum_test_tools/eof/v1/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ def bytecode(self) -> bytes:
462462
initcode = Container(
463463
sections=[
464464
Section(
465-
data=Op.CREATE3(0, 0, 0, 0, len(self.deploy_container)) + Op.STOP(),
465+
data=Op.EOFCREATE(0, 0, 0, 0, len(self.deploy_container)) + Op.STOP(),
466466
kind=SectionKind.CODE,
467467
max_stack_height=4,
468468
),

src/ethereum_test_tools/vm/opcode.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5014,11 +5014,41 @@ class Opcodes(Opcode, Enum):
50145014
50155015
"""
50165016

5017-
CREATE3 = Opcode(0xEC, popped_stack_items=4, pushed_stack_items=1, data_portion_length=1)
5017+
EXCHANGE = Opcode(0xE8, data_portion_length=1)
50185018
"""
50195019
!!! Note: This opcode is under development
50205020
5021-
CREATE3()
5021+
EXCHANGE[mn]
5022+
----
5023+
5024+
Description
5025+
----
5026+
Exchanges two stack positions. Two nybbles, n is high 4 bits + 1, then m is 4 low bits + 1.
5027+
Exchanges tne n+1'th item with the n + m + 1 item.
5028+
5029+
Inputs
5030+
----
5031+
n + m + 1, or ((imm >> 4) + (imm &0x0F) + 3) from the raw immediate,
5032+
5033+
Outputs
5034+
----
5035+
n + m + 1, or ((imm >> 4) + (imm &0x0F) + 3) from the raw immediate,
5036+
5037+
Fork
5038+
----
5039+
EOF_FORK
5040+
5041+
Gas
5042+
----
5043+
3
5044+
5045+
"""
5046+
5047+
EOFCREATE = Opcode(0xEC, popped_stack_items=4, pushed_stack_items=1, data_portion_length=1)
5048+
"""
5049+
!!! Note: This opcode is under development
5050+
5051+
EOFCREATE[ initcontainer_index](value, salt, input_offset, input_size)
50225052
----
50235053
50245054
Description
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
EOF tests for EIP-6206 JUMPF
3+
"""
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
"""
2+
EOF V1 Code Validation tests
3+
"""
4+
5+
from typing import List
6+
7+
from ethereum_test_tools import EOFException
8+
from ethereum_test_tools.eof.v1 import Container, Section
9+
from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION
10+
from ethereum_test_tools.vm.opcode import Opcodes as Op
11+
12+
13+
def quick_code(code, inputs=0, outputs=NON_RETURNING_SECTION, height=0):
14+
"""A sorter way to write code with section 0 defaults"""
15+
return Section.Code(
16+
code=code, code_inputs=inputs, code_outputs=outputs, max_stack_height=height
17+
)
18+
19+
20+
def container_name(c: Container):
21+
"""
22+
Return the name of the container for use in pytest ids.
23+
"""
24+
if hasattr(c, "name"):
25+
return c.name
26+
else:
27+
return c.__class__.__name__
28+
29+
30+
def generate_jumpf_target_rules():
31+
"""
32+
Generate tests for JUMPF where we are testing the validity of the JUNMPF target.
33+
We are not testing stack so a lot of the logic is to get correct stack values.
34+
"""
35+
valid = []
36+
invalid = []
37+
for current_outputs in [NON_RETURNING_SECTION, 0, 2, 4]:
38+
current_non_returning = current_outputs == NON_RETURNING_SECTION
39+
current_height = 0 if current_non_returning else current_outputs
40+
for target_outputs in [NON_RETURNING_SECTION, 0, 2, 4]:
41+
target_non_returning = target_outputs == NON_RETURNING_SECTION
42+
target_height = 0 if target_non_returning else target_outputs
43+
delta = (
44+
0
45+
if target_non_returning or current_non_returning
46+
else target_outputs - current_height
47+
)
48+
current_extra_push = max(0, current_height - target_height)
49+
current_section = Section.Code(
50+
code=Op.PUSH0 * (current_height)
51+
+ Op.CALLDATALOAD(0)
52+
+ Op.RJUMPI[1]
53+
+ (Op.STOP if current_non_returning else Op.RETF)
54+
+ Op.PUSH0 * current_extra_push
55+
+ Op.JUMPF[2],
56+
code_inputs=0,
57+
code_outputs=current_outputs,
58+
max_stack_height=current_height + max(1, current_extra_push),
59+
)
60+
target_section = Section.Code(
61+
code=((Op.PUSH0 * delta) if delta >= 0 else (Op.POP * -delta))
62+
+ Op.CALLF[3]
63+
+ (Op.STOP if target_non_returning else Op.RETF),
64+
code_inputs=current_height,
65+
code_outputs=target_outputs,
66+
max_stack_height=max(current_height, current_height + delta),
67+
)
68+
69+
container = Container(
70+
name="target_co-%s_to-%s"
71+
% (
72+
"N" if current_non_returning else current_outputs,
73+
"N" if target_non_returning else target_outputs,
74+
),
75+
sections=[
76+
quick_code(Op.JUMPF[1], height=0 if current_non_returning else current_height)
77+
if current_non_returning
78+
else quick_code(
79+
Op.CALLF[1](0, 0) + Op.STOP,
80+
height=0 if current_non_returning else 2 + current_outputs,
81+
),
82+
current_section,
83+
target_section,
84+
quick_code(Op.SSTORE(0, 1) + Op.RETF, outputs=0, height=2),
85+
],
86+
)
87+
88+
# now sort validity...
89+
if target_non_returning:
90+
valid.append(container)
91+
elif current_non_returning or current_outputs < target_outputs:
92+
# both as non-returning handled above
93+
container.validity_error = EOFException.UNDEFINED_EXCEPTION
94+
invalid.append(container)
95+
else:
96+
# both are returning, and current >= target
97+
valid.append(container)
98+
return (valid, invalid)
99+
100+
101+
def generate_jumpf_stack_returning_rules():
102+
"""
103+
Generate tests for JUMPF where we are testing the stack rules. Returning section cases
104+
"""
105+
valid = []
106+
invalid = []
107+
for current_outputs in [0, 2, 4]:
108+
for target_outputs in [x for x in [0, 2, 4] if x <= current_outputs]:
109+
for target_inputs in [0, 2, 4]:
110+
for stack_diff in [-1, 0, 1] if target_inputs > 0 else [0, 1]:
111+
target_delta = target_outputs - target_inputs
112+
container = Container(
113+
name="stack-retuning_co-%d_to-%d_ti-%d_diff-%d"
114+
% (current_outputs, target_outputs, target_inputs, stack_diff),
115+
sections=[
116+
quick_code(
117+
Op.CALLF[1] + Op.SSTORE(0, 1) + Op.STOP, height=2 + current_outputs
118+
),
119+
quick_code(
120+
Op.PUSH0 * max(0, target_inputs + stack_diff) + Op.JUMPF[2],
121+
outputs=current_outputs,
122+
height=target_inputs,
123+
),
124+
quick_code(
125+
(
126+
Op.POP * -target_delta
127+
if target_delta < 0
128+
else Op.PUSH0 * target_delta
129+
)
130+
+ Op.RETF,
131+
inputs=target_inputs,
132+
outputs=target_outputs,
133+
height=max(target_inputs, target_outputs),
134+
),
135+
],
136+
)
137+
138+
if stack_diff == current_outputs - target_outputs:
139+
valid.append(container)
140+
else:
141+
container.validity_error = EOFException.UNDEFINED_EXCEPTION
142+
invalid.append(container)
143+
144+
return (valid, invalid)
145+
146+
147+
def generate_jumpf_stack_non_returning_rules():
148+
"""
149+
Generate tests for JUMPF where we are testing the stack rules. Non-returning section cases.
150+
"""
151+
valid = []
152+
invalid = []
153+
for stack_height in [0, 2, 4]:
154+
for target_inputs in [0, 2, 4]:
155+
container = Container(
156+
name="stack-non-retuning_h-%d_ti-%d" % (stack_height, target_inputs),
157+
sections=[
158+
quick_code(Op.JUMPF[1]),
159+
quick_code(
160+
Op.PUSH0 * stack_height + Op.JUMPF[2],
161+
height=stack_height,
162+
),
163+
quick_code(
164+
Op.POP * target_inputs + Op.SSTORE(0, 1) + Op.STOP,
165+
inputs=target_inputs,
166+
height=max(2, target_inputs),
167+
),
168+
],
169+
)
170+
171+
if stack_height >= target_inputs:
172+
valid.append(container)
173+
else:
174+
container.validity_error = EOFException.UNDEFINED_EXCEPTION
175+
invalid.append(container)
176+
177+
return (valid, invalid)
178+
179+
180+
jump_forward = Container(
181+
name="jump_forward",
182+
sections=[quick_code(Op.JUMPF[1]), quick_code(Op.SSTORE(0, 1) + Op.STOP, height=2)],
183+
)
184+
jump_backward = Container(
185+
name="jump_backward",
186+
sections=[
187+
quick_code(Op.CALLF[2] + Op.SSTORE(0, 1) + Op.STOP, height=2),
188+
quick_code(Op.RETF, outputs=0),
189+
quick_code(Op.JUMPF[1], outputs=0),
190+
],
191+
)
192+
jump_to_self = Container(
193+
name="jump_to_self",
194+
sections=[
195+
quick_code(
196+
Op.SLOAD(0) + Op.ISZERO + Op.RJUMPI[1] + Op.STOP + Op.SSTORE(0, 1) + Op.JUMPF[0],
197+
height=2,
198+
)
199+
],
200+
)
201+
jump_too_large = Container(
202+
name="jump_too_large",
203+
sections=[quick_code(Op.JUMPF[1025])],
204+
validity_error=EOFException.UNDEFINED_EXCEPTION,
205+
)
206+
jump_way_too_large = Container(
207+
name="jump_way_too_large",
208+
sections=[quick_code(Op.JUMPF[0xFFFF])],
209+
validity_error=EOFException.UNDEFINED_EXCEPTION,
210+
)
211+
jump_non_existent_section = Container(
212+
name="jump_non_existent_section",
213+
sections=[quick_code(Op.JUMPF[5])],
214+
validity_error=EOFException.UNDEFINED_EXCEPTION,
215+
)
216+
callf_non_returning = Container(
217+
name="callf_non_returning",
218+
sections=[quick_code(Op.CALLF[1]), quick_code(Op.STOP, outputs=NON_RETURNING_SECTION)],
219+
validity_error=EOFException.UNDEFINED_EXCEPTION,
220+
)
221+
222+
223+
jumpf_targets = generate_jumpf_target_rules()
224+
jumpf_stack_returning = generate_jumpf_stack_returning_rules()
225+
jumpf_stack_non_returning = generate_jumpf_stack_non_returning_rules()
226+
227+
VALID: List[Container] = [
228+
jump_forward,
229+
jump_backward,
230+
jump_to_self,
231+
*jumpf_targets[0],
232+
*jumpf_stack_returning[0],
233+
*jumpf_stack_non_returning[0],
234+
]
235+
236+
INVALID: List[Container] = [
237+
jump_too_large,
238+
jump_too_large,
239+
jump_non_existent_section,
240+
callf_non_returning,
241+
*jumpf_targets[1],
242+
*jumpf_stack_returning[1],
243+
*jumpf_stack_non_returning[1],
244+
]

tests/prague/eip6206_jumpf/spec.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""
2+
EOF V1 Constants used throughout all tests
3+
"""
4+
5+
EOF_FORK_NAME = "Prague"

0 commit comments

Comments
 (0)