1616
1717import pytest
1818from execution_testing import (
19+ AccessList ,
1920 Account ,
21+ Address ,
2022 Alloc ,
2123 AuthorizationTuple ,
2224 Environment ,
2931)
3032from execution_testing .checklists import EIPChecklist
3133
32- from .spec import Spec , ref_spec_8037
34+ from .spec import ref_spec_8037
3335
3436REFERENCE_SPEC_GIT_PATH = ref_spec_8037 .git_path
3537REFERENCE_SPEC_VERSION = ref_spec_8037 .version
@@ -266,6 +268,36 @@ def test_refund_with_reservoir_state_gas(
266268 state_test (env = env , pre = pre , post = post , tx = tx )
267269
268270
271+ def _access_list_over_regular_cap (
272+ fork : Fork , cap : int , * , margin_num : int = 1 , margin_den : int = 1
273+ ) -> list [AccessList ]:
274+ """
275+ Build an access list whose intrinsic *regular* gas exceeds ``cap`` by
276+ roughly the factor ``margin_num / margin_den``.
277+
278+ Each access-list address adds a fixed amount to the regular intrinsic
279+ (the EIP-2930 address cost plus the EIP-7981 floor-token surcharge) and
280+ a much smaller amount to the calldata floor, so the list raises the
281+ regular operand of ``max(intrinsic_regular, calldata_floor)`` over the
282+ cap while the floor stays below it. No state gas is incurred.
283+ """
284+ intrinsic = fork .transaction_intrinsic_cost_calculator ()
285+ base_regular = intrinsic (return_cost_deducted_prior_execution = True )
286+ per_address_regular = (
287+ intrinsic (
288+ access_list = [AccessList (address = Address (0x100 ), storage_keys = [])],
289+ return_cost_deducted_prior_execution = True ,
290+ )
291+ - base_regular
292+ )
293+ assert per_address_regular > 0
294+ num_entries = (cap * margin_num ) // (per_address_regular * margin_den ) + 1
295+ return [
296+ AccessList (address = Address (0x10000 + i ), storage_keys = [])
297+ for i in range (num_entries )
298+ ]
299+
300+
269301@pytest .mark .exception_test
270302@pytest .mark .valid_from ("EIP8037" )
271303def test_intrinsic_regular_gas_exceeds_cap (
@@ -274,30 +306,43 @@ def test_intrinsic_regular_gas_exceeds_cap(
274306 fork : Fork ,
275307) -> None :
276308 """
277- Test that tx is rejected when intrinsic regular gas exceeds cap.
278-
279- validate_transaction checks that the intrinsic regular gas (or
280- calldata floor) does not exceed the transaction gas limit cap.
281- A transaction with enough calldata to push intrinsic cost above
282- the cap is invalid even with a high gas_limit.
309+ Reject a transaction whose intrinsic *regular* gas exceeds the cap.
310+
311+ EIP-8037 enforces ``max(intrinsic_regular, calldata_floor) <=
312+ TX_MAX_GAS_LIMIT`` after the separate sufficiency check
313+ ``max(intrinsic_total, calldata_floor) <= tx.gas``. A large access list
314+ raises the regular intrinsic over the cap while adding no state gas and
315+ keeping the calldata floor below the cap. ``gas_limit`` is set above the
316+ total intrinsic so the sufficiency check passes and the cap is the only
317+ reason the transaction is rejected; a client that compares the intrinsic
318+ against ``tx.gas`` but never against the cap would wrongly accept it.
283319 """
284- gas_costs = fork .gas_costs ()
285- gas_limit_cap = fork .transaction_gas_limit_cap ()
286- assert gas_limit_cap is not None
287- # One more non-zero byte than needed to exceed the cap
288- calldata_len = gas_limit_cap // gas_costs .TX_DATA_PER_NON_ZERO + 1
289- calldata = b"\x01 " * calldata_len
320+ cap = fork .transaction_gas_limit_cap ()
321+ assert cap is not None
322+ floor_cost = fork .transaction_data_floor_cost_calculator ()
323+ intrinsic = fork .transaction_intrinsic_cost_calculator ()
324+
325+ access_list = _access_list_over_regular_cap (fork , cap )
326+ regular = intrinsic (
327+ access_list = access_list ,
328+ return_cost_deducted_prior_execution = True ,
329+ )
330+ floor = floor_cost (data = b"" , access_list = access_list )
331+ state = fork .transaction_intrinsic_state_gas ()
332+ tx_gas = regular + state + 1_000_000
290333
291- contract = pre .deploy_contract (code = Op .STOP )
334+ assert max (regular , floor ) > cap , "cap check must fire"
335+ assert regular + state <= tx_gas , "sufficiency check must not fire"
336+ assert floor <= tx_gas
292337
293338 tx = Transaction (
294- to = contract ,
295- gas_limit = gas_limit_cap * 2 ,
296- data = calldata ,
339+ ty = 1 ,
340+ to = pre .deploy_contract (code = Op .STOP ),
341+ gas_limit = tx_gas ,
342+ access_list = access_list ,
297343 sender = pre .fund_eoa (),
298344 error = TransactionException .INTRINSIC_GAS_TOO_LOW ,
299345 )
300-
301346 state_test (pre = pre , post = {}, tx = tx )
302347
303348
@@ -309,46 +354,96 @@ def test_intrinsic_regular_gas_exceeds_cap_with_floor_below_cap(
309354 fork : Fork ,
310355) -> None :
311356 """
312- Test rejection when intrinsic regular gas exceeds the per-tx gas
313- cap while the calldata floor stays below the cap.
314-
315- EIP-7825/8037 applies the cap to both intrinsic dimensions
316- independently. The companion `test_intrinsic_regular_gas_exceeds_cap`
317- pushes both dimensions above the cap with non-zero calldata, so an
318- implementation that only checks `max(regular, floor)` against the
319- cap would still pass. This test isolates the regular-only case via
320- a large EIP-7702 authorization list and minimal calldata.
357+ Reject when intrinsic *regular* gas exceeds the cap while the calldata
358+ floor stays below it, isolating the regular operand of
359+ ``max(intrinsic_regular, calldata_floor)``.
360+
361+ A large access list with no calldata pushes the regular intrinsic over
362+ the cap while the floor stays well below it, and ``gas_limit`` covers
363+ the total intrinsic so the sufficiency check passes. The explicit
364+ ``floor < cap`` assertion guarantees the rejection comes from the
365+ regular operand, so a client that compares only the calldata floor
366+ against the cap would wrongly accept the transaction.
321367 """
322- gas_limit_cap = fork .transaction_gas_limit_cap ()
323- assert gas_limit_cap is not None
368+ cap = fork .transaction_gas_limit_cap ()
369+ assert cap is not None
370+ floor_cost = fork .transaction_data_floor_cost_calculator ()
371+ intrinsic = fork .transaction_intrinsic_cost_calculator ()
324372
325- # Authorizations contribute to regular intrinsic only (not floor).
326- # Pick enough to push regular > cap by a comfortable margin.
327- auth_count = ( gas_limit_cap // Spec . PER_AUTH_BASE_COST ) + 1
328- calldata = b" \x01 " * 4 # tiny: floor stays << cap.
329-
330- target = pre . deploy_contract ( code = Op . STOP )
331- authorizations = [
332- AuthorizationTuple (
333- address = target ,
334- nonce = 0 ,
335- signer = pre . fund_eoa (),
336- )
337- for _ in range ( auth_count )
338- ]
373+ access_list = _access_list_over_regular_cap (
374+ fork , cap , margin_num = 5 , margin_den = 4
375+ )
376+ regular = intrinsic (
377+ access_list = access_list ,
378+ return_cost_deducted_prior_execution = True ,
379+ )
380+ floor = floor_cost ( data = b"" , access_list = access_list )
381+ state = fork . transaction_intrinsic_state_gas ()
382+ tx_gas = regular + state + 1_000_000
383+
384+ assert regular > cap , "regular operand must exceed the cap"
385+ assert floor < cap , "calldata floor must stay below the cap"
386+ assert regular + state <= tx_gas , "sufficiency check must not fire"
339387
340388 tx = Transaction (
341- ty = 4 ,
342- to = target ,
343- gas_limit = gas_limit_cap * 2 ,
344- data = calldata ,
345- authorization_list = authorizations ,
389+ ty = 1 ,
390+ to = pre .deploy_contract (code = Op .STOP ),
391+ gas_limit = tx_gas ,
392+ access_list = access_list ,
346393 sender = pre .fund_eoa (),
347394 error = TransactionException .INTRINSIC_GAS_TOO_LOW ,
348395 )
349396 state_test (pre = pre , post = {}, tx = tx )
350397
351398
399+ @pytest .mark .valid_from ("EIP8037" )
400+ def test_intrinsic_within_cap_gas_limit_above_cap (
401+ state_test : StateTestFiller ,
402+ pre : Alloc ,
403+ fork : Fork ,
404+ ) -> None :
405+ """
406+ Accept a transaction whose ``gas_limit`` exceeds the cap when both
407+ intrinsic operands stay below it.
408+
409+ EIP-8037 relaxes the EIP-7825 cap on ``tx.gas`` itself; only
410+ ``max(intrinsic_regular, calldata_floor)`` is capped. This positive
411+ control sets ``gas_limit`` above the cap with a small access list so
412+ both operands are far below it, and the transaction must execute. It is
413+ the accepting counterpart to the cap-rejection tests above.
414+ """
415+ cap = fork .transaction_gas_limit_cap ()
416+ assert cap is not None
417+ floor_cost = fork .transaction_data_floor_cost_calculator ()
418+ intrinsic = fork .transaction_intrinsic_cost_calculator ()
419+
420+ access_list = [
421+ AccessList (address = Address (0x10000 + i ), storage_keys = [])
422+ for i in range (16 )
423+ ]
424+ regular = intrinsic (
425+ access_list = access_list ,
426+ return_cost_deducted_prior_execution = True ,
427+ )
428+ floor = floor_cost (data = b"" , access_list = access_list )
429+ assert regular <= cap
430+ assert floor <= cap
431+
432+ storage = Storage ()
433+ contract = pre .deploy_contract (
434+ code = Op .SSTORE (storage .store_next (1 ), 1 ),
435+ )
436+
437+ tx = Transaction (
438+ ty = 1 ,
439+ to = contract ,
440+ gas_limit = cap + 3_000_000 ,
441+ access_list = access_list ,
442+ sender = pre .fund_eoa (),
443+ )
444+ state_test (pre = pre , post = {contract : Account (storage = storage )}, tx = tx )
445+
446+
352447@pytest .mark .parametrize (
353448 "above_floor" ,
354449 [
0 commit comments