Skip to content

Commit de86c20

Browse files
kclowesspencer-tb
andcommitted
feat(spec-specs, tests): EIP-8037 - per-dimension block gas limit check at tx inclusion (#2703)
* feat(tests): 8037 Check 2d gas before tx inclusion * feat(spec-specs): More tests for 2d tx inclusion spec change * fix(tests): consolidate state gas boundary tests and clean docstrings Cleanups to the per-dimension block gas inclusion tests: - Combine `test_block_state_gas_limit_exact_fit` and `test_block_state_gas_limit_exceeded` into `test_block_state_gas_limit_boundary` parametrized `delta=[0, 1]` (ids `exact_fit`, `exceeded`) and inline the `_block_state_gas_limit_setup` helper. Removes ~45 lines with no coverage loss. - Fix docstring references in both boundary cases: state contribution is `tx.gas - intrinsic_regular`, not `tx.gas - TX_MAX_GAS_LIMIT`. - Tighten wording in `test_creation_tx_regular_check_subtracts_intrinsic_state` and add assertion messages that spell out the old-vs-new formula discrimination. - Apply ruff format (drop an unneeded line wrap on `create_intrinsic_regular` in `test_creation_tx_state_check_exceeded`). Co-authored-by: kclowes <kclowes@users.noreply.github.com> --------- Co-authored-by: spencer-tb <spencer.tb@ethereum.org>
1 parent c230841 commit de86c20

3 files changed

Lines changed: 334 additions & 20 deletions

File tree

src/ethereum/forks/amsterdam/fork.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
TX_MAX_GAS_LIMIT,
8181
BlobTransaction,
8282
FeeMarketTransaction,
83+
IntrinsicGasCost,
8384
LegacyTransaction,
8485
SetCodeTransaction,
8586
Transaction,
@@ -488,6 +489,7 @@ def check_transaction(
488489
block_output: vm.BlockOutput,
489490
tx: Transaction,
490491
tx_state: TransactionState,
492+
intrinsic: IntrinsicGasCost,
491493
) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
492494
"""
493495
Check if the transaction is includable in the block.
@@ -502,6 +504,9 @@ def check_transaction(
502504
The transaction.
503505
tx_state :
504506
The transaction state tracker.
507+
intrinsic :
508+
The transaction's intrinsic gas cost, split into regular and
509+
state components.
505510
506511
Returns
507512
-------
@@ -549,16 +554,29 @@ def check_transaction(
549554
is empty.
550555
551556
"""
552-
# Regular gas is capped at TX_MAX_GAS_LIMIT per EIP-7825.
553-
# State gas is not checked per-tx; block-end validation enforces
557+
# Per-tx 2D gas inclusion check: for each dimension the worst-case
558+
# contribution must fit in the remaining budget. Block-end
559+
# validation still enforces
554560
# max(block_regular_gas_used, block_state_gas_used) <= gas_limit.
555561
regular_gas_available = (
556562
block_env.block_gas_limit - block_output.block_gas_used
557563
)
564+
state_gas_available = (
565+
block_env.block_gas_limit - block_output.block_state_gas_used
566+
)
558567
blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
559568

560-
if min(TX_MAX_GAS_LIMIT, tx.gas) > regular_gas_available:
561-
raise GasUsedExceedsLimitError("regular gas used exceeds limit")
569+
# Worst-case regular contribution: tx.gas minus the portion that
570+
# must go to intrinsic state gas, capped at TX_MAX_GAS_LIMIT.
571+
if min(TX_MAX_GAS_LIMIT, tx.gas - intrinsic.state) > (
572+
regular_gas_available
573+
):
574+
raise GasUsedExceedsLimitError("gas used exceeds limit")
575+
576+
# Worst-case state contribution: tx.gas minus the portion that
577+
# must go to intrinsic regular gas.
578+
if tx.gas - intrinsic.regular > state_gas_available:
579+
raise GasUsedExceedsLimitError("gas used exceeds limit")
562580

563581
tx_blob_gas_used = calculate_total_blob_gas(tx)
564582
if tx_blob_gas_used > blob_gas_available:
@@ -991,6 +1009,7 @@ def process_transaction(
9911009
block_output=block_output,
9921010
tx=tx,
9931011
tx_state=tx_state,
1012+
intrinsic=intrinsic,
9941013
)
9951014

9961015
sender_account = get_account(tx_state, sender)

tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_block_2d_gas_accounting.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -288,21 +288,28 @@ def test_block_2d_gas_boundary_exact_fit(
288288
num_sstores: int,
289289
) -> None:
290290
"""
291-
Verify a block is valid when max(regular, state) == gas_limit.
291+
Verify a block is valid when state gas dominates regular gas.
292292
293-
Set block_gas_limit = block_state_gas (the dominant dimension).
294293
Clients that sum regular + state will reject this valid block.
295294
"""
296295
tx_regular, tx_state = sstore_tx_gas(fork, num_sstores)
297-
block_state = num_txs * tx_state
298-
block_gas_limit = block_state
296+
intrinsic_regular = fork.transaction_intrinsic_cost_calculator()()
299297

300-
# tx_limit must exceed tx_regular + tx_state so the tx is valid,
301-
# but the per-tx regular reservation against block gas must still
302-
# leave room for all txs.
303298
tx_limit = tx_regular + tx_state + tx_regular // 10
304-
worst = block_gas_limit - (num_txs - 1) * tx_regular
305-
assert worst >= tx_limit, "per-tx regular gas check fails"
299+
300+
# Per-tx worst-case state contribution: tx.gas - intrinsic_regular.
301+
# The block_gas_limit must leave enough state budget for every tx.
302+
worst_state_per_tx = tx_limit - intrinsic_regular
303+
block_gas_limit = max(
304+
# Regular dimension: last tx must fit.
305+
(num_txs - 1) * tx_regular + tx_limit,
306+
# State dimension: cumulative worst-case must fit.
307+
num_txs * worst_state_per_tx,
308+
)
309+
310+
block_regular = num_txs * tx_regular
311+
block_state = num_txs * tx_state
312+
expected_gas_used = max(block_regular, block_state)
306313

307314
txs, post = sstore_txs(
308315
pre,
@@ -320,7 +327,7 @@ def test_block_2d_gas_boundary_exact_fit(
320327
Block(
321328
txs=txs,
322329
gas_limit=block_gas_limit,
323-
header_verify=Header(gas_used=block_gas_limit),
330+
header_verify=Header(gas_used=expected_gas_used),
324331
)
325332
],
326333
post=post,

0 commit comments

Comments
 (0)