Skip to content

Commit 9df25cb

Browse files
committed
fix invalid opcodes
rebase
1 parent 2a19923 commit 9df25cb

File tree

4 files changed

+67
-87
lines changed

4 files changed

+67
-87
lines changed

tests/frontier/scenarios/common.py

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@
1212
from ethereum_test_tools.vm.opcode import Opcodes as Op
1313

1414

15+
@dataclass
16+
class SpecialAddress:
17+
"""Special values that are re-directed to test generated contracts."""
18+
19+
GAS_HASH_ADDRESS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
20+
EXTERNAL_ADDRESS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
21+
INVALID_CALL_ADDRESS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
22+
23+
1524
@dataclass
1625
class ScenarioDebug:
1726
"""Debug selector for the development."""
@@ -172,47 +181,41 @@ def translate_result(
172181

173182

174183
def replace_special_calls_in_operation(
175-
pre: Alloc, operation: Bytecode, external_address: Address
184+
pre: Alloc, fork: Fork, operation: Bytecode, external_address: Address
176185
) -> Bytecode:
177186
"""
178187
Run find replace of some special calls to the contracts that we don't know at compile time
179188
replace 0xfff..fff address to external_address
180189
replace special call to 0xfff..ffe address to gas_hash_address contract.
181190
"""
182191
gas_hash_address = make_gas_hash_contract(pre)
183-
invalid_opcode_contract = make_invalid_opcode_contract(pre)
184-
185-
"""Replace Op.CALL(Op.GAS, 0xfff..ffe, 0, 64, 32, 0, 0) to gas_hash_address"""
186-
"""Replace Op.CALL(1000, 0xfff..ffd, 0, 64, 32, 0, 0) to invalid_opcode_contract"""
187-
"""Replace BALANCE(0xfff..fff) to BALANCE(external_address) in operation"""
188-
"""Replace EXTCODESIZE(0xfff..fff) to EXTCODESIZE(external_address) in operation"""
189-
"""Replace EXTCODEHASH(0xfff..fff) to EXTCODEHASH(external_address) in operation"""
190-
"""Replace EXTCODECOPY(0xfff..fff, ...) to EXTCODECOPY(external_address, ...)"""
192+
invalid_opcode_contract = make_invalid_opcode_contract(pre, fork)
193+
191194
replace_list: List[Tuple[str, str]] = [
192195
(
193-
"600060006020604060007ffffffffffffffffffffffffffffffffffffffffffffffff"
194-
"ffffffffffffffffd620186a0f1",
195-
Op.CALL(10000, invalid_opcode_contract, 0, 64, 32, 0, 0).hex(),
196+
"602060646020604060007f"
197+
+ hex(SpecialAddress.INVALID_CALL_ADDRESS)[2:].lower()
198+
+ "611388f1",
199+
Op.CALL(Op.SUB(Op.GAS, 200000), invalid_opcode_contract, 0, 64, 32, 100, 32).hex(),
196200
),
197201
(
198-
"600060006020604060007ffffffffffffffffffffffffffffffffffffffffffffffff"
199-
"ffffffffffffffffe5af1",
202+
"600060006020604060007f" + hex(SpecialAddress.GAS_HASH_ADDRESS)[2:].lower() + "5af1",
200203
Op.CALL(Op.SUB(Op.GAS, 200000), gas_hash_address, 0, 64, 32, 0, 0).hex(),
201204
),
202205
(
203-
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff31",
206+
"7f" + hex(SpecialAddress.EXTERNAL_ADDRESS)[2:].lower() + "31",
204207
Op.BALANCE(external_address).hex(),
205208
),
206209
(
207-
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3b",
210+
"7f" + hex(SpecialAddress.EXTERNAL_ADDRESS)[2:].lower() + "3b",
208211
Op.EXTCODESIZE(external_address).hex(),
209212
),
210213
(
211-
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
214+
"7f" + hex(SpecialAddress.EXTERNAL_ADDRESS)[2:].lower() + "3f",
212215
Op.EXTCODEHASH(external_address).hex(),
213216
),
214217
(
215-
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3c",
218+
"7f" + hex(SpecialAddress.EXTERNAL_ADDRESS)[2:].lower() + "3c",
216219
"3c".join(Op.BALANCE(external_address).hex().rsplit("31", 1)),
217220
),
218221
]
@@ -256,20 +259,44 @@ def make_gas_hash_contract(pre: Alloc) -> Address:
256259
return gas_hash_address
257260

258261

259-
def make_invalid_opcode_contract(pre: Alloc) -> Address:
262+
def make_invalid_opcode_contract(pre: Alloc, fork: Fork) -> Address:
260263
"""
261264
Deploy a contract that will execute any asked byte as an opcode from calldataload
262-
With 0-ed input stack of 10 elements, valid for opcodes starting at 0x0C.
265+
Deploy 20 empty stack elements. Jump to opcode instruction. if worked, return 0.
263266
"""
264267
invalid_opcode_caller = pre.deploy_contract(
265-
code=Op.PUSH0 * 10
266-
+ Op.JUMP(Op.SUB(Op.MUL(2, Op.CALLDATALOAD(0)), 1)) # here pc is 19
267-
+ Op.JUMPDEST * 4
268+
code=Op.PUSH1(0) * 20
269+
+ Op.JUMP(Op.ADD(Op.MUL(7, Op.CALLDATALOAD(0)), 20 * 2 + 10))
268270
+ sum(
269271
[
270-
Bytecode(bytes([opcode]), popped_stack_items=0, pushed_stack_items=0) + Op.JUMPDEST
271-
for opcode in range(0x0C, 0xFF)
272+
Op.JUMPDEST
273+
+ Bytecode(bytes([opcode]), popped_stack_items=0, pushed_stack_items=0)
274+
+ Op.RETURN(0, 0)
275+
for opcode in range(0x00, 0xFF)
272276
],
273277
)
274278
)
275-
return invalid_opcode_caller
279+
280+
invalid_opcodes = []
281+
valid_opcode_values = [opcode.int() for opcode in fork.valid_opcodes()]
282+
283+
for op in range(0x00, 0xFF):
284+
if op not in valid_opcode_values:
285+
invalid_opcodes.append(op)
286+
287+
code = Bytecode(
288+
sum(
289+
Op.MSTORE(64, opcode)
290+
+ Op.MSTORE(
291+
32,
292+
Op.CALL(gas=50000, address=invalid_opcode_caller, args_offset=64, args_size=32),
293+
)
294+
+ Op.MSTORE(0, Op.ADD(Op.MLOAD(0), Op.MLOAD(32)))
295+
for opcode in invalid_opcodes
296+
)
297+
# If any of invalid instructions works, mstore[0] will be > 1
298+
+ Op.MSTORE(0, Op.ADD(Op.MLOAD(0), 1))
299+
+ Op.RETURN(0, 32)
300+
)
301+
302+
return pre.deploy_contract(code=code)

tests/frontier/scenarios/programs/context_calls.py

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from ethereum_test_forks import Byzantium, Cancun, Constantinople, Istanbul, London, Shanghai
66
from ethereum_test_tools.vm.opcode import Opcodes as Op
77

8-
from ..common import ProgramResult, ScenarioExpectOpcode
8+
from ..common import ProgramResult, ScenarioExpectOpcode, SpecialAddress
99

1010
# Check that ADDRESS is really the code execution address in all scenarios
1111
program_address = pytest.param(
@@ -16,8 +16,7 @@
1616

1717
# Check the BALANCE in all execution contexts
1818
program_balance = pytest.param(
19-
Op.MSTORE(0, Op.BALANCE(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
20-
+ Op.RETURN(0, 32),
19+
Op.MSTORE(0, Op.BALANCE(SpecialAddress.EXTERNAL_ADDRESS)) + Op.RETURN(0, 32),
2120
ProgramResult(result=ScenarioExpectOpcode.BALANCE),
2221
id="program_BALANCE",
2322
)
@@ -81,10 +80,8 @@
8180

8281
# Check that extcodesize and extcodecopy of pre deployed contract stay the same in all contexts
8382
program_ext_codecopy_codesize = pytest.param(
84-
Op.MSTORE(
85-
0, Op.EXTCODESIZE(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
86-
)
87-
+ Op.EXTCODECOPY(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 0, 0, 30)
83+
Op.MSTORE(0, Op.EXTCODESIZE(SpecialAddress.EXTERNAL_ADDRESS))
84+
+ Op.EXTCODECOPY(SpecialAddress.EXTERNAL_ADDRESS, 0, 0, 30)
8885
+ Op.RETURN(0, 32),
8986
ProgramResult(result=0x6001600101000000000000000000000000000000000000000000000000000005),
9087
id="program_EXTCODECOPY_EXTCODESIZE",
@@ -112,10 +109,7 @@
112109

113110
# Check that extcodehash stays the same in all contexts
114111
program_extcodehash = pytest.param(
115-
Op.MSTORE(
116-
0, Op.EXTCODEHASH(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
117-
)
118-
+ Op.RETURN(0, 32),
112+
Op.MSTORE(0, Op.EXTCODEHASH(SpecialAddress.EXTERNAL_ADDRESS)) + Op.RETURN(0, 32),
119113
ProgramResult(
120114
result=0x8C634A8B28DD46F5DCB9A9F5DA1FAED26D0FB5ED98F3873A29AD27AAAFFDE0E4,
121115
from_fork=Constantinople,
@@ -128,9 +122,7 @@
128122
program_blockhash = pytest.param(
129123
# Calculate gas hash of Op.BLOCKHASH(0) value
130124
Op.MSTORE(64, Op.BLOCKHASH(0))
131-
+ Op.CALL(
132-
Op.GAS, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, 0, 64, 32, 0, 0
133-
)
125+
+ Op.CALL(Op.GAS, SpecialAddress.GAS_HASH_ADDRESS, 0, 64, 32, 0, 0)
134126
+ Op.MSTORE(0, 1)
135127
+ Op.RETURN(0, 32),
136128
ProgramResult(result=1),
@@ -160,9 +152,7 @@
160152
# Calculate gas hash of DIFFICULTY value
161153
Op.MSTORE(0, Op.PREVRANDAO)
162154
+ Op.MSTORE(64, Op.PREVRANDAO)
163-
+ Op.CALL(
164-
Op.GAS, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, 0, 64, 32, 0, 0
165-
)
155+
+ Op.CALL(Op.GAS, SpecialAddress.GAS_HASH_ADDRESS, 0, 64, 32, 0, 0)
166156
+ Op.MSTORE(0, 1)
167157
+ Op.RETURN(0, 32),
168158
ProgramResult(result=1),
@@ -192,9 +182,7 @@
192182
program_basefee = pytest.param(
193183
# Calculate gas hash of BASEFEE value
194184
Op.MSTORE(64, Op.BASEFEE)
195-
+ Op.CALL(
196-
Op.GAS, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, 0, 64, 32, 0, 0
197-
)
185+
+ Op.CALL(Op.GAS, SpecialAddress.GAS_HASH_ADDRESS, 0, 64, 32, 0, 0)
198186
+ Op.MSTORE(0, 1)
199187
+ Op.RETURN(0, 32),
200188
ProgramResult(result=1, from_fork=London),
@@ -210,9 +198,7 @@
210198
program_blobbasefee = pytest.param(
211199
# Calculate gas hash of BLOBBASEFEE value
212200
Op.MSTORE(64, Op.BLOBBASEFEE)
213-
+ Op.CALL(
214-
Op.GAS, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, 0, 64, 32, 0, 0
215-
)
201+
+ Op.CALL(Op.GAS, SpecialAddress.GAS_HASH_ADDRESS, 0, 64, 32, 0, 0)
216202
+ Op.MSTORE(0, 1)
217203
+ Op.RETURN(0, 32),
218204
ProgramResult(result=1, from_fork=Cancun),

tests/frontier/scenarios/programs/invalid_opcodes.py

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,13 @@
22

33
import pytest
44

5-
from ethereum_test_tools import Bytecode
65
from ethereum_test_tools.vm.opcode import Opcodes as Op
76

8-
from ..common import ProgramResult
9-
10-
invalid_opcode_ranges = [
11-
range(0x0C, 0x10),
12-
range(0x1E, 0x20),
13-
range(0x21, 0x30),
14-
range(0x4B, 0x50),
15-
range(0xA5, 0xF0),
16-
range(0xF6, 0xFA),
17-
range(0xFB, 0xFD),
18-
range(0xFE, 0xFF),
19-
]
20-
21-
22-
def make_all_invalid_opcode_calls() -> Bytecode:
23-
"""Call special contract to initiate all invalid opcode instruction."""
24-
invalid_opcode_caller = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
25-
26-
code = Bytecode(
27-
sum(
28-
Op.MSTORE(64, opcode)
29-
+ Op.MSTORE(
30-
32,
31-
Op.CALL(gas=100000, address=invalid_opcode_caller, args_offset=64, args_size=32),
32-
)
33-
+ Op.MSTORE(0, Op.ADD(Op.MLOAD(0), Op.MLOAD(32)))
34-
for opcode_range in invalid_opcode_ranges
35-
for opcode in opcode_range
36-
)
37-
# If any of invalid instructions works, mstore[0] will be > 1
38-
+ Op.MSTORE(0, Op.ADD(Op.MLOAD(0), 1))
39-
+ Op.RETURN(0, 32)
40-
)
41-
return code
42-
7+
from ..common import ProgramResult, SpecialAddress
438

449
program_invalid = pytest.param(
45-
make_all_invalid_opcode_calls(),
10+
Op.MSTORE(0, Op.CALL(5000, SpecialAddress.INVALID_CALL_ADDRESS, 0, 64, 32, 100, 32))
11+
+ Op.RETURN(100, 32),
4612
ProgramResult(result=1),
4713
id="program_INVALID",
4814
)

tests/frontier/scenarios/test_scenarios.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def scenarios(fork: Fork, pre: Alloc, operation: Bytecode, debug: ScenarioDebug)
9494
external_balance = 123
9595
external_address = pre.deploy_contract(code=Op.ADD(1, 1), balance=external_balance)
9696

97-
operation = replace_special_calls_in_operation(pre, operation, external_address)
97+
operation = replace_special_calls_in_operation(pre, fork, operation, external_address)
9898

9999
combinations_input: ScenarioGeneratorInput = ScenarioGeneratorInput(
100100
fork=fork,
@@ -134,6 +134,7 @@ def scenarios(fork: Fork, pre: Alloc, operation: Bytecode, debug: ScenarioDebug)
134134
# select program to debug (program, "scenario_name")
135135
# program=None select all programs
136136
# scenario_name="" select all scenarios
137+
# Example: [ScenarioDebug(test_param=program_invalid, scenario_name="scenario_CALL_CALL")],
137138
"debug",
138139
[ScenarioDebug(test_param=None, scenario_name="")],
139140
ids=["debug"],

0 commit comments

Comments
 (0)