diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4c2dc3437f..3a1fcec650 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,7 @@ Test fixtures for use by clients are available for each release on the [Github r - 🐞 Fix incorrect `!=` operator for `FixedSizeBytes` ([#477](https://github.com/ethereum/execution-spec-tests/pull/477)). - ✨ Add Macro enum that represents byte sequence of Op instructions ([#457](https://github.com/ethereum/execution-spec-tests/pull/457)) +- ✨ Number of parameters used to call opcodes (to generate bytecode) is now checked ([#492](https://github.com/ethereum/execution-spec-tests/pull/492)). - ✨ Libraries have been refactored to use `pydantic` for type checking in most test types ([#486](https://github.com/ethereum/execution-spec-tests/pull/486)). ### 🔧 EVM Tools diff --git a/src/ethereum_test_tools/tests/test_vm.py b/src/ethereum_test_tools/tests/test_vm.py index dc71ae91ed..b7f97ea30a 100644 --- a/src/ethereum_test_tools/tests/test_vm.py +++ b/src/ethereum_test_tools/tests/test_vm.py @@ -138,7 +138,7 @@ b"\x60\x08\x60\x07\x60\x06\x60\x05\x60\x04\x60\x7b\x60\x01\xf1", ), ( - Op.CREATE(1, Address(12), 4, 5, 6, 7, 8), + Op.CREATE(1, Address(12), 4, 5, 6, 7, 8, unchecked=True), b"\x60\x08\x60\x07\x60\x06\x60\x05\x60\x04\x73\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x60\x01\xf0", ), diff --git a/src/ethereum_test_tools/vm/opcode.py b/src/ethereum_test_tools/vm/opcode.py index e2bbb168ea..7283aec356 100644 --- a/src/ethereum_test_tools/vm/opcode.py +++ b/src/ethereum_test_tools/vm/opcode.py @@ -122,7 +122,9 @@ def __new__( return obj raise TypeError("Opcode constructor '__new__' didn't return an instance!") - def __call__(self, *args_t: Union[int, bytes, str, "Opcode", FixedSizeBytes]) -> bytes: + def __call__( + self, *args_t: Union[int, bytes, str, "Opcode", FixedSizeBytes], unchecked: bool = False + ) -> bytes: """ Makes all opcode instances callable to return formatted bytecode, which constitutes a data portion, that is located after the opcode byte, and pre-opcode bytecode, which is normally @@ -180,6 +182,12 @@ def __call__(self, *args_t: Union[int, bytes, str, "Opcode", FixedSizeBytes]) -> raise TypeError("Opcode data portion must be either an int or bytes/hex string") # The rest of the arguments conform the stack. + if len(args) != self.popped_stack_items and not unchecked: + raise ValueError( + f"Opcode {self._name_} requires {self.popped_stack_items} stack elements, but " + f"{len(args)} were provided. Use 'unchecked=True' parameter to ignore this check." + ) + while len(args) > 0: data = args.pop() if isinstance(data, int) or isinstance(data, FixedSizeBytes): diff --git a/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py b/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py index 81500aa1e0..a0a83cd993 100644 --- a/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py +++ b/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py @@ -143,7 +143,7 @@ def __new__(cls, name, bases, classdict): # noqa: D102 + Op.SSTORE(1, Op.TLOAD(0)) + Op.SSTORE(2, Op.TLOAD(1)) ), - "callee_bytecode": Op.TSTORE(1), + "callee_bytecode": Op.TSTORE(1, unchecked=True), "expected_caller_storage": {0: 0, 1: 420, 2: 420}, "expected_callee_storage": {}, } @@ -182,13 +182,9 @@ def __new__(cls, name, bases, classdict): # noqa: D102 gas_limit = Spec.TSTORE_GAS_COST + (PUSH_OPCODE_COST * 2) - 1 if opcode == Op.DELEGATECALL: - contract_call = opcode( - opcode(gas_limit, callee_address, 0, 0, 0, 0), - ) + contract_call = opcode(gas_limit, callee_address, 0, 0, 0, 0) elif opcode in [Op.CALL, Op.CALLCODE]: - contract_call = opcode( - opcode(gas_limit, callee_address, 0, 0, 0, 0, 0), - ) + contract_call = opcode(gas_limit, callee_address, 0, 0, 0, 0, 0) else: raise ValueError("Unexpected opcode.") classdict[f"{opcode._name_}_WITH_OUT_OF_GAS"] = { @@ -199,18 +195,7 @@ def __new__(cls, name, bases, classdict): # noqa: D102 "caller_bytecode": ( Op.TSTORE(0, 420) + Op.TSTORE(1, 420) - + Op.SSTORE( - 0, - opcode( - Spec.TSTORE_GAS_COST + (PUSH_OPCODE_COST * 2) - 1, - callee_address, - 0, - 0, - 0, - 0, - 0, - ), - ) + + Op.SSTORE(0, contract_call) + Op.SSTORE(1, Op.TLOAD(0)) + Op.SSTORE(2, Op.TLOAD(1)) ), @@ -286,7 +271,9 @@ class CallContextTestCases(PytestParameterEnum, metaclass=DynamicCallContextTest + Op.SSTORE(0, Op.STATICCALL(0xFFFF, callee_address, 0, 0, 0, 0)) # limit gas + Op.SSTORE(1, Op.TLOAD(0)) ), - "callee_bytecode": Op.TSTORE(0), # calling with stack underflow still fails + "callee_bytecode": Op.TSTORE( + 0, unchecked=True + ), # calling with stack underflow still fails "expected_caller_storage": {0: 0, 1: 420}, "expected_callee_storage": {}, } diff --git a/tests/shanghai/eip3860_initcode/test_initcode.py b/tests/shanghai/eip3860_initcode/test_initcode.py index d9dae80879..3bd30ff1e9 100644 --- a/tests/shanghai/eip3860_initcode/test_initcode.py +++ b/tests/shanghai/eip3860_initcode/test_initcode.py @@ -483,13 +483,13 @@ def create_code(self, opcode: Op, initcode: Initcode): # noqa: D102 # stack: [Gas 2, Call Result, Gas 1] + Op.SWAP1 # stack: [Call Result, Gas 2, Gas 1] - + Op.SSTORE(0) + + Op.SSTORE(0, unchecked=True) # stack: [Gas 2, Gas 1] + Op.SWAP1 # stack: [Gas 1, Gas 2] + Op.SUB # stack: [Gas 1 - Gas 2] - + Op.SSTORE(1) + + Op.SSTORE(1, unchecked=True) ) @pytest.fixture