From 544568b6e7b244f3bce4cf3d2c5034eaa43a77c6 Mon Sep 17 00:00:00 2001 From: Dimitry Kh Date: Thu, 15 Aug 2024 10:42:04 +0200 Subject: [PATCH] test all opcodes --- converted-ethereum-tests.txt | 5 + tests/frontier/opcodes/test_all_opcodes.py | 205 +++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 tests/frontier/opcodes/test_all_opcodes.py diff --git a/converted-ethereum-tests.txt b/converted-ethereum-tests.txt index 2177ef29fb..91abcf3ce4 100644 --- a/converted-ethereum-tests.txt +++ b/converted-ethereum-tests.txt @@ -1,3 +1,8 @@ +([#748](https://github.com/ethereum/execution-spec-tests/pull/748)) +GeneralStateTests/stBadOpcode/undefinedOpcodeFirstByte.json +GeneralStateTests/stBadOpcode/badOpcodes.json +GeneralStateTests/stBugs/evmBytecode.json + ([#497](https://github.com/ethereum/execution-spec-tests/pull/497)) GeneralStateTests/stCreate2/call_outsize_then_create2_successful_then_returndatasize.json GeneralStateTests/stCreate2/call_then_create2_successful_then_returndatasize.json diff --git a/tests/frontier/opcodes/test_all_opcodes.py b/tests/frontier/opcodes/test_all_opcodes.py new file mode 100644 index 0000000000..8746414d2a --- /dev/null +++ b/tests/frontier/opcodes/test_all_opcodes.py @@ -0,0 +1,205 @@ +""" +Call every possible opcode to see if it is recognized by the evm +If opcode is undefined the subcall will fail +""" + +from typing import Dict, List + +import pytest + +from ethereum_test_forks import ( + Berlin, + Byzantium, + Cancun, + Constantinople, + ConstantinopleFix, + Fork, + Frontier, + Homestead, + Istanbul, + London, + Paris, + Shanghai, +) +from ethereum_test_tools import ( + Account, + Address, + Alloc, + Bytecode, + Environment, + StateTestFiller, + Transaction, +) +from ethereum_test_tools.vm.opcode import Opcode +from ethereum_test_tools.vm.opcode import Opcodes as Op +from ethereum_test_tools.vm.opcode import UndefinedOpcodes + +REFERENCE_SPEC_GIT_PATH = "N/A" +REFERENCE_SPEC_VERSION = "N/A" +all_opcodes = set(Op) +undefined_opcodes = set(UndefinedOpcodes) + +opcodes_by_fork: Dict[Fork, List[Opcode]] = {} +opcodes_by_fork[Frontier] = [ + Op.STOP, + Op.ADD, + Op.MUL, + Op.SUB, + Op.DIV, + Op.SDIV, + Op.MOD, + Op.SMOD, + Op.ADDMOD, + Op.MULMOD, + Op.EXP, + Op.SIGNEXTEND, + Op.LT, + Op.GT, + Op.SLT, + Op.SGT, + Op.EQ, + Op.ISZERO, + Op.AND, + Op.OR, + Op.XOR, + Op.NOT, + Op.BYTE, + Op.SHA3, + Op.ADDRESS, + Op.BALANCE, + Op.ORIGIN, + Op.CALLER, + Op.CALLVALUE, + Op.CALLDATALOAD, + Op.CALLDATASIZE, + Op.CALLDATACOPY, + Op.CODESIZE, + Op.CODECOPY, + Op.GASPRICE, + Op.EXTCODESIZE, + Op.EXTCODECOPY, + Op.BLOCKHASH, + Op.COINBASE, + Op.TIMESTAMP, + Op.NUMBER, + Op.PREVRANDAO, + Op.GASLIMIT, + Op.POP, + Op.MLOAD, + Op.MSTORE, + Op.MSTORE8, + Op.SLOAD, + Op.SSTORE, + Op.PC, + Op.MSIZE, + Op.GAS, + Op.JUMPDEST, + *[Opcode(code) for code in range(0x60, 0x80)], # PUSH1-32 + *[Opcode(code) for code in range(0x80, 0x90)], # DUP1-16 + *[Opcode(code) for code in range(0x90, 0xA0)], # SWAP1-16 + Op.LOG0, + Op.LOG1, + Op.LOG2, + Op.LOG3, + Op.LOG4, + Op.CREATE, + Op.CALL, + Op.CALLCODE, + Op.RETURN, + Op.SELFDESTRUCT, +] +opcodes_by_fork[Homestead] = opcodes_by_fork[Frontier] + [Op.DELEGATECALL] +opcodes_by_fork[Byzantium] = opcodes_by_fork[Homestead] + [Op.RETURNDATASIZE, Op.STATICCALL] +opcodes_by_fork[Constantinople] = opcodes_by_fork[Byzantium] + [ + Op.SHL, + Op.SHR, + Op.SAR, + Op.EXTCODEHASH, + Op.CREATE2, +] +opcodes_by_fork[ConstantinopleFix] = opcodes_by_fork[Constantinople] +opcodes_by_fork[Istanbul] = opcodes_by_fork[ConstantinopleFix] + [Op.CHAINID, Op.SELFBALANCE] +opcodes_by_fork[Berlin] = opcodes_by_fork[Istanbul] +opcodes_by_fork[London] = opcodes_by_fork[Berlin] + [Op.BASEFEE] +opcodes_by_fork[Paris] = opcodes_by_fork[London] +opcodes_by_fork[Shanghai] = opcodes_by_fork[Paris] + [Op.PUSH0] +opcodes_by_fork[Cancun] = opcodes_by_fork[Shanghai] + [ + Op.BLOBHASH, + Op.BLOBBASEFEE, + Op.TLOAD, + Op.TSTORE, + Op.MCOPY, +] + + +def prepare_stack(opcode: Opcode) -> Bytecode: + """Prepare the stack for opcode""" + if opcode == Op.CREATE: + return Op.MSTORE(0, 0x6001600155) + Op.PUSH1(5) + Op.PUSH1(27) + Op.PUSH1(5) + if opcode == Op.CREATE2: + return Op.MSTORE(0, 0x6001600155) + Op.PUSH1(1) + Op.PUSH1(5) + Op.PUSH1(27) + Op.PUSH1(5) + return Op.PUSH1(0x01) * 32 + + +@pytest.mark.valid_from("Frontier") +def test_all_opcodes(state_test: StateTestFiller, pre: Alloc, fork: Fork): + """Check if the opcode supported on given fork""" + code_worked = 1000 + + code_contract: Dict[Opcode, Address] = {} + for opcode in sorted(all_opcodes | undefined_opcodes): + code_contract[opcode] = pre.deploy_contract( + balance=10, + code=prepare_stack(opcode) + opcode, + storage={}, + ) + + # EVM code to make the call and store the result + caller = pre.deploy_contract( + code=sum( + Op.SSTORE( + Op.PUSH1(opcode.int()), + Op.CALL(1_000_000, opcode_address, 0, 0, 0, 0, 0), + ) + for opcode, opcode_address in code_contract.items() + ) + + Op.SSTORE(code_worked, 1) + + Op.STOP, + ) + + post = { + caller: Account( + storage={**{opcode.int(): 1 for opcode in opcodes_by_fork[fork]}, code_worked: 1} + ), + } + + tx = Transaction( + sender=pre.fund_eoa(), + gas_limit=500_000_000, + to=caller, + data=b"", + value=0, + protected=False, + ) + + state_test(env=Environment(), pre=pre, post=post, tx=tx) + + +@pytest.mark.valid_from("Cancun") +def test_cover_revert(state_test: StateTestFiller, pre: Alloc): + """Cover state revert from original tests""" + caller = pre.deploy_contract( + code=Op.SSTORE(1, 1) + Op.REVERT(0, 0), + ) + + tx = Transaction( + sender=pre.fund_eoa(), + gas_limit=1_000_000, + to=caller, + data=b"01", + value=0, + protected=False, + ) + + post = {} + state_test(env=Environment(), pre=pre, post=post, tx=tx)