diff --git a/converted-ethereum-tests.txt b/converted-ethereum-tests.txt index f11bdc4490..7611d406d6 100644 --- a/converted-ethereum-tests.txt +++ b/converted-ethereum-tests.txt @@ -1,3 +1,119 @@ +GeneralStateTests/stBadOpcode/opc0CDiffPlaces.json +GeneralStateTests/stBadOpcode/opc0DDiffPlaces.json +GeneralStateTests/stBadOpcode/opc0EDiffPlaces.json +GeneralStateTests/stBadOpcode/opc0FDiffPlaces.json +GeneralStateTests/stBadOpcode/opc1EDiffPlaces.json +GeneralStateTests/stBadOpcode/opc1FDiffPlaces.json +GeneralStateTests/stBadOpcode/opc2ADiffPlaces.json +GeneralStateTests/stBadOpcode/opc2BDiffPlaces.json +GeneralStateTests/stBadOpcode/opc2CDiffPlaces.json +GeneralStateTests/stBadOpcode/opc2DDiffPlaces.json +GeneralStateTests/stBadOpcode/opc2EDiffPlaces.json +GeneralStateTests/stBadOpcode/opc2FDiffPlaces.json +GeneralStateTests/stBadOpcode/opc4ADiffPlaces.json +GeneralStateTests/stBadOpcode/opc4BDiffPlaces.json +GeneralStateTests/stBadOpcode/opc4CDiffPlaces.json +GeneralStateTests/stBadOpcode/opc4DDiffPlaces.json +GeneralStateTests/stBadOpcode/opc4EDiffPlaces.json +GeneralStateTests/stBadOpcode/opc4FDiffPlaces.json +GeneralStateTests/stBadOpcode/opc5CDiffPlaces.json +GeneralStateTests/stBadOpcode/opc5DDiffPlaces.json +GeneralStateTests/stBadOpcode/opc5EDiffPlaces.json +GeneralStateTests/stBadOpcode/opc5FDiffPlaces.json +GeneralStateTests/stBadOpcode/opc21DiffPlaces.json +GeneralStateTests/stBadOpcode/opc22DiffPlaces.json +GeneralStateTests/stBadOpcode/opc23DiffPlaces.json +GeneralStateTests/stBadOpcode/opc24DiffPlaces.json +GeneralStateTests/stBadOpcode/opc25DiffPlaces.json +GeneralStateTests/stBadOpcode/opc26DiffPlaces.json +GeneralStateTests/stBadOpcode/opc27DiffPlaces.json +GeneralStateTests/stBadOpcode/opc28DiffPlaces.json +GeneralStateTests/stBadOpcode/opc29DiffPlaces.json +GeneralStateTests/stBadOpcode/opc49DiffPlaces.json +GeneralStateTests/stBadOpcode/opcA5DiffPlaces.json +GeneralStateTests/stBadOpcode/opcA6DiffPlaces.json +GeneralStateTests/stBadOpcode/opcA7DiffPlaces.json +GeneralStateTests/stBadOpcode/opcA8DiffPlaces.json +GeneralStateTests/stBadOpcode/opcA9DiffPlaces.json +GeneralStateTests/stBadOpcode/opcAADiffPlaces.json +GeneralStateTests/stBadOpcode/opcABDiffPlaces.json +GeneralStateTests/stBadOpcode/opcACDiffPlaces.json +GeneralStateTests/stBadOpcode/opcADDiffPlaces.json +GeneralStateTests/stBadOpcode/opcAEDiffPlaces.json +GeneralStateTests/stBadOpcode/opcAFDiffPlaces.json +GeneralStateTests/stBadOpcode/opcB0DiffPlaces.json +GeneralStateTests/stBadOpcode/opcB1DiffPlaces.json +GeneralStateTests/stBadOpcode/opcB2DiffPlaces.json +GeneralStateTests/stBadOpcode/opcB3DiffPlaces.json +GeneralStateTests/stBadOpcode/opcB4DiffPlaces.json +GeneralStateTests/stBadOpcode/opcB5DiffPlaces.json +GeneralStateTests/stBadOpcode/opcB6DiffPlaces.json +GeneralStateTests/stBadOpcode/opcB7DiffPlaces.json +GeneralStateTests/stBadOpcode/opcB8DiffPlaces.json +GeneralStateTests/stBadOpcode/opcB9DiffPlaces.json +GeneralStateTests/stBadOpcode/opcBADiffPlaces.json +GeneralStateTests/stBadOpcode/opcBBDiffPlaces.json +GeneralStateTests/stBadOpcode/opcBCDiffPlaces.json +GeneralStateTests/stBadOpcode/opcBDDiffPlaces.json +GeneralStateTests/stBadOpcode/opcBEDiffPlaces.json +GeneralStateTests/stBadOpcode/opcBFDiffPlaces.json +GeneralStateTests/stBadOpcode/opcC0DiffPlaces.json +GeneralStateTests/stBadOpcode/opcC1DiffPlaces.json +GeneralStateTests/stBadOpcode/opcC2DiffPlaces.json +GeneralStateTests/stBadOpcode/opcC3DiffPlaces.json +GeneralStateTests/stBadOpcode/opcC4DiffPlaces.json +GeneralStateTests/stBadOpcode/opcC5DiffPlaces.json +GeneralStateTests/stBadOpcode/opcC6DiffPlaces.json +GeneralStateTests/stBadOpcode/opcC7DiffPlaces.json +GeneralStateTests/stBadOpcode/opcC8DiffPlaces.json +GeneralStateTests/stBadOpcode/opcC9DiffPlaces.json +GeneralStateTests/stBadOpcode/opcCADiffPlaces.json +GeneralStateTests/stBadOpcode/opcCBDiffPlaces.json +GeneralStateTests/stBadOpcode/opcCCDiffPlaces.json +GeneralStateTests/stBadOpcode/opcCDDiffPlaces.json +GeneralStateTests/stBadOpcode/opcCEDiffPlaces.json +GeneralStateTests/stBadOpcode/opcCFDiffPlaces.json +GeneralStateTests/stBadOpcode/opcD0DiffPlaces.json +GeneralStateTests/stBadOpcode/opcD1DiffPlaces.json +GeneralStateTests/stBadOpcode/opcD2DiffPlaces.json +GeneralStateTests/stBadOpcode/opcD3DiffPlaces.json +GeneralStateTests/stBadOpcode/opcD4DiffPlaces.json +GeneralStateTests/stBadOpcode/opcD5DiffPlaces.json +GeneralStateTests/stBadOpcode/opcD6DiffPlaces.json +GeneralStateTests/stBadOpcode/opcD7DiffPlaces.json +GeneralStateTests/stBadOpcode/opcD8DiffPlaces.json +GeneralStateTests/stBadOpcode/opcD9DiffPlaces.json +GeneralStateTests/stBadOpcode/opcDADiffPlaces.json +GeneralStateTests/stBadOpcode/opcDBDiffPlaces.json +GeneralStateTests/stBadOpcode/opcDCDiffPlaces.json +GeneralStateTests/stBadOpcode/opcDDDiffPlaces.json +GeneralStateTests/stBadOpcode/opcDEDiffPlaces.json +GeneralStateTests/stBadOpcode/opcDFDiffPlaces.json +GeneralStateTests/stBadOpcode/opcE0DiffPlaces.json +GeneralStateTests/stBadOpcode/opcE1DiffPlaces.json +GeneralStateTests/stBadOpcode/opcE2DiffPlaces.json +GeneralStateTests/stBadOpcode/opcE3DiffPlaces.json +GeneralStateTests/stBadOpcode/opcE4DiffPlaces.json +GeneralStateTests/stBadOpcode/opcE5DiffPlaces.json +GeneralStateTests/stBadOpcode/opcE6DiffPlaces.json +GeneralStateTests/stBadOpcode/opcE7DiffPlaces.json +GeneralStateTests/stBadOpcode/opcE8DiffPlaces.json +GeneralStateTests/stBadOpcode/opcE9DiffPlaces.json +GeneralStateTests/stBadOpcode/opcEADiffPlaces.json +GeneralStateTests/stBadOpcode/opcEBDiffPlaces.json +GeneralStateTests/stBadOpcode/opcECDiffPlaces.json +GeneralStateTests/stBadOpcode/opcEDDiffPlaces.json +GeneralStateTests/stBadOpcode/opcEEDiffPlaces.json +GeneralStateTests/stBadOpcode/opcEFDiffPlaces.json +GeneralStateTests/stBadOpcode/opcF6DiffPlaces.json +GeneralStateTests/stBadOpcode/opcF7DiffPlaces.json +GeneralStateTests/stBadOpcode/opcF8DiffPlaces.json +GeneralStateTests/stBadOpcode/opcF9DiffPlaces.json +GeneralStateTests/stBadOpcode/opcFBDiffPlaces.json +GeneralStateTests/stBadOpcode/opcFCDiffPlaces.json +GeneralStateTests/stBadOpcode/opcFEDiffPlaces.json + + ([#748](https://github.com/ethereum/execution-spec-tests/pull/748)) GeneralStateTests/stBadOpcode/badOpcodes.json GeneralStateTests/stBugs/evmBytecode.json diff --git a/tests/frontier/scenarios/__init__.py b/tests/frontier/scenarios/__init__.py new file mode 100644 index 0000000000..20f2893c0e --- /dev/null +++ b/tests/frontier/scenarios/__init__.py @@ -0,0 +1,3 @@ +""" +Scenarios common import +""" diff --git a/tests/frontier/scenarios/common.py b/tests/frontier/scenarios/common.py new file mode 100644 index 0000000000..0deead7bb1 --- /dev/null +++ b/tests/frontier/scenarios/common.py @@ -0,0 +1,37 @@ +""" +Define Scenario class for test_scenarios test +""" +from typing import Optional + +from ethereum_test_forks import Byzantium, Constantinople, Fork, Frontier, Homestead +from ethereum_test_tools import Address +from ethereum_test_tools.vm.opcode import Opcode +from ethereum_test_tools.vm.opcode import Opcodes as Op + + +class Scenario: + """ + Describe test scenario that will be run in test and it's conditions + """ + + name: str + code: Address + fork: Fork + not_reverting: bool + + def __init__(self, name: str, code: Address, fork: Fork, reverts: bool = False): + self.name = name + self.code = code + self.fork = fork + self.not_reverting = not reverts + + +def get_valid_fork_for_call(call: Opcode, second_call: Optional[Opcode] = Op.CALL) -> Fork: + """Return fork for which this call will be valid""" + if call == Op.CREATE2 or second_call == Op.CREATE2: + return Constantinople + if call == Op.STATICCALL or second_call == Op.STATICCALL: + return Byzantium + if call == Op.DELEGATECALL or second_call == Op.DELEGATECALL: + return Homestead + return Frontier diff --git a/tests/frontier/scenarios/programs/__init__.py b/tests/frontier/scenarios/programs/__init__.py new file mode 100644 index 0000000000..20f2893c0e --- /dev/null +++ b/tests/frontier/scenarios/programs/__init__.py @@ -0,0 +1,3 @@ +""" +Scenarios common import +""" diff --git a/tests/frontier/scenarios/programs/all_frontier_opcodes.py b/tests/frontier/scenarios/programs/all_frontier_opcodes.py new file mode 100644 index 0000000000..d43898f784 --- /dev/null +++ b/tests/frontier/scenarios/programs/all_frontier_opcodes.py @@ -0,0 +1,81 @@ +""" +Define a program for scenario test that executes all frontier opcodes and entangles it's result +""" +import pytest + +from ethereum_test_tools import Bytecode +from ethereum_test_tools.vm.opcode import Opcodes as Op + + +def make_all_opcode_program() -> Bytecode: + """Make a program that call each Frontier opcode and tangles it's results""" + code: Bytecode = ( + Op.MSTORE(0, Op.ADD(1, 1)) # 2 + + Op.MSTORE(0, Op.MUL(3, Op.MLOAD(0))) # 6 + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), 1)) # 5 + + Op.MSTORE(0, Op.DIV(Op.MLOAD(0), 2)) # 2 + + Op.MSTORE(0, Op.SDIV(Op.MLOAD(0), 2)) # 1 + + Op.MSTORE(0, Op.ADD(Op.MLOAD(0), 19)) # 20 + + Op.MSTORE(0, Op.SMOD(Op.MLOAD(0), 3)) # 2 + + Op.MSTORE(0, Op.ADDMOD(Op.MLOAD(0), 3, 2)) # 1 + + Op.MSTORE(0, Op.MULMOD(Op.MLOAD(0), 10, 2)) # 0 + + Op.MSTORE(0, Op.ADD(Op.MLOAD(0), 3)) # 3 + + Op.MSTORE(0, Op.EXP(Op.MLOAD(0), 2)) # 9 + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), 9)) # 0 + + Op.MSTORE(0, Op.SIGNEXTEND(Op.MLOAD(0), 0xFF)) # MAX + + Op.MSTORE(0, Op.LT(11, Op.MLOAD(0))) # 1 + + Op.MSTORE(0, Op.GT(0, Op.MLOAD(0))) # 0 + + Op.MSTORE(0, Op.SLT(Op.SUB(0, 1), Op.MLOAD(0))) # 1 + + Op.MSTORE(0, Op.SGT(Op.MLOAD(0), Op.SUB(0, 1))) # 1 + + Op.MSTORE(0, Op.EQ(Op.MLOAD(0), 0)) # 0 + + Op.MSTORE(0, Op.ISZERO(Op.MLOAD(0))) # 1 + + Op.MSTORE(0, Op.AND(Op.MLOAD(0), 1)) # 1 + + Op.MSTORE(0, Op.OR(Op.MLOAD(0), 0)) # 1 + + Op.MSTORE(0, Op.XOR(Op.MLOAD(0), 2)) # 3 + + Op.MSTORE(0, Op.NOT(Op.MLOAD(0))) # 0xFFF....FFC + + Op.MSTORE(0, Op.BYTE(31, Op.MLOAD(0))) # 0xFC + + Op.MSTORE(0, Op.SHA3(0, 32)) # 0x371f36870d18f32a11fea0f144b021c8b407bb50f8e... + + Op.MSTORE(0, Op.DIV(Op.MLOAD(0), Op.ADD(Op.CALLVALUE, 1))) # same + + Op.MSTORE(0, Op.DIV(Op.MLOAD(0), Op.GASPRICE)) # 0x5831f0d814f4b8434ffdce4ed44... + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH1(1))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH2(0x0FFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH3(0x0FFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH4(0x0FFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH5(0x0FFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH6(0x0FFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH7(0x0FFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH8(0x0FFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH9(0x0FFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH10(0x0FFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH11(0x0FFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH12(0x0FFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH13(0x0FFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH14(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH15(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH16(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH17(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH18(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH19(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH20(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH21(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH22(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH23(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH24(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH25(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH26(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH27(0x0FFFFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH28(0x0FFFFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH29(0x0FFFFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH30(0x0FFFFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH31(0x0FFFFFFFFFFFFFFFFF))) + + Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH32(0x0FFFFFFFFFFFFFFF))) + + Op.RETURN(0, 32) + ) + return code + + +program_all_frontier_opcodes = pytest.param( + make_all_opcode_program(), + 0x5831F0D814F4B8434FFDCE4DD24B00D8B7A3F67F8C316EC51A4D9EEC535AD4A, + id="program_ALL_FRONTIER_OPCODES", +) diff --git a/tests/frontier/scenarios/scenarios/__init__.py b/tests/frontier/scenarios/scenarios/__init__.py new file mode 100644 index 0000000000..20f2893c0e --- /dev/null +++ b/tests/frontier/scenarios/scenarios/__init__.py @@ -0,0 +1,3 @@ +""" +Scenarios common import +""" diff --git a/tests/frontier/scenarios/scenarios/call_combinations.py b/tests/frontier/scenarios/scenarios/call_combinations.py new file mode 100644 index 0000000000..f912de0e32 --- /dev/null +++ b/tests/frontier/scenarios/scenarios/call_combinations.py @@ -0,0 +1,63 @@ +""" +Define Scenario that will put a given program in all call contexts +""" +from typing import List + +from ethereum_test_tools import Address, Alloc +from ethereum_test_tools.vm.opcode import Opcode +from ethereum_test_tools.vm.opcode import Opcodes as Op + +from ..common import Scenario, get_valid_fork_for_call + + +def scenarios_call_combinations(pre: Alloc, operation_contract: Address) -> List[Scenario]: + """ + Generate Scenarios for call combinations + This is the actual test case. We take code that we want to test at operation_contract + and put it in the context of call combinations. + + Example: scenario_contract -> [callcode -> delegatecall]* -> code + We assume that code always returns it's result + That we pass as return value in scenario_contract for the post state verification + """ + list: List[Scenario] = [] + max_scenario_gas = 500000 + first_calls: List[Opcode] = [Op.CALLCODE, Op.DELEGATECALL, Op.STATICCALL] + second_calls: List[Opcode] = [Op.CALL, Op.CALLCODE, Op.DELEGATECALL, Op.STATICCALL, Op.NOOP] + + for first_call in first_calls: + for second_call in second_calls: + if second_call == Op.NOOP: + """Only one call""" + scenario_contract = pre.deploy_contract( + code=first_call(gas=max_scenario_gas, address=operation_contract, ret_size=32) + + Op.RETURN(0, 32) + ) + list.append( + Scenario( + name=f"scenario_{first_call}", + code=scenario_contract, + fork=get_valid_fork_for_call(first_call), + ) + ) + else: + """Call combination""" + sub_contract = pre.deploy_contract( + code=second_call( + gas=Op.SUB(Op.GAS, 100000), address=operation_contract, ret_size=32 + ) + + Op.RETURN(0, 32) + ) + scenario_contract = pre.deploy_contract( + code=first_call(gas=Op.SUB(Op.GAS, 100000), address=sub_contract, ret_size=32) + + Op.RETURN(0, 32) + ) + list.append( + Scenario( + name=f"scenario_{first_call}_{second_call}", + code=scenario_contract, + fork=get_valid_fork_for_call(first_call, second_call), + ) + ) + + return list diff --git a/tests/frontier/scenarios/scenarios/create_combinations.py b/tests/frontier/scenarios/scenarios/create_combinations.py new file mode 100644 index 0000000000..7fc98cefdd --- /dev/null +++ b/tests/frontier/scenarios/scenarios/create_combinations.py @@ -0,0 +1,65 @@ +""" +Define Scenario that will put a given program in create contexts +""" +from typing import List + +from ethereum_test_forks import Constantinople, Frontier +from ethereum_test_tools import Address, Alloc, Bytecode +from ethereum_test_tools.vm.opcode import Macros as Om +from ethereum_test_tools.vm.opcode import Opcode +from ethereum_test_tools.vm.opcode import Opcodes as Op + +from ..common import Scenario, get_valid_fork_for_call + + +def scenarios_create_combinations(pre: Alloc, operation_contract: Address) -> List[Scenario]: + """Generate Scenarios for create combinations""" + list: List[Scenario] = [] + max_scenario_gas = 100000 + create_types: List[Opcode] = [Op.CREATE, Op.CREATE2] + + # run code in create constructor + for create in create_types: + salt = [0] if create == Op.CREATE2 else [] + + # the code result in init code will be actually code of a deployed contract + scenario_contract = pre.deploy_contract( + code=Op.EXTCODECOPY(operation_contract, 0, 0, Op.EXTCODESIZE(operation_contract)) + + Op.MSTORE(0, create(0, 0, Op.EXTCODESIZE(operation_contract), *salt)) + + Op.EXTCODECOPY(Op.MLOAD(0), 0, 0, 32) + + Op.RETURN(0, 32) + ) + list.append( + Scenario( + name=f"scenario_{create}_constructor", + code=scenario_contract, + fork=Frontier if create == Op.CREATE else Constantinople, + ) + ) + + # create a contract with test code and call it + deploy_code = Bytecode( + Op.EXTCODECOPY(operation_contract, 0, 0, Op.EXTCODESIZE(operation_contract)) + + Op.RETURN(0, Op.EXTCODESIZE(operation_contract)) + ) + deploy_code_size: int = int(len(deploy_code.hex()) / 2) + call_types: List[Opcode] = [Op.CALL, Op.CALLCODE, Op.DELEGATECALL, Op.STATICCALL] + + for create in create_types: + for call in call_types: + salt = [0] if create == Op.CREATE2 else [] + scenario_contract = pre.deploy_contract( + code=Om.MSTORE(deploy_code, 0) + + Op.MSTORE(0, create(0, 0, deploy_code_size, *salt)) + + call(gas=max_scenario_gas, address=Op.MLOAD(0), ret_size=32) + + Op.RETURN(0, 32) + ) + list.append( + Scenario( + name=f"scenario_{create}_then_{call}", + code=scenario_contract, + fork=get_valid_fork_for_call(call, create), + ) + ) + + return list diff --git a/tests/frontier/scenarios/scenarios/revert_combinations.py b/tests/frontier/scenarios/scenarios/revert_combinations.py new file mode 100644 index 0000000000..d7b37c056c --- /dev/null +++ b/tests/frontier/scenarios/scenarios/revert_combinations.py @@ -0,0 +1,36 @@ +""" +Define Scenario that will run a given program and then revert +""" +from typing import List + +from ethereum_test_forks import Frontier +from ethereum_test_tools import Address, Alloc +from ethereum_test_tools.vm.opcode import Macro +from ethereum_test_tools.vm.opcode import Macros as Om +from ethereum_test_tools.vm.opcode import Opcode +from ethereum_test_tools.vm.opcode import Opcodes as Op + +from ..common import Scenario + + +def scenarios_revert_combinations(pre: Alloc, operation_contract: Address) -> List[Scenario]: + """Generate Scenarios for revert combinations""" + list: List[Scenario] = [] + max_scenario_gas = 100000 + revert_types: List[Opcode | Macro] = [Op.REVERT, Op.STOP, Om.OOG] + for revert in revert_types: + scenario_contract = pre.deploy_contract( + code=Op.CALL(gas=max_scenario_gas, address=operation_contract, ret_size=32) + + revert + + Op.RETURN(0, 32) + ) + list.append( + Scenario( + name=f"scenario_revert_by_{revert}", + code=scenario_contract, + fork=Frontier, + reverts=True, + ) + ) + + return list diff --git a/tests/frontier/scenarios/test_scenarios.py b/tests/frontier/scenarios/test_scenarios.py new file mode 100644 index 0000000000..dec43ea053 --- /dev/null +++ b/tests/frontier/scenarios/test_scenarios.py @@ -0,0 +1,157 @@ +""" +Call every possible opcode and test that the subcall is successful +if the opcode is supported by the fork supports and fails otherwise. +""" + +from typing import Dict, List + +import pytest + +from ethereum_test_forks import Fork +from ethereum_test_tools import Account, Alloc, Bytecode, Environment, StateTestFiller, Transaction +from ethereum_test_tools.vm.opcode import Opcodes as Op + +from .common import Scenario +from .programs.all_frontier_opcodes import program_all_frontier_opcodes +from .scenarios.call_combinations import scenarios_call_combinations +from .scenarios.create_combinations import scenarios_create_combinations +from .scenarios.revert_combinations import scenarios_revert_combinations + +REFERENCE_SPEC_GIT_PATH = "N/A" +REFERENCE_SPEC_VERSION = "N/A" + + +@pytest.fixture +def scenarios(pre: Alloc, operation: Bytecode) -> List[Scenario]: + """ + This is the main parametrization vector + Define list of contracts that execute scenarios for a given operation + """ + list: List[Scenario] = [] + operation_contract = pre.deploy_contract(code=operation) + + call_combinations = scenarios_call_combinations(pre, operation_contract) + for combination in call_combinations: + list.append(combination) + + call_combinations = scenarios_create_combinations(pre, operation_contract) + for combination in call_combinations: + list.append(combination) + + revert_combinations = scenarios_revert_combinations(pre, operation_contract) + for combination in revert_combinations: + list.append(combination) + + """ + // 21. 0x00FD Run the code, call a contract that reverts, then run again + // 22. 0x00FE Run the code, call a contract that goes out of gas, then run again + // 23. 0x00FF Run the code, call a contract that self-destructs, then run again + // 34. 0x60BACCFA57 Call recurse to the limit + """ + + return list + + +# This opcodes require fork +# SHL +# SHR +# SAR +# SELFBALANCE + +# This opcodes change on the context of scenario +# ADDRESS +# BALANCE +# ORIGIN +# CALLER +# CALLDATALOAD +# CALLDATASIZE +# CALLDATACOPY +# CODESIZE +# CODECOPY +# EXTCODESIZE +# EXTCODECOPY +# RETURNDATASIZE +# RETURNDATACOPY +# EXTCODEHASH +# BLOCKHASH + +# This opcodes change on evm config when generating the test +# COINBASE +# TIMESTAMP +# NUMBER +# PREVRANDAO +# GASLIMIT +# CHAINID +# BASEFEE +# BLOBHASH +# BLOBBASEFEE + + +@pytest.mark.valid_from("Frontier") +@pytest.mark.parametrize( + "operation, result", + [ + # Check that ORIGIN stays the same in all contexts + pytest.param( + Op.MSTORE(0, Op.ORIGIN) + Op.RETURN(0, 32), + 0xA94F5374FCE5EDBC8E2A8697C15331677E6EBF0B, + id="program_ORIGIN", + ), + program_all_frontier_opcodes, + ], +) +def test_scenarios( + state_test: StateTestFiller, + pre: Alloc, + fork: Fork, + result: int, + scenarios, +): + """ + Test given operation in different scenarios + Verify that it's return value equal to expected result on every scenario, + that is valid for the given fork + + Note: Don't use pytest parametrize for scenario production, because scenarios will be complex + Generate one test file for [each operation] * [each scenario] to save space + As well as operations will be complex too + """ + tx_origin = pre.fund_eoa() + code_worked = 1000 + + runner_contract = pre.deploy_contract( + code=sum( + Op.MSTORE(0, 0) + + Op.CALL(1000000, scenario.code, 0, 0, 0, 0, 32) + + Op.SSTORE(index, Op.MLOAD(0)) + for index, (scenario) in enumerate(scenarios) + ) + + Op.SSTORE(code_worked, 1) + ) + post = { + runner_contract: Account( + storage={ + **{ + index: result if fork >= scenario.fork and scenario.not_reverting else 0 + for index, (scenario) in enumerate(scenarios) + }, + code_worked: 1, + } + ), + } + + hint: Dict[int, str] = {} + for index, (scenario) in enumerate(scenarios): + hint[index] = scenario.name + + tx = Transaction( + sender=tx_origin, + gas_limit=500_000_000, + gas_price=10, + to=runner_contract, + data=b"", + value=0, + protected=False, + ) + + state_test(env=Environment(), pre=pre, post=post, tx=tx, post_hint=hint)