Skip to content

feat(tests): test all opcodes by fork #748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions converted-ethereum-tests.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
([#748](https://github.com/ethereum/execution-spec-tests/pull/748))
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
Expand Down
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Test fixtures for use by clients are available for each release on the [Github r

- ✨ EIP-4844 test `tests/cancun/eip4844_blobs/test_point_evaluation_precompile.py` includes an EOF test case ([#610](https://github.com/ethereum/execution-spec-tests/pull/610)).
- ✨ Example test `tests/frontier/opcodes/test_dup.py` now includes EOF parametrization ([#610](https://github.com/ethereum/execution-spec-tests/pull/610)).
- ✨ Convert all opcodes validation test `tests/frontier/opcodes/test_all_opcodes.py` ([#748](https://github.com/ethereum/execution-spec-tests/pull/748)).

### πŸ› οΈ Framework

Expand All @@ -29,6 +30,7 @@ Test fixtures for use by clients are available for each release on the [Github r
- ✨ Added optional parameter to all `with_all_*` markers to specify a lambda function that filters the parametrized values ([#739](https://github.com/ethereum/execution-spec-tests/pull/739)).
- ✨ Added [`extend_with_defaults` utility function](https://ethereum.github.io/execution-spec-tests/main/writing_tests/writing_a_new_test/#ethereum_test_tools.utility.pytest.extend_with_defaults), which helps extend test case parameter sets with default values. `@pytest.mark.parametrize` ([#739](https://github.com/ethereum/execution-spec-tests/pull/739)).
- ✨ Added `Container.Init` to `ethereum_test_types.EOF.V1` package, which allows generation of an EOF init container more easily ([#739](https://github.com/ethereum/execution-spec-tests/pull/739)).
- ✨ Introduce method valid_opcodes() to the fork class ([#748](https://github.com/ethereum/execution-spec-tests/pull/748)).
- 🐞 Fixed `consume` exit code return values, ensuring that pytest's return value is correctly propagated to the shell. This allows the shell to accurately reflect the test results (e.g., failures) based on the pytest exit code ([#765](https://github.com/ethereum/execution-spec-tests/pull/765)).

### πŸ”§ EVM Tools
Expand Down
10 changes: 10 additions & 0 deletions src/ethereum_test_forks/base_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,16 @@ def call_opcodes(
"""
pass

@classmethod
@abstractmethod
def valid_opcodes(
cls,
) -> List[Opcodes]:
"""
Returns the list of Opcodes that are valid to work on this fork.
"""
pass

@classmethod
@abstractmethod
def create_opcodes(
Expand Down
214 changes: 214 additions & 0 deletions src/ethereum_test_forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,145 @@ def call_opcodes(
(Opcodes.CALLCODE, EVMCodeType.LEGACY),
]

@classmethod
def valid_opcodes(
cls,
) -> List[Opcodes]:
"""
Returns the list of Opcodes that are valid to work on this fork.
"""
return [
Opcodes.STOP,
Opcodes.ADD,
Opcodes.MUL,
Opcodes.SUB,
Opcodes.DIV,
Opcodes.SDIV,
Opcodes.MOD,
Opcodes.SMOD,
Opcodes.ADDMOD,
Opcodes.MULMOD,
Opcodes.EXP,
Opcodes.SIGNEXTEND,
Opcodes.LT,
Opcodes.GT,
Opcodes.SLT,
Opcodes.SGT,
Opcodes.EQ,
Opcodes.ISZERO,
Opcodes.AND,
Opcodes.OR,
Opcodes.XOR,
Opcodes.NOT,
Opcodes.BYTE,
Opcodes.SHA3,
Opcodes.ADDRESS,
Opcodes.BALANCE,
Opcodes.ORIGIN,
Opcodes.CALLER,
Opcodes.CALLVALUE,
Opcodes.CALLDATALOAD,
Opcodes.CALLDATASIZE,
Opcodes.CALLDATACOPY,
Opcodes.CODESIZE,
Opcodes.CODECOPY,
Opcodes.GASPRICE,
Opcodes.EXTCODESIZE,
Opcodes.EXTCODECOPY,
Opcodes.BLOCKHASH,
Opcodes.COINBASE,
Opcodes.TIMESTAMP,
Opcodes.NUMBER,
Opcodes.PREVRANDAO,
Opcodes.GASLIMIT,
Opcodes.POP,
Opcodes.MLOAD,
Opcodes.MSTORE,
Opcodes.MSTORE8,
Opcodes.SLOAD,
Opcodes.SSTORE,
Opcodes.PC,
Opcodes.MSIZE,
Opcodes.GAS,
Opcodes.JUMP,
Opcodes.JUMPI,
Opcodes.JUMPDEST,
Opcodes.PUSH1,
Opcodes.PUSH2,
Opcodes.PUSH3,
Opcodes.PUSH4,
Opcodes.PUSH5,
Opcodes.PUSH6,
Opcodes.PUSH7,
Opcodes.PUSH8,
Opcodes.PUSH9,
Opcodes.PUSH10,
Opcodes.PUSH11,
Opcodes.PUSH12,
Opcodes.PUSH13,
Opcodes.PUSH14,
Opcodes.PUSH15,
Opcodes.PUSH16,
Opcodes.PUSH17,
Opcodes.PUSH18,
Opcodes.PUSH19,
Opcodes.PUSH20,
Opcodes.PUSH21,
Opcodes.PUSH22,
Opcodes.PUSH23,
Opcodes.PUSH24,
Opcodes.PUSH25,
Opcodes.PUSH26,
Opcodes.PUSH27,
Opcodes.PUSH28,
Opcodes.PUSH29,
Opcodes.PUSH30,
Opcodes.PUSH31,
Opcodes.PUSH32,
Opcodes.DUP1,
Opcodes.DUP2,
Opcodes.DUP3,
Opcodes.DUP4,
Opcodes.DUP5,
Opcodes.DUP6,
Opcodes.DUP7,
Opcodes.DUP8,
Opcodes.DUP9,
Opcodes.DUP10,
Opcodes.DUP11,
Opcodes.DUP12,
Opcodes.DUP13,
Opcodes.DUP14,
Opcodes.DUP15,
Opcodes.DUP16,
Opcodes.SWAP1,
Opcodes.SWAP2,
Opcodes.SWAP3,
Opcodes.SWAP4,
Opcodes.SWAP5,
Opcodes.SWAP6,
Opcodes.SWAP7,
Opcodes.SWAP8,
Opcodes.SWAP9,
Opcodes.SWAP10,
Opcodes.SWAP11,
Opcodes.SWAP12,
Opcodes.SWAP13,
Opcodes.SWAP14,
Opcodes.SWAP15,
Opcodes.SWAP16,
Opcodes.LOG0,
Opcodes.LOG1,
Opcodes.LOG2,
Opcodes.LOG3,
Opcodes.LOG4,
Opcodes.CREATE,
Opcodes.CALL,
Opcodes.CALLCODE,
Opcodes.RETURN,
Opcodes.SELFDESTRUCT,
]

@classmethod
def create_opcodes(
cls, block_number: int = 0, timestamp: int = 0
Expand Down Expand Up @@ -254,6 +393,15 @@ def call_opcodes(
Homestead, cls
).call_opcodes(block_number, timestamp)

@classmethod
def valid_opcodes(
cls,
) -> List[Opcodes]:
"""
Returns the list of Opcodes that are valid to work on this fork.
"""
return [Opcodes.DELEGATECALL] + super(Homestead, cls).valid_opcodes()


class Byzantium(Homestead):
"""
Expand Down Expand Up @@ -290,6 +438,15 @@ def call_opcodes(
Byzantium, cls
).call_opcodes(block_number, timestamp)

@classmethod
def valid_opcodes(
cls,
) -> List[Opcodes]:
"""
Returns the list of Opcodes that are valid to work on this fork.
"""
return [Opcodes.RETURNDATASIZE, Opcodes.STATICCALL] + super(Byzantium, cls).valid_opcodes()


class Constantinople(Byzantium):
"""
Expand All @@ -315,6 +472,21 @@ def create_opcodes(
Constantinople, cls
).create_opcodes(block_number, timestamp)

@classmethod
def valid_opcodes(
cls,
) -> List[Opcodes]:
"""
Returns the list of Opcodes that are valid to work on this fork.
"""
return [
Opcodes.SHL,
Opcodes.SHR,
Opcodes.SAR,
Opcodes.EXTCODEHASH,
Opcodes.CREATE2,
] + super(Constantinople, cls).valid_opcodes()


class ConstantinopleFix(Constantinople, solc_name="constantinople"):
"""
Expand All @@ -336,6 +508,15 @@ def precompiles(cls, block_number: int = 0, timestamp: int = 0) -> List[Address]
"""
return [Address(9)] + super(Istanbul, cls).precompiles(block_number, timestamp)

@classmethod
def valid_opcodes(
cls,
) -> List[Opcodes]:
"""
Returns the list of Opcodes that are valid to work on this fork.
"""
return [Opcodes.CHAINID, Opcodes.SELFBALANCE] + super(Istanbul, cls).valid_opcodes()


# Glacier forks skipped, unless explicitly specified
class MuirGlacier(Istanbul, solc_name="istanbul", ignore=True):
Expand Down Expand Up @@ -392,6 +573,15 @@ def contract_creating_tx_types(cls, block_number: int = 0, timestamp: int = 0) -
"""
return [2] + super(London, cls).contract_creating_tx_types(block_number, timestamp)

@classmethod
def valid_opcodes(
cls,
) -> List[Opcodes]:
"""
Returns the list of Opcodes that are valid to work on this fork.
"""
return [Opcodes.BASEFEE] + super(London, cls).valid_opcodes()


# Glacier forks skipped, unless explicitly specified
class ArrowGlacier(London, solc_name="london", ignore=True):
Expand Down Expand Up @@ -471,6 +661,15 @@ def engine_new_payload_version(
"""
return 2

@classmethod
def valid_opcodes(
cls,
) -> List[Opcodes]:
"""
Returns the list of Opcodes that are valid to work on this fork.
"""
return [Opcodes.PUSH0] + super(Shanghai, cls).valid_opcodes()


class Cancun(Shanghai):
"""
Expand Down Expand Up @@ -572,6 +771,21 @@ def engine_new_payload_beacon_root(cls, block_number: int = 0, timestamp: int =
"""
return True

@classmethod
def valid_opcodes(
cls,
) -> List[Opcodes]:
"""
Returns the list of Opcodes that are valid to work on this fork.
"""
return [
Opcodes.BLOBHASH,
Opcodes.BLOBBASEFEE,
Opcodes.TLOAD,
Opcodes.TSTORE,
Opcodes.MCOPY,
] + super(Cancun, cls).valid_opcodes()


class Prague(Cancun):
"""
Expand Down
12 changes: 12 additions & 0 deletions src/ethereum_test_tools/tests/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from ethereum_test_specs import StateTest
from ethereum_test_types import Alloc, Environment, Transaction
from ethereum_test_vm import Opcodes as Op
from ethereum_test_vm import UndefinedOpcodes
from evm_transition_tool import GethTransitionTool

from ..code import CalldataCase, Case, Conditional, Initcode, Solc, Switch, Yul
Expand Down Expand Up @@ -640,3 +641,14 @@ def test_switch(tx_data: bytes, switch_bytecode: bytes, expected_storage: Mappin
fixture_format=FixtureFormats.BLOCKCHAIN_TEST,
eips=None,
)


def test_full_opcode_range():
"""
Test that the full opcode range is covered by the opcode set defined by
Opcodes and UndefineOpcodes.
"""
assert len(set(Op) & set(UndefinedOpcodes)) == 0
full_possible_opcode_set = set(Op) | set(UndefinedOpcodes)
assert len(full_possible_opcode_set) == 256
assert set(op.hex() for op in full_possible_opcode_set) == set(f"{i:02x}" for i in range(256))
Loading
Loading