diff --git a/src/ethereum/forks/amsterdam/fork.py b/src/ethereum/forks/amsterdam/fork.py index 751a46ffa0d..56bf9ab036a 100644 --- a/src/ethereum/forks/amsterdam/fork.py +++ b/src/ethereum/forks/amsterdam/fork.py @@ -85,7 +85,6 @@ TX_MAX_GAS_LIMIT, BlobTransaction, FeeMarketTransaction, - IntrinsicGasCost, LegacyTransaction, SetCodeTransaction, Transaction, @@ -492,7 +491,6 @@ def check_transaction( block_output: vm.BlockOutput, tx: Transaction, tx_state: TransactionState, - intrinsic: IntrinsicGasCost, ) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]: """ Check if the transaction is includable in the block. @@ -507,9 +505,6 @@ def check_transaction( The transaction. tx_state : The transaction state tracker. - intrinsic : - The transaction's intrinsic gas cost, split into regular and - state components. Returns ------- @@ -557,10 +552,6 @@ def check_transaction( is empty. """ - # Per-tx 2D gas inclusion check: for each dimension the worst-case - # contribution must fit in the remaining budget. Block-end - # validation still enforces - # max(block_regular_gas_used, block_state_gas_used) <= gas_limit. regular_gas_available = ( block_env.block_gas_limit - block_output.block_gas_used ) @@ -569,16 +560,11 @@ def check_transaction( ) blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used - # Worst-case regular contribution: tx.gas minus the portion that - # must go to intrinsic state gas, capped at TX_MAX_GAS_LIMIT. - if min(TX_MAX_GAS_LIMIT, tx.gas - intrinsic.state) > ( - regular_gas_available - ): + # EIP-8037 per-dimension inclusion check. + if min(TX_MAX_GAS_LIMIT, tx.gas) > regular_gas_available: raise GasUsedExceedsLimitError("gas used exceeds limit") - # Worst-case state contribution: tx.gas minus the portion that - # must go to intrinsic regular gas. - if tx.gas - intrinsic.regular > state_gas_available: + if tx.gas > state_gas_available: raise GasUsedExceedsLimitError("gas used exceeds limit") tx_blob_gas_used = calculate_total_blob_gas(tx) @@ -1020,7 +1006,6 @@ def process_transaction( block_output=block_output, tx=tx, tx_state=tx_state, - intrinsic=intrinsic, ) sender_account = get_account(tx_state, sender) diff --git a/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_block_2d_gas_accounting.py b/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_block_2d_gas_accounting.py index da1e886537a..4cf7d13ed2c 100644 --- a/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_block_2d_gas_accounting.py +++ b/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_block_2d_gas_accounting.py @@ -11,21 +11,27 @@ import pytest from execution_testing import ( + AccessList, Account, + Address, Alloc, + AuthorizationTuple, Block, BlockchainTestFiller, Bytecode, Environment, Fork, + Hash, Header, Op, Storage, Transaction, TransactionException, TransactionReceipt, + add_kzg_version, ) +from ...cancun.eip4844_blobs.spec import Spec as EIP4844_Spec from .spec import ref_spec_8037 REFERENCE_SPEC_GIT_PATH = ref_spec_8037.git_path @@ -483,6 +489,121 @@ def test_multi_block_dimension_flip( ) +@pytest.mark.parametrize( + "tx_gas_delta, expected_exception", + [ + pytest.param(0, None, id="gas_equal"), + pytest.param( + 1, + TransactionException.GAS_ALLOWANCE_EXCEEDED, + id="gas_one_above", + marks=pytest.mark.exception_test, + ), + pytest.param( + 2, + TransactionException.GAS_ALLOWANCE_EXCEEDED, + id="gas_two_above", + marks=pytest.mark.exception_test, + ), + ], +) +@pytest.mark.parametrize( + "block_gas_limit", + [ + pytest.param(0x0FFFFFD, id="bgl_0x0fffffd"), + pytest.param(0x01FFFFE, id="bgl_0x01ffffe"), + ], +) +@pytest.mark.parametrize( + "tx_type, contract_creation", + [ + pytest.param(0, False, id="type_0_call"), + pytest.param(0, True, id="type_0_create"), + pytest.param(1, False, id="type_1_call"), + pytest.param(1, True, id="type_1_create"), + pytest.param(2, False, id="type_2_call"), + pytest.param(2, True, id="type_2_create"), + pytest.param(3, False, id="type_3_blob"), + pytest.param(4, False, id="type_4_set_code"), + ], +) +@pytest.mark.valid_from("EIP8037") +def test_tx_gas_limit_block_boundary( + blockchain_test: BlockchainTestFiller, + pre: Alloc, + tx_type: int, + contract_creation: bool, + block_gas_limit: int, + tx_gas_delta: int, + expected_exception: TransactionException | None, +) -> None: + """ + Reject tx whose ``gas_limit`` exceeds the block ``gas_limit``. + + EIP-8037 inclusion rule: ``min(TX_MAX_GAS_LIMIT, tx.gas) <= + regular_gas_available`` and ``tx.gas <= state_gas_available``. + At block start both budgets equal ``block_gas_limit``. + """ + gas_limit = block_gas_limit + tx_gas_delta + gas_price = 10 + sender = pre.fund_eoa(amount=gas_limit * gas_price + 10**18) + + to = None if contract_creation else pre.fund_eoa(amount=0) + access_list = None + authorization_list = None + blob_versioned_hashes = None + extra_fee_args: dict = {} + if tx_type == 1: + access_list = [AccessList(address=Address(1), storage_keys=[Hash(0)])] + elif tx_type == 2: + access_list = [] + elif tx_type == 3: + blob_versioned_hashes = add_kzg_version( + [Hash(1)], EIP4844_Spec.BLOB_COMMITMENT_VERSION_KZG + ) + extra_fee_args["max_fee_per_blob_gas"] = 1 + elif tx_type == 4: + authorization_list = [ + AuthorizationTuple( + signer=pre.fund_eoa(amount=0), address=Address(1) + ) + ] + + if tx_type in (0, 1): + fee_args: dict = {"gas_price": gas_price} + else: + fee_args = { + "max_fee_per_gas": gas_price, + "max_priority_fee_per_gas": 0, + } + fee_args.update(extra_fee_args) + + tx = Transaction( + ty=tx_type, + sender=sender, + to=to, + gas_limit=gas_limit, + access_list=access_list, + authorization_list=authorization_list, + blob_versioned_hashes=blob_versioned_hashes, + error=expected_exception, + **fee_args, + ) + + blockchain_test( + genesis_environment=Environment(gas_limit=block_gas_limit), + pre=pre, + blocks=[ + Block( + txs=[tx], + gas_limit=block_gas_limit, + exception=expected_exception, + ) + ], + post={}, + ) + + @pytest.mark.parametrize( "delta", [