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 7d007cca02..cb6acfb75e 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 @@ -197,6 +197,7 @@ class ExecutionSpecsExceptionMapper(ExceptionMapper): """ mapping_substring: ClassVar[Dict[ExceptionBase, str]] = { + TransactionException.TYPE_NOT_SUPPORTED: "TransactionTypeError", TransactionException.TYPE_4_EMPTY_AUTHORIZATION_LIST: ( "EmptyAuthorizationListError" ), diff --git a/src/ethereum/forks/monad_eight/transactions.py b/src/ethereum/forks/monad_eight/transactions.py index 24bb4880a7..2073ae50a1 100644 --- a/src/ethereum/forks/monad_eight/transactions.py +++ b/src/ethereum/forks/monad_eight/transactions.py @@ -495,15 +495,14 @@ def decode_transaction(tx: LegacyTransaction | Bytes) -> Transaction: Needed because non-legacy transactions aren't RLP. Legacy transactions are returned as-is, while other transaction types - are decoded based on their type identifier prefix. + are decoded based on their type identifier prefix. Blob transactions + are not accepted, so their type byte is unknown here. """ if isinstance(tx, Bytes): if tx[0] == 1: return rlp.decode_to(AccessListTransaction, tx[1:]) elif tx[0] == 2: return rlp.decode_to(FeeMarketTransaction, tx[1:]) - elif tx[0] == 3: - return rlp.decode_to(BlobTransaction, tx[1:]) elif tx[0] == 4: return rlp.decode_to(SetCodeTransaction, tx[1:]) else: @@ -541,6 +540,9 @@ def validate_transaction(tx: Transaction) -> Tuple[Uint, Uint]: [EIP-2681]: https://eips.ethereum.org/EIPS/eip-2681 [EIP-7623]: https://eips.ethereum.org/EIPS/eip-7623 """ + if isinstance(tx, BlobTransaction): + raise TransactionTypeError(3) + from .vm.interpreter import MAX_INIT_CODE_SIZE intrinsic_gas, calldata_floor_gas_cost = calculate_intrinsic_cost(tx) diff --git a/src/ethereum/forks/monad_next/transactions.py b/src/ethereum/forks/monad_next/transactions.py index 24bb4880a7..2073ae50a1 100644 --- a/src/ethereum/forks/monad_next/transactions.py +++ b/src/ethereum/forks/monad_next/transactions.py @@ -495,15 +495,14 @@ def decode_transaction(tx: LegacyTransaction | Bytes) -> Transaction: Needed because non-legacy transactions aren't RLP. Legacy transactions are returned as-is, while other transaction types - are decoded based on their type identifier prefix. + are decoded based on their type identifier prefix. Blob transactions + are not accepted, so their type byte is unknown here. """ if isinstance(tx, Bytes): if tx[0] == 1: return rlp.decode_to(AccessListTransaction, tx[1:]) elif tx[0] == 2: return rlp.decode_to(FeeMarketTransaction, tx[1:]) - elif tx[0] == 3: - return rlp.decode_to(BlobTransaction, tx[1:]) elif tx[0] == 4: return rlp.decode_to(SetCodeTransaction, tx[1:]) else: @@ -541,6 +540,9 @@ def validate_transaction(tx: Transaction) -> Tuple[Uint, Uint]: [EIP-2681]: https://eips.ethereum.org/EIPS/eip-2681 [EIP-7623]: https://eips.ethereum.org/EIPS/eip-7623 """ + if isinstance(tx, BlobTransaction): + raise TransactionTypeError(3) + from .vm.interpreter import MAX_INIT_CODE_SIZE intrinsic_gas, calldata_floor_gas_cost = calculate_intrinsic_cost(tx) diff --git a/src/ethereum/forks/monad_nine/transactions.py b/src/ethereum/forks/monad_nine/transactions.py index 24bb4880a7..2073ae50a1 100644 --- a/src/ethereum/forks/monad_nine/transactions.py +++ b/src/ethereum/forks/monad_nine/transactions.py @@ -495,15 +495,14 @@ def decode_transaction(tx: LegacyTransaction | Bytes) -> Transaction: Needed because non-legacy transactions aren't RLP. Legacy transactions are returned as-is, while other transaction types - are decoded based on their type identifier prefix. + are decoded based on their type identifier prefix. Blob transactions + are not accepted, so their type byte is unknown here. """ if isinstance(tx, Bytes): if tx[0] == 1: return rlp.decode_to(AccessListTransaction, tx[1:]) elif tx[0] == 2: return rlp.decode_to(FeeMarketTransaction, tx[1:]) - elif tx[0] == 3: - return rlp.decode_to(BlobTransaction, tx[1:]) elif tx[0] == 4: return rlp.decode_to(SetCodeTransaction, tx[1:]) else: @@ -541,6 +540,9 @@ def validate_transaction(tx: Transaction) -> Tuple[Uint, Uint]: [EIP-2681]: https://eips.ethereum.org/EIPS/eip-2681 [EIP-7623]: https://eips.ethereum.org/EIPS/eip-7623 """ + if isinstance(tx, BlobTransaction): + raise TransactionTypeError(3) + from .vm.interpreter import MAX_INIT_CODE_SIZE intrinsic_gas, calldata_floor_gas_cost = calculate_intrinsic_cost(tx) diff --git a/src/ethereum/forks/monad_ten/transactions.py b/src/ethereum/forks/monad_ten/transactions.py index 24bb4880a7..2073ae50a1 100644 --- a/src/ethereum/forks/monad_ten/transactions.py +++ b/src/ethereum/forks/monad_ten/transactions.py @@ -495,15 +495,14 @@ def decode_transaction(tx: LegacyTransaction | Bytes) -> Transaction: Needed because non-legacy transactions aren't RLP. Legacy transactions are returned as-is, while other transaction types - are decoded based on their type identifier prefix. + are decoded based on their type identifier prefix. Blob transactions + are not accepted, so their type byte is unknown here. """ if isinstance(tx, Bytes): if tx[0] == 1: return rlp.decode_to(AccessListTransaction, tx[1:]) elif tx[0] == 2: return rlp.decode_to(FeeMarketTransaction, tx[1:]) - elif tx[0] == 3: - return rlp.decode_to(BlobTransaction, tx[1:]) elif tx[0] == 4: return rlp.decode_to(SetCodeTransaction, tx[1:]) else: @@ -541,6 +540,9 @@ def validate_transaction(tx: Transaction) -> Tuple[Uint, Uint]: [EIP-2681]: https://eips.ethereum.org/EIPS/eip-2681 [EIP-7623]: https://eips.ethereum.org/EIPS/eip-7623 """ + if isinstance(tx, BlobTransaction): + raise TransactionTypeError(3) + from .vm.interpreter import MAX_INIT_CODE_SIZE intrinsic_gas, calldata_floor_gas_cost = calculate_intrinsic_cost(tx) diff --git a/tests/amsterdam/eip7981_increase_access_list_cost/test_transaction_validity.py b/tests/amsterdam/eip7981_increase_access_list_cost/test_transaction_validity.py index 440e844c86..fffbc62d27 100644 --- a/tests/amsterdam/eip7981_increase_access_list_cost/test_transaction_validity.py +++ b/tests/amsterdam/eip7981_increase_access_list_cost/test_transaction_validity.py @@ -258,6 +258,9 @@ def test_mixed_zero_nonzero_bytes_floor_cost( 3, [], id="type_3_empty_access_list", + marks=pytest.mark.not_valid_for( + "MONAD_EIGHT", subsequent_forks=True + ), ), pytest.param( 4, diff --git a/tests/istanbul/eip1344_chainid/test_chainid.py b/tests/istanbul/eip1344_chainid/test_chainid.py index 8379b55cc9..78d48d2153 100644 --- a/tests/istanbul/eip1344_chainid/test_chainid.py +++ b/tests/istanbul/eip1344_chainid/test_chainid.py @@ -17,8 +17,8 @@ @pytest.mark.with_all_typed_transactions( - marks=lambda tx_type: pytest.mark.skip( - reason="type 3 transactions aren't supported in Monad" + marks=lambda tx_type: pytest.mark.not_valid_for( + "MONAD_EIGHT", subsequent_forks=True ) if tx_type == 3 else None diff --git a/tests/monad_eight/typed_transactions/__init__.py b/tests/monad_eight/typed_transactions/__init__.py new file mode 100644 index 0000000000..d21a75bb78 --- /dev/null +++ b/tests/monad_eight/typed_transactions/__init__.py @@ -0,0 +1 @@ +"""Tests for the transaction types Monad forks accept.""" diff --git a/tests/monad_eight/typed_transactions/test_blob_transaction.py b/tests/monad_eight/typed_transactions/test_blob_transaction.py new file mode 100644 index 0000000000..93b5b4ebb5 --- /dev/null +++ b/tests/monad_eight/typed_transactions/test_blob_transaction.py @@ -0,0 +1,65 @@ +""" +Tests that Monad forks reject blob transactions. + +Monad advertises no blob schedule and carries the blob header fields +fixed at zero, so the type byte EIP-4844 assigns is unknown to the fork +and a transaction carrying it is invalid rather than merely unused. +""" + +import pytest +from execution_testing import ( + Account, + Alloc, + Block, + BlockchainTestFiller, + Hash, + Transaction, + TransactionException, + add_kzg_version, +) +from execution_testing.forks.helpers import Fork + +BLOB_COMMITMENT_VERSION_KZG = 1 + +pytestmark = [ + pytest.mark.valid_from("MONAD_EIGHT"), + pytest.mark.exception_test, +] + + +@pytest.mark.parametrize( + "gas_limit", + [ + pytest.param(100_000, id="sufficient_gas"), + # Below the intrinsic cost, so the transaction would fail generic + # validation too; the unknown type is reported ahead of it. + pytest.param(1_000, id="below_intrinsic_gas"), + ], +) +def test_blob_transaction_is_rejected( + blockchain_test: BlockchainTestFiller, + pre: Alloc, + fork: Fork, + gas_limit: int, +) -> None: + """A blob transaction is not a transaction type the fork knows.""" + assert 3 not in fork.tx_types() + + sender = pre.fund_eoa() + tx = Transaction( + ty=3, + to=pre.fund_eoa(amount=0), + gas_limit=gas_limit, + max_fee_per_blob_gas=1, + blob_versioned_hashes=add_kzg_version( + [Hash(1)], BLOB_COMMITMENT_VERSION_KZG + ), + sender=sender, + error=TransactionException.TYPE_NOT_SUPPORTED, + ) + + blockchain_test( + pre=pre, + post={sender: Account(nonce=0)}, + blocks=[Block(txs=[tx], exception=tx.error)], + ) diff --git a/tests/prague/eip7623_increase_calldata_cost/test_execution_gas.py b/tests/prague/eip7623_increase_calldata_cost/test_execution_gas.py index e0722edae4..4a6f5de540 100644 --- a/tests/prague/eip7623_increase_calldata_cost/test_execution_gas.py +++ b/tests/prague/eip7623_increase_calldata_cost/test_execution_gas.py @@ -69,7 +69,9 @@ def to( True, None, id="type_3", - marks=pytest.mark.skip(reason="Not supported in Monad"), + marks=pytest.mark.not_valid_for( + "MONAD_EIGHT", subsequent_forks=True + ), ), pytest.param(4, True, [Address(1)], id="type_4"), ], @@ -153,7 +155,9 @@ def to( True, None, id="type_3", - marks=pytest.mark.skip(reason="Not supported in Monad"), + marks=pytest.mark.not_valid_for( + "MONAD_EIGHT", subsequent_forks=True + ), ), pytest.param(4, True, [Address(1)], id="type_4"), ], diff --git a/tests/prague/eip7623_increase_calldata_cost/test_transaction_validity.py b/tests/prague/eip7623_increase_calldata_cost/test_transaction_validity.py index 792c8e8fc9..d20f3bb8a0 100644 --- a/tests/prague/eip7623_increase_calldata_cost/test_transaction_validity.py +++ b/tests/prague/eip7623_increase_calldata_cost/test_transaction_validity.py @@ -179,7 +179,7 @@ def test_transaction_validity_type_1_type_2( ) -@pytest.mark.skip(reason="Not supported in Monad") +@pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) @pytest.mark.parametrize( "access_list", [