diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1106cf5775..1341ef4136 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -34,6 +34,7 @@ Test fixtures for use by clients are available for each release on the [Github r - ✨ Add tests for `modexp` and `ripemd` precompiled contracts ([#1691](https://github.com/ethereum/execution-specs/pull/1691)). - ✨ Add `ecrecover` precompile tests originating form `evmone` unittests ([#1685](https://github.com/ethereum/execution-specs/pull/1685)). - ✨ Add stack overflow tests and expand `BLOCKHASH` tests ([#1728](https://github.com/ethereum/execution-specs/pull/1728)). +- ✨ Add tests that EIP-1559 and EIP-2930 typed txs are invalid and void before their fork ([#1754](https://github.com/ethereum/execution-specs/pull/1754)). ## [v5.3.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v5.3.0) - 2025-10-09 diff --git a/packages/testing/src/execution_testing/client_clis/clis/ethrex.py b/packages/testing/src/execution_testing/client_clis/clis/ethrex.py index d2f3aa7a33..5ef952cfac 100644 --- a/packages/testing/src/execution_testing/client_clis/clis/ethrex.py +++ b/packages/testing/src/execution_testing/client_clis/clis/ethrex.py @@ -51,6 +51,9 @@ class EthrexExceptionMapper(ExceptionMapper): TransactionException.TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH: ( r"blob version not supported|Invalid blob versioned hash" ), + TransactionException.TYPE_2_TX_PRE_FORK: ( + r"Type 2 transactions are not supported before the London fork" + ), TransactionException.TYPE_3_TX_PRE_FORK: ( r"blob versioned hashes not supported|" r"Type 3 transactions are not supported before the Cancun fork" diff --git a/packages/testing/src/execution_testing/client_clis/clis/evmone.py b/packages/testing/src/execution_testing/client_clis/clis/evmone.py index 21e1e07ae6..da9c0a1d7d 100644 --- a/packages/testing/src/execution_testing/client_clis/clis/evmone.py +++ b/packages/testing/src/execution_testing/client_clis/clis/evmone.py @@ -350,6 +350,8 @@ class EvmoneExceptionMapper(ExceptionMapper): ), TransactionException.TYPE_4_TX_PRE_FORK: "transaction type not supported", TransactionException.TYPE_3_TX_PRE_FORK: "transaction type not supported", + TransactionException.TYPE_2_TX_PRE_FORK: "transaction type not supported", + TransactionException.TYPE_1_TX_PRE_FORK: "transaction type not supported", TransactionException.TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH: "invalid blob hash version", TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED: "blob gas limit exceeded", TransactionException.TYPE_3_TX_ZERO_BLOBS: "empty blob hashes list", diff --git a/packages/testing/src/execution_testing/client_clis/clis/execution_specs.py b/packages/testing/src/execution_testing/client_clis/clis/execution_specs.py index ab3806459b..2af430fb84 100644 --- a/packages/testing/src/execution_testing/client_clis/clis/execution_specs.py +++ b/packages/testing/src/execution_testing/client_clis/clis/execution_specs.py @@ -195,6 +195,12 @@ class ExecutionSpecsExceptionMapper(ExceptionMapper): TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS: ( r"InsufficientMaxFeePerGasError|InvalidBlock" # Temporary solution for issue #1981. ), + TransactionException.TYPE_1_TX_PRE_FORK: ( + r"module '.*transactions' has no attribute 'AccessListTransaction'" + ), + TransactionException.TYPE_2_TX_PRE_FORK: ( + r"'.*transactions' has no attribute 'FeeMarketTransaction'" + ), TransactionException.TYPE_3_TX_PRE_FORK: ( r"module '.*transactions' has no attribute 'BlobTransaction'" ), diff --git a/packages/testing/src/execution_testing/client_clis/clis/geth.py b/packages/testing/src/execution_testing/client_clis/clis/geth.py index 44797686df..1ce81e2561 100644 --- a/packages/testing/src/execution_testing/client_clis/clis/geth.py +++ b/packages/testing/src/execution_testing/client_clis/clis/geth.py @@ -54,6 +54,12 @@ class GethExceptionMapper(ExceptionMapper): TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS: ( "max priority fee per gas higher than max fee per gas" ), + TransactionException.TYPE_1_TX_PRE_FORK: ( + "transaction type not supported" + ), + TransactionException.TYPE_2_TX_PRE_FORK: ( + "transaction type not supported" + ), TransactionException.TYPE_3_TX_PRE_FORK: ( "transaction type not supported" ), diff --git a/packages/testing/src/execution_testing/client_clis/clis/nethermind.py b/packages/testing/src/execution_testing/client_clis/clis/nethermind.py index ac640c052d..54b36a2308 100644 --- a/packages/testing/src/execution_testing/client_clis/clis/nethermind.py +++ b/packages/testing/src/execution_testing/client_clis/clis/nethermind.py @@ -395,6 +395,12 @@ class NethermindExceptionMapper(ExceptionMapper): TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS: ( "InsufficientMaxFeePerBlobGas: Not enough to cover blob gas fee" ), + TransactionException.TYPE_1_TX_PRE_FORK: ( + "InvalidTxType: Transaction type in Custom is not supported" + ), + TransactionException.TYPE_2_TX_PRE_FORK: ( + "InvalidTxType: Transaction type in Custom is not supported" + ), TransactionException.TYPE_3_TX_PRE_FORK: ( "InvalidTxType: Transaction type in Custom is not supported" ), diff --git a/packages/testing/src/execution_testing/exceptions/exceptions.py b/packages/testing/src/execution_testing/exceptions/exceptions.py index 53d57f06f8..8abf5d031b 100644 --- a/packages/testing/src/execution_testing/exceptions/exceptions.py +++ b/packages/testing/src/execution_testing/exceptions/exceptions.py @@ -269,6 +269,10 @@ class TransactionException(ExceptionBase): """ Transaction's initcode for a contract-creating transaction is too large. """ + TYPE_1_TX_PRE_FORK = auto() + """Transaction type 1 included before activation fork.""" + TYPE_2_TX_PRE_FORK = auto() + """Transaction type 2 included before activation fork.""" TYPE_3_TX_PRE_FORK = auto() """Transaction type 3 included before activation fork.""" TYPE_3_TX_ZERO_BLOBS_PRE_FORK = auto() diff --git a/packages/testing/src/execution_testing/exceptions/exceptions/transaction.py b/packages/testing/src/execution_testing/exceptions/exceptions/transaction.py index ee67b0d55e..7e25bb38fc 100644 --- a/packages/testing/src/execution_testing/exceptions/exceptions/transaction.py +++ b/packages/testing/src/execution_testing/exceptions/exceptions/transaction.py @@ -153,6 +153,10 @@ class TransactionException(ExceptionBase): """ Transaction's initcode for a contract-creating transaction is too large. """ + TYPE_1_TX_PRE_FORK = auto() + """Transaction type 1 included before activation fork.""" + TYPE_2_TX_PRE_FORK = auto() + """Transaction type 2 included before activation fork.""" TYPE_3_TX_PRE_FORK = auto() """Transaction type 3 included before activation fork.""" TYPE_3_TX_ZERO_BLOBS_PRE_FORK = auto() diff --git a/tests/berlin/eip2930_access_list/test_tx_type.py b/tests/berlin/eip2930_access_list/test_tx_type.py new file mode 100644 index 0000000000..c9e22e55d9 --- /dev/null +++ b/tests/berlin/eip2930_access_list/test_tx_type.py @@ -0,0 +1,73 @@ +"""Test the tx type validation for EIP-2930.""" + +from typing import Generator + +import pytest +from execution_testing import ( + Account, + Alloc, + Fork, + ParameterSet, + StateTestFiller, + Transaction, + TransactionException, +) +from execution_testing import Opcodes as Op +from execution_testing.forks import Byzantium + +from .spec import ref_spec_2930 + +REFERENCE_SPEC_GIT_PATH = ref_spec_2930.git_path +REFERENCE_SPEC_VERSION = ref_spec_2930.version + +TX_TYPE = 1 + + +def tx_validity(fork: Fork) -> Generator[ParameterSet, None, None]: + """ + Return a generator of parameters for the tx validity test. + """ + valid = TX_TYPE in fork.tx_types() + yield pytest.param( + valid, + marks=[pytest.mark.exception_test] if not valid else [], + id="valid" if valid else "invalid", + ) + + +@pytest.mark.ported_from( + [ + "https://github.com/ethereum/legacytests/blob/master/src/LegacyTests/Cancun/GeneralStateTestsFiller/stExample/accessListExampleFiller.yml" + ], + pr=["https://github.com/ethereum/execution-specs/pull/1754"], +) +@pytest.mark.parametrize_by_fork("valid", tx_validity) +def test_eip2930_tx_validity( + state_test: StateTestFiller, + fork: Fork, + pre: Alloc, + valid: bool, +) -> None: + """ + Tests that an EIP-2930 tx is correctly rejected before fork activation. + """ + account = pre.deploy_contract( + code=Op.SSTORE(0, 1), + storage={0: 0xDEADBEEF}, + ) + sender = pre.fund_eoa() + + tx = Transaction( + to=account, + sender=sender, + gas_limit=100_000, + access_list=[], + protected=fork >= Byzantium, + error=TransactionException.TYPE_1_TX_PRE_FORK if not valid else None, + ) + + post = {account: Account(storage={0: 0xDEADBEEF if not valid else 1})} + if not valid: + post[sender] = pre[sender] # type: ignore + + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/london/eip1559_fee_market_change/__init__.py b/tests/london/eip1559_fee_market_change/__init__.py new file mode 100644 index 0000000000..ffcf07efa0 --- /dev/null +++ b/tests/london/eip1559_fee_market_change/__init__.py @@ -0,0 +1,3 @@ +""" +Tests for [EIP-1559: Fee market change for ETH 1.0 chain](https://eips.ethereum.org/EIPS/eip-1559). +""" diff --git a/tests/london/eip1559_fee_market_change/spec.py b/tests/london/eip1559_fee_market_change/spec.py new file mode 100644 index 0000000000..0e9d0a59f8 --- /dev/null +++ b/tests/london/eip1559_fee_market_change/spec.py @@ -0,0 +1,16 @@ +"""Defines EIP-1559 specification constants and functions.""" + +from dataclasses import dataclass + + +@dataclass(frozen=True) +class ReferenceSpec: + """Defines the reference spec version and git path.""" + + git_path: str + version: str + + +ref_spec_1559 = ReferenceSpec( + "EIPS/eip-1559.md", "ba6c342c23164072adb500c3136e3ae6eabff306" +) diff --git a/tests/london/eip1559_fee_market_change/test_tx_type.py b/tests/london/eip1559_fee_market_change/test_tx_type.py new file mode 100644 index 0000000000..d4e2caaa84 --- /dev/null +++ b/tests/london/eip1559_fee_market_change/test_tx_type.py @@ -0,0 +1,73 @@ +"""Test the tx type validation for EIP-1559.""" + +from typing import Generator + +import pytest +from execution_testing import ( + Account, + Alloc, + Fork, + ParameterSet, + StateTestFiller, + Transaction, + TransactionException, +) +from execution_testing import Opcodes as Op +from execution_testing.forks import Byzantium + +from .spec import ref_spec_1559 + +REFERENCE_SPEC_GIT_PATH = ref_spec_1559.git_path +REFERENCE_SPEC_VERSION = ref_spec_1559.version + +TX_TYPE = 2 + + +def tx_validity(fork: Fork) -> Generator[ParameterSet, None, None]: + """ + Return a generator of parameters for the tx validity test. + """ + valid = TX_TYPE in fork.tx_types() + yield pytest.param( + valid, + marks=[pytest.mark.exception_test] if not valid else [], + id="valid" if valid else "invalid", + ) + + +@pytest.mark.ported_from( + [ + "https://github.com/ethereum/legacytests/blob/master/Cancun/GeneralStateTests/stEIP1559/typeTwoBerlin.json" + ], + pr=["https://github.com/ethereum/execution-specs/pull/1754"], +) +@pytest.mark.parametrize_by_fork("valid", tx_validity) +def test_eip1559_tx_validity( + state_test: StateTestFiller, + fork: Fork, + pre: Alloc, + valid: bool, +) -> None: + """ + Tests that an EIP-1559 tx has no effect before London. + """ + account = pre.deploy_contract( + code=Op.SSTORE(0, 1), + storage={0: 0xDEADBEEF}, + ) + sender = pre.fund_eoa() + + tx = Transaction( + to=account, + sender=sender, + gas_limit=100_000, + max_priority_fee_per_gas=1, + protected=fork >= Byzantium, + error=TransactionException.TYPE_2_TX_PRE_FORK if not valid else None, + ) + + post = {account: Account(storage={0: 0xDEADBEEF if not valid else 1})} + if not valid: + post[sender] = pre[sender] # type: ignore + + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/static/state_tests/stEIP1559/typeTwoBerlinFiller.yml b/tests/static/state_tests/stEIP1559/typeTwoBerlinFiller.yml deleted file mode 100644 index 389580ac6c..0000000000 --- a/tests/static/state_tests/stEIP1559/typeTwoBerlinFiller.yml +++ /dev/null @@ -1,60 +0,0 @@ -typeTwoBerlin: - _info: - comment: Ori Pomerantz qbzzt1@gmail.com - - env: - currentCoinbase: 2adc25665018aa1fe0e6bc666dac8fc2697ff9ba - currentDifficulty: 0x20000 - currentGasLimit: 0xFF112233445566 - currentNumber: 1 - currentTimestamp: 1000 - currentBaseFee: 1000 - - - pre: - : - balance: '1000000000000000000' - code: | - :yul berlin - { - sstore(0, add(1,1)) - } - nonce: '0' - storage: {} - - : - balance: '1000000000000000000' - code: '0x' - nonce: 1 - storage: {} - - - transaction: - data: - - data: :label declaredKeyWrite :raw 0x00 - accessList: [] - maxFeePerGas: 1000 - maxPriorityFeePerGas: 1000 - gasLimit: - - '400000' - nonce: 1 - to: - value: - - '100000' - secretKey: "" - - - expect: - - - indexes: - data: !!int -1 - gas: !!int -1 - value: !!int -1 - - network: - - '>=Cancun' - result: - : - storage: - 0: 2 - diff --git a/tests/static/state_tests/stExample/accessListExampleFiller.yml b/tests/static/state_tests/stExample/accessListExampleFiller.yml deleted file mode 100644 index 67b8469168..0000000000 --- a/tests/static/state_tests/stExample/accessListExampleFiller.yml +++ /dev/null @@ -1,66 +0,0 @@ -accessListExample: - _info: - comment: A test shows accessList transaction example - - env: - currentCoinbase: 2adc25665018aa1fe0e6bc666dac8fc2697ff9ba - currentDifficulty: 0x20000 - currentGasLimit: 0xFF112233445566 - currentNumber: 1 - currentTimestamp: 1000 - - expect: - - network: - - '>=Cancun' - result: - : - storage: - 0x00: 2 - - - - pre: - : - balance: '1000000000000000000' - code: | - { - ; Can also add lll style comments here - [[0]] (ADD 1 1) - } - nonce: '0' - storage: {} - - : - balance: '1000000000000000000' - code: '0x' - nonce: '0' - storage: {} - - - transaction: - data: - - data: :label declaredKeyWrite :raw 0x00 - accessList: - - address: - storageKeys: - - 0x00 - - 0x01 - - address: 0x195e7baea6a6c7c4c2dfeb977efac326af552d87 - storageKeys: - - 0x00 - - - data: :label declaredKeyWrite2 :raw 0x00 - accessList: - - address: 0x195e7baea6a6c7c4c2dfeb977efac326af552d87 - storageKeys: - - 0x00 - - gasLimit: - - '400000' - gasPrice: '10' - nonce: '0' - to: - value: - - '100000' - secretKey: "" -