diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 2b88c0b350..37b2cd513c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -57,6 +57,7 @@ Test fixtures for use by clients are available for each release on the [Github r - 🔀 Update `pydantic` from 2.8.2 to 2.9.2 ([#960](https://github.com/ethereum/execution-spec-tests/pull/960)). - ✨ Add the `eest make test` command, an interactive CLI that helps users create a new test module and function ([#950](https://github.com/ethereum/execution-spec-tests/pull/950)). - ✨ Add the `eest clean` command that helps delete generated files and directories from the repository ([#980](https://github.com/ethereum/execution-spec-tests/pull/980)). +- ✨ Add framework changes for EIP-7742, required for Prague devnet-5 ([#931](https://github.com/ethereum/execution-spec-tests/pull/931)). ### 🔧 EVM Tools diff --git a/src/ethereum_test_fixtures/blockchain.py b/src/ethereum_test_fixtures/blockchain.py index 27de996526..d792748df9 100644 --- a/src/ethereum_test_fixtures/blockchain.py +++ b/src/ethereum_test_fixtures/blockchain.py @@ -3,7 +3,18 @@ """ from functools import cached_property -from typing import Annotated, Any, ClassVar, List, Literal, Tuple, Union, get_args, get_type_hints +from typing import ( + Annotated, + Any, + ClassVar, + List, + Literal, + Tuple, + Union, + cast, + get_args, + get_type_hints, +) from ethereum import rlp as eth_rlp from ethereum_types.numeric import Uint @@ -97,9 +108,13 @@ class FixtureHeader(CamelModel): extra_data: Bytes prev_randao: Hash = Field(Hash(0), alias="mixHash") nonce: HeaderNonce = Field(HeaderNonce(0), validate_default=True) - base_fee_per_gas: Annotated[ - ZeroPaddedHexNumber, HeaderForkRequirement("base_fee") - ] | None = Field(None) + base_fee_per_gas: ( + Annotated[ + ZeroPaddedHexNumber, + HeaderForkRequirement("base_fee"), + ] + | None + ) = Field(None) withdrawals_root: Annotated[Hash, HeaderForkRequirement("withdrawals")] | None = Field(None) blob_gas_used: ( Annotated[ZeroPaddedHexNumber, HeaderForkRequirement("blob_gas_used")] | None @@ -111,6 +126,9 @@ class FixtureHeader(CamelModel): None ) requests_hash: Annotated[Hash, HeaderForkRequirement("requests")] | None = Field(None) + target_blobs_per_block: ( + Annotated[ZeroPaddedHexNumber, HeaderForkRequirement("target_blobs_per_block")] | None + ) = Field(None) fork: Fork | None = Field(None, exclude=True) @@ -224,7 +242,13 @@ def from_fixture_header( EngineNewPayloadV1Parameters = Tuple[FixtureExecutionPayload] EngineNewPayloadV3Parameters = Tuple[FixtureExecutionPayload, List[Hash], Hash] -EngineNewPayloadV4Parameters = Tuple[FixtureExecutionPayload, List[Hash], Hash, List[Bytes]] +EngineNewPayloadV4Parameters = Tuple[ + FixtureExecutionPayload, + List[Hash], + Hash, + List[Bytes], + HexNumber, +] # Important: We check EngineNewPayloadV3Parameters first as it has more fields, and pydantic # has a weird behavior when the smaller tuple is checked first. @@ -286,32 +310,37 @@ def from_fixture_header( transactions=transactions, withdrawals=withdrawals, ) - params: EngineNewPayloadParameters - if ( - fork.engine_new_payload_requests(header.number, header.timestamp) - and requests is not None - ): - parent_beacon_block_root = header.parent_beacon_block_root - assert parent_beacon_block_root is not None - params = ( - execution_payload, - Transaction.list_blob_versioned_hashes(transactions), - parent_beacon_block_root, - requests, - ) - elif fork.engine_new_payload_blob_hashes(header.number, header.timestamp): - parent_beacon_block_root = header.parent_beacon_block_root - assert parent_beacon_block_root is not None - params = ( - execution_payload, - Transaction.list_blob_versioned_hashes(transactions), - parent_beacon_block_root, - ) - else: - params = (execution_payload,) + params: List[Any] = [execution_payload] + if fork.engine_new_payload_blob_hashes(header.number, header.timestamp): + blob_hashes = Transaction.list_blob_versioned_hashes(transactions) + if blob_hashes is None: + raise ValueError(f"Blob hashes are required for ${fork}.") + params.append(blob_hashes) + + if fork.engine_new_payload_beacon_root(header.number, header.timestamp): + parent_beacon_block_root = header.parent_beacon_block_root + if parent_beacon_block_root is None: + raise ValueError(f"Parent beacon block root is required for ${fork}.") + params.append(parent_beacon_block_root) + + if fork.engine_new_payload_requests(header.number, header.timestamp): + if requests is None: + raise ValueError(f"Requests are required for ${fork}.") + params.append(requests) + + if fork.engine_new_payload_target_blobs_per_block(header.number, header.timestamp): + target_blobs_per_block = header.target_blobs_per_block + if target_blobs_per_block is None: + raise ValueError(f"Target blobs per block is required for ${fork}.") + params.append(target_blobs_per_block) + + payload_params: EngineNewPayloadParameters = cast( + EngineNewPayloadParameters, + tuple(params), + ) new_payload = cls( - params=params, + params=payload_params, new_payload_version=new_payload_version, forkchoice_updated_version=forkchoice_updated_version, **kwargs, diff --git a/src/ethereum_test_fixtures/tests/test_blockchain.py b/src/ethereum_test_fixtures/tests/test_blockchain.py index b121a1bfed..07399604e9 100644 --- a/src/ethereum_test_fixtures/tests/test_blockchain.py +++ b/src/ethereum_test_fixtures/tests/test_blockchain.py @@ -16,6 +16,7 @@ Bytes, Hash, HeaderNonce, + HexNumber, TestPrivateKey, ZeroPaddedHexNumber, to_json, @@ -668,6 +669,7 @@ excess_blob_gas=18, parent_beacon_block_root=19, requests_hash=20, + target_blobs_per_block=21, ), transactions=[ Transaction( @@ -729,7 +731,7 @@ "blobGasUsed": hex(17), "excessBlobGas": hex(18), "blockHash": ( - "0x93bd662d8a80a1f54bffc6d140b83d6cda233209998809f9540be51178b4d0b6" + "0x9f6459fb2eca2b75ee861e97d679ba91457bb446c8484a7ad76d1675a7f78fde" ), "transactions": [ Transaction( @@ -787,8 +789,9 @@ ), ).requests_list ], + HexNumber(21).hex(), ], - "forkchoiceUpdatedVersion": "3", + "forkchoiceUpdatedVersion": "4", "newPayloadVersion": "4", "validationError": "BlockException.INCORRECT_BLOCK_FORMAT" "|TransactionException.INTRINSIC_GAS_TOO_LOW", @@ -823,6 +826,7 @@ excess_blob_gas=18, parent_beacon_block_root=19, requests_hash=20, + target_blobs_per_block=21, ), transactions=[ Transaction( @@ -883,7 +887,7 @@ "blobGasUsed": hex(17), "excessBlobGas": hex(18), "blockHash": ( - "0x93bd662d8a80a1f54bffc6d140b83d6cda233209998809f9540be51178b4d0b6" + "0x9f6459fb2eca2b75ee861e97d679ba91457bb446c8484a7ad76d1675a7f78fde" ), "transactions": [ Transaction( @@ -941,9 +945,10 @@ ), ).requests_list ], + HexNumber(21).hex(), ], "newPayloadVersion": "4", - "forkchoiceUpdatedVersion": "3", + "forkchoiceUpdatedVersion": "4", "validationError": "BlockException.INCORRECT_BLOCK_FORMAT" "|TransactionException.INTRINSIC_GAS_TOO_LOW", }, @@ -1163,7 +1168,7 @@ def test_json_deserialization( id="fixture_engine_new_payload_parameters_v3", ), pytest.param( - False, + True, EngineNewPayloadParametersAdapter, ( FixtureExecutionPayload.from_fixture_header( @@ -1227,6 +1232,7 @@ def test_json_deserialization( target_pubkey=BLSPublicKey(2), ), ).requests_list, + HexNumber(9), ), [ { @@ -1292,6 +1298,7 @@ def test_json_deserialization( ), ).requests_list ], + HexNumber(9).hex(), ], id="fixture_engine_new_payload_parameters_v4", ), diff --git a/src/ethereum_test_forks/base_fork.py b/src/ethereum_test_forks/base_fork.py index 19716201b5..4ba6808479 100644 --- a/src/ethereum_test_forks/base_fork.py +++ b/src/ethereum_test_forks/base_fork.py @@ -246,6 +246,14 @@ def transaction_intrinsic_cost_calculator( """ pass + @classmethod + @abstractmethod + def header_target_blobs_per_block_required(cls, block_number: int, timestamp: int) -> bool: + """ + Returns true if the header must contain target blobs per block. + """ + pass + @classmethod @abstractmethod def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int: @@ -254,6 +262,22 @@ def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int: """ pass + @classmethod + @abstractmethod + def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int: + """ + Returns the target blobs per block for a given fork. + """ + pass + + @classmethod + @abstractmethod + def max_blobs_per_block(cls, block_number: int, timestamp: int) -> int: + """ + Returns the max blobs per block for a given fork. + """ + pass + @classmethod @abstractmethod def get_reward(cls, block_number: int = 0, timestamp: int = 0) -> int: @@ -357,6 +381,37 @@ def engine_new_payload_requests(cls, block_number: int = 0, timestamp: int = 0) """ pass + @classmethod + @abstractmethod + def engine_new_payload_target_blobs_per_block( + cls, block_number: int = 0, timestamp: int = 0 + ) -> bool: + """ + Returns true if the engine api version requires new payload calls to include + target blobs per block. + """ + pass + + @classmethod + @abstractmethod + def engine_payload_attribute_target_blobs_per_block( + cls, block_number: int = 0, timestamp: int = 0 + ) -> bool: + """ + Returns true if the payload attributes include the target blobs per block. + """ + pass + + @classmethod + @abstractmethod + def engine_payload_attribute_max_blobs_per_block( + cls, block_number: int = 0, timestamp: int = 0 + ) -> bool: + """ + Returns true if the payload attributes include the max blobs per block. + """ + pass + @classmethod @abstractmethod def engine_forkchoice_updated_version( diff --git a/src/ethereum_test_forks/forks/forks.py b/src/ethereum_test_forks/forks/forks.py index ac3da65438..1ea4fa50cb 100644 --- a/src/ethereum_test_forks/forks/forks.py +++ b/src/ethereum_test_forks/forks/forks.py @@ -232,6 +232,20 @@ def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int: """ return 0 + @classmethod + def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int: + """ + Returns the target number of blobs per block for a given fork. + """ + return 0 + + @classmethod + def max_blobs_per_block(cls, block_number: int, timestamp: int) -> int: + """ + Returns the max number of blobs per block for a given fork. + """ + return 0 + @classmethod def header_requests_required(cls, block_number: int, timestamp: int) -> bool: """ @@ -255,6 +269,17 @@ def header_beacon_root_required(cls, block_number: int = 0, timestamp: int = 0) """ return False + @classmethod + def header_target_blobs_per_block_required( + cls, + block_number: int = 0, + timestamp: int = 0, + ) -> bool: + """ + At genesis, header must not contain target blobs per block. + """ + return False + @classmethod def engine_new_payload_blob_hashes(cls, block_number: int = 0, timestamp: int = 0) -> bool: """ @@ -276,6 +301,35 @@ def engine_new_payload_requests(cls, block_number: int = 0, timestamp: int = 0) """ return False + @classmethod + def engine_new_payload_target_blobs_per_block( + cls, + block_number: int = 0, + timestamp: int = 0, + ) -> bool: + """ + At genesis, payloads do not have target blobs per block. + """ + return False + + @classmethod + def engine_payload_attribute_target_blobs_per_block( + cls, block_number: int = 0, timestamp: int = 0 + ) -> bool: + """ + At genesis, payload attributes do not include the target blobs per block. + """ + return False + + @classmethod + def engine_payload_attribute_max_blobs_per_block( + cls, block_number: int = 0, timestamp: int = 0 + ) -> bool: + """ + At genesis, payload attributes do not include the max blobs per block. + """ + return False + @classmethod def engine_forkchoice_updated_version( cls, block_number: int = 0, timestamp: int = 0 @@ -546,9 +600,9 @@ def call_opcodes( """ At Homestead, DELEGATECALL opcode was introduced. """ - return [(Opcodes.DELEGATECALL, EVMCodeType.LEGACY),] + super( - Homestead, cls - ).call_opcodes(block_number, timestamp) + return [(Opcodes.DELEGATECALL, EVMCodeType.LEGACY)] + super(Homestead, cls).call_opcodes( + block_number, timestamp + ) @classmethod def valid_opcodes( @@ -622,9 +676,9 @@ def call_opcodes( """ At Byzantium, STATICCALL opcode was introduced. """ - return [(Opcodes.STATICCALL, EVMCodeType.LEGACY),] + super( - Byzantium, cls - ).call_opcodes(block_number, timestamp) + return [(Opcodes.STATICCALL, EVMCodeType.LEGACY)] + super(Byzantium, cls).call_opcodes( + block_number, timestamp + ) @classmethod def valid_opcodes( @@ -656,9 +710,9 @@ def create_opcodes( """ At Constantinople, `CREATE2` opcode is added. """ - return [(Opcodes.CREATE2, EVMCodeType.LEGACY),] + super( - Constantinople, cls - ).create_opcodes(block_number, timestamp) + return [(Opcodes.CREATE2, EVMCodeType.LEGACY)] + super(Constantinople, cls).create_opcodes( + block_number, timestamp + ) @classmethod def valid_opcodes( @@ -938,10 +992,24 @@ def header_beacon_root_required(cls, block_number: int = 0, timestamp: int = 0) @classmethod def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int: """ - Blobs are enabled started from Cancun. + Blobs are enabled starting from Cancun. """ return 2**17 + @classmethod + def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int: + """ + Blobs are enabled starting from Cancun, with a static target of 3 blobs. + """ + return 3 + + @classmethod + def max_blobs_per_block(cls, block_number: int, timestamp: int) -> int: + """ + Blobs are enabled starting from Cancun, with a static max of 6 blobs. + """ + return 6 + @classmethod def tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]: """ @@ -1176,6 +1244,18 @@ def header_requests_required(cls, block_number: int, timestamp: int) -> bool: """ return True + @classmethod + def header_target_blobs_per_block_required( + cls, + block_number: int = 0, + timestamp: int = 0, + ) -> bool: + """ + Prague requires that the execution layer header contains the beacon + chain target blobs per block. + """ + return True + @classmethod def engine_new_payload_requests(cls, block_number: int = 0, timestamp: int = 0) -> bool: """ @@ -1184,9 +1264,11 @@ def engine_new_payload_requests(cls, block_number: int = 0, timestamp: int = 0) return True @classmethod - def engine_new_payload_blob_hashes(cls, block_number: int = 0, timestamp: int = 0) -> bool: + def engine_new_payload_target_blobs_per_block( + cls, block_number: int = 0, timestamp: int = 0 + ) -> bool: """ - Starting at Prague, new payload directives must contain requests as parameter. + Starting at Prague, new payloads include the target blobs per block as a parameter. """ return True @@ -1200,13 +1282,22 @@ def engine_new_payload_version( return 4 @classmethod - def engine_forkchoice_updated_version( + def engine_payload_attribute_target_blobs_per_block( cls, block_number: int = 0, timestamp: int = 0 - ) -> Optional[int]: + ) -> bool: """ - At Prague, version number of NewPayload and ForkchoiceUpdated diverge. + Starting at Prague, payload attributes include the target blobs per block. """ - return 3 + return True + + @classmethod + def engine_payload_attribute_max_blobs_per_block( + cls, block_number: int = 0, timestamp: int = 0 + ) -> bool: + """ + Starting at Prague, payload attributes include the max blobs per block. + """ + return True class CancunEIP7692( # noqa: SC200 @@ -1224,7 +1315,7 @@ def evm_code_types(cls, block_number: int = 0, timestamp: int = 0) -> List[EVMCo """ EOF V1 is supported starting from this fork. """ - return super(CancunEIP7692, cls,).evm_code_types( # noqa: SC200 + return super(CancunEIP7692, cls).evm_code_types( # noqa: SC200 block_number, timestamp, ) + [EVMCodeType.EOF_V1] @@ -1253,7 +1344,7 @@ def create_opcodes( """ EOF V1 introduces `EOFCREATE`. """ - return [(Opcodes.EOFCREATE, EVMCodeType.EOF_V1),] + super( + return [(Opcodes.EOFCREATE, EVMCodeType.EOF_V1)] + super( CancunEIP7692, cls # noqa: SC200 ).create_opcodes(block_number, timestamp) @@ -1283,7 +1374,7 @@ def evm_code_types(cls, block_number: int = 0, timestamp: int = 0) -> List[EVMCo """ EOF V1 is supported starting from Osaka. """ - return super(Osaka, cls,).evm_code_types( + return super(Osaka, cls).evm_code_types( block_number, timestamp, ) + [EVMCodeType.EOF_V1] diff --git a/src/ethereum_test_rpc/types.py b/src/ethereum_test_rpc/types.py index 0cb0828d54..6541d55075 100644 --- a/src/ethereum_test_rpc/types.py +++ b/src/ethereum_test_rpc/types.py @@ -111,6 +111,8 @@ class PayloadAttributes(CamelModel): suggested_fee_recipient: Address withdrawals: List[Withdrawal] | None = None parent_beacon_block_root: Hash | None = None + target_blobs_per_block: HexNumber | None = None + max_blobs_per_block: HexNumber | None = None class BlobsBundle(CamelModel): diff --git a/src/ethereum_test_specs/blockchain.py b/src/ethereum_test_specs/blockchain.py index ca81a2ad78..089bdee533 100644 --- a/src/ethereum_test_specs/blockchain.py +++ b/src/ethereum_test_specs/blockchain.py @@ -119,6 +119,7 @@ class Header(CamelModel): excess_blob_gas: Removable | HexNumber | None = None parent_beacon_block_root: Removable | Hash | None = None requests_hash: Removable | Hash | None = None + target_blobs_per_block: Removable | HexNumber | None = None REMOVE_FIELD: ClassVar[Removable] = Removable() """ @@ -269,6 +270,8 @@ def set_environment(self, env: Environment) -> Environment: new_env_values["blob_gas_used"] = self.blob_gas_used if not isinstance(self.parent_beacon_block_root, Removable): new_env_values["parent_beacon_block_root"] = self.parent_beacon_block_root + if not isinstance(self.target_blobs_per_block, Removable): + new_env_values["target_blobs_per_block"] = self.target_blobs_per_block """ These values are required, but they depend on the previous environment, so they can be calculated here. @@ -357,6 +360,11 @@ def make_genesis( ), parent_beacon_block_root=env.parent_beacon_block_root, requests_hash=Requests() if fork.header_requests_required(0, 0) else None, + target_blobs_per_block=( + fork.target_blobs_per_block(0, 0) + if fork.header_target_blobs_per_block_required(0, 0) + else None + ), fork=fork, ) @@ -549,9 +557,11 @@ def make_fixture( header=header, txs=[FixtureTransaction.from_transaction(tx) for tx in txs], ommers=[], - withdrawals=[FixtureWithdrawal.from_withdrawal(w) for w in new_env.withdrawals] - if new_env.withdrawals is not None - else None, + withdrawals=( + [FixtureWithdrawal.from_withdrawal(w) for w in new_env.withdrawals] + if new_env.withdrawals is not None + else None + ), fork=fork, ).with_rlp(txs=txs) if block.exception is None: diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index 95c9387fa2..cb3bcfba7d 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -390,6 +390,10 @@ class EnvironmentGeneric(CamelModel, Generic[NumberBoundTypeVar]): difficulty: NumberBoundTypeVar | None = Field(None, alias="currentDifficulty") base_fee_per_gas: NumberBoundTypeVar | None = Field(None, alias="currentBaseFee") excess_blob_gas: NumberBoundTypeVar | None = Field(None, alias="currentExcessBlobGas") + target_blobs_per_block: NumberBoundTypeVar | None = Field( + None, + alias="currentTargetBlobsPerBlock", + ) parent_difficulty: NumberBoundTypeVar | None = Field(None) parent_timestamp: NumberBoundTypeVar | None = Field(None) @@ -475,6 +479,14 @@ def set_fork_requirements(self, fork: Fork) -> "Environment": ): updated_values["parent_beacon_block_root"] = 0 + if ( + fork.header_target_blobs_per_block_required(number, timestamp) + and self.target_blobs_per_block is None + ): + updated_values["target_blobs_per_block"] = fork.target_blobs_per_block( + number, timestamp + ) + return self.copy(**updated_values) diff --git a/src/pytest_plugins/execute/rpc/hive.py b/src/pytest_plugins/execute/rpc/hive.py index 2911ab1938..50a3e40771 100644 --- a/src/pytest_plugins/execute/rpc/hive.py +++ b/src/pytest_plugins/execute/rpc/hive.py @@ -250,13 +250,20 @@ def base_pre_genesis( base_fee_per_gas=env.base_fee_per_gas, blob_gas_used=env.blob_gas_used, excess_blob_gas=env.excess_blob_gas, - withdrawals_root=Withdrawal.list_root(env.withdrawals) - if env.withdrawals is not None - else None, + withdrawals_root=( + Withdrawal.list_root(env.withdrawals) if env.withdrawals is not None else None + ), parent_beacon_block_root=env.parent_beacon_block_root, requests_hash=Requests() if base_fork.header_requests_required(block_number=block_number, timestamp=timestamp) else None, + target_blobs_per_block=( + base_fork.target_blobs_per_block(block_number=block_number, timestamp=timestamp) + if base_fork.header_target_blobs_per_block_required( + block_number=block_number, timestamp=timestamp + ) + else None + ), ) return (pre_alloc, genesis) @@ -651,6 +658,16 @@ def generate_block(self: "EthRPC"): suggested_fee_recipient=Address(0), withdrawals=[] if self.fork.header_withdrawals_required() else None, parent_beacon_block_root=parent_beacon_block_root, + target_blobs_per_block=( + self.fork.target_blobs_per_block(0, 0) + if self.fork.engine_payload_attribute_target_blobs_per_block(0, 0) + else None + ), + max_blobs_per_block=( + self.fork.max_blobs_per_block(0, 0) + if self.fork.engine_payload_attribute_max_blobs_per_block(0, 0) + else None + ), ) forkchoice_updated_version = self.fork.engine_forkchoice_updated_version() assert ( diff --git a/tests/cancun/eip4844_blobs/test_blob_txs.py b/tests/cancun/eip4844_blobs/test_blob_txs.py index 1e1047c6e8..6a8494bcbc 100644 --- a/tests/cancun/eip4844_blobs/test_blob_txs.py +++ b/tests/cancun/eip4844_blobs/test_blob_txs.py @@ -20,7 +20,7 @@ import pytest -from ethereum_test_forks import Fork +from ethereum_test_forks import Fork, Prague from ethereum_test_tools import ( EOA, AccessList, @@ -290,14 +290,24 @@ def tx_access_list() -> List[AccessList]: @pytest.fixture -def tx_error() -> Optional[TransactionException]: +def tx_error( + request: pytest.FixtureRequest, + fork: Fork, +) -> Optional[TransactionException]: """ Default expected error produced by the block transactions (no error). Can be overloaded on test cases where the transactions are expected to fail. """ - return None + if not hasattr(request, "param"): + return None + if fork >= Prague and request.param in [ + TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED, + TransactionException.TYPE_3_TX_MAX_BLOB_GAS_ALLOWANCE_EXCEEDED, + ]: + return None + return request.param @pytest.fixture @@ -736,7 +746,10 @@ def test_invalid_normal_gas( invalid_blob_combinations(), ) @pytest.mark.parametrize( - "tx_error", [TransactionException.TYPE_3_TX_MAX_BLOB_GAS_ALLOWANCE_EXCEEDED], ids=[""] + "tx_error", + [TransactionException.TYPE_3_TX_MAX_BLOB_GAS_ALLOWANCE_EXCEEDED], + ids=[""], + indirect=True, ) @pytest.mark.valid_from("Cancun") def test_invalid_block_blob_count( @@ -1007,6 +1020,7 @@ def test_insufficient_balance_blob_tx_combinations( ), ], ids=["too_few_blobs", "too_many_blobs"], + indirect=["tx_error"], ) @pytest.mark.valid_from("Cancun") def test_invalid_tx_blob_count(