Skip to content

feat(test-specs): Allow automatic transaction gas-limit - #2969

Merged
spencer-tb merged 46 commits into
ethereum:forks/amsterdamfrom
marioevz:implicit-gas-limit
Jun 15, 2026
Merged

feat(test-specs): Allow automatic transaction gas-limit#2969
spencer-tb merged 46 commits into
ethereum:forks/amsterdamfrom
marioevz:implicit-gas-limit

Conversation

@marioevz

@marioevz marioevz commented Jun 9, 2026

Copy link
Copy Markdown
Member

🗒️ Description

Summary

While reviewing #2901 I noticed that the bulk of its test changes were simply bumping the gas_limit of transactions in tests that don't actually care about the exact gas limit — they only care that the transaction executes in full without running out of gas.

This PR makes the transaction gas_limit optional. When a test omits it, the filling/execution tooling calculates it automatically based on fork properties.

Review Plan

Tests

folder Owner
tests/amsterdam/eip7928_block_level_access_lists @raxhvl
tests/amsterdam/eip8037_state_creation_gas_cost_increase @spencer-tb
tests/amsterdam/* (others) @spencer-tb
tests/berlin @kclowes
tests/byzantium ''
tests/cancun ''
tests/common ''
tests/constantinople ''
tests/frontier ''
tests/homestead @felix314159
tests/istanbul ''
tests/osaka ''
tests/paris ''
tests/prague ''
tests/shanghai @danceratopz
tests/ported_static @spencer-tb

Testing Framework

folder Owner
packages/testing/src/execution_testing/cli/pytest_commands/plugins @leolara
packages/testing/src/execution_testing/execution @leolara
packages/testing/src/execution_testing/fixtures @leolara
packages/testing/src/execution_testing/forks @leolara
packages/testing/src/execution_testing/specs @leolara
packages/testing/src/execution_testing/test_types @leolara
packages/testing/src/execution_testing/tools @leolara

Tooling changes

Transaction.gas_limit now defaults to None instead of HexNumber(21_000).

Transaction.set_gas_limit() resolves an unset limit in place, and signing now raises ValueError("gas_limit must be set to sign a transaction") if the limit is still unset by the time the transaction is signed.

This lead to many typing issues that have now been resolved, but it can be argued that a better choice is to leave HexNumber as the type for Transaction.gas_limit and rely instead on model_fields_set to determine whether the gas limit has been set by the tester.

BlockchainTest and StateTest Fillers

For State tests (StateTest), an unset gas_limit is filled with the fork's transaction_gas_limit_cap(), falling back to the environment gas_limit when there is no cap.

For Blockchain tests (BlockchainTest), the block's remaining gas (env.gas_limit minus the gas already claimed by transactions with explicit limits) is split evenly across the transactions that leave gas_limit unset, clamped to the fork's transaction gas-limit cap. It raises a clear "test correctness" error if no gas remains.

Execute Tests

The same logic is implemented for the execute path (calculate_max_transaction_gas_limit in execution/base.py, applied in transaction_post.py and blob_transaction.py), so implicit gas limits work when running against a live client, not just during filling.

It can be argued that there's a better path to resolve the transaction gas limit in the case of execute, such as:

  • Use eth_estimateGas and use the value returned, with the minor caveat that the contracts that the transaction requires have to be already on chain before calling this RPC method.
  • Run the T8N tool interface with the transaction to obtain the gas used.

Both methods require more work and could be implemented in a future PR.

State gas reservoir (EIP-8037)

Rather than special-casing EIP-8037 (fork.is_eip_enabled(8037)), this PR introduces a fork-level concept:

  • New abstract fork method Fork.state_gas_reservoir_enabled() (alongside the existing transaction_gas_limit_cap()), defaulting to False and turned on by EIP-8037, currently in Amsterdam.
  • New Transaction.state_gas_reservoir field. When a fork enables the reservoir, the transaction gas-limit cap is treated as removed and set_gas_limit() accounts for the requested reservoir (transaction_gas_limit_cap + state_gas_reservoir) when sizing the implicit limit.
  • EIP-8037 tests were refactored to express their requirement through state_gas_reservoir instead of hand-computing gas limits (tx.state_gas_reservoir = state_gas_reservoir instead of tx.gas_limit = gas_limit_cap + state_gas_reservoir).

Test cleanup

The implicit gas limit was applied across the test suite, removing explicit gas_limit assignments from transactions wherever the precise limit was irrelevant to what the test exercises. This touches ~509 test files (net ~7,200 lines removed) spanning Frontier through Amsterdam and many individual EIPs (1153, 4844, 5656, 6780, 7516, 7708, 7843, 7928, 8024, Blake2, etc.).

Tests that should rely on the implicit gas limit are those that only need the transaction to execute fully without running out of gas. Tests that intentionally probe gas-limit behavior continue to set it explicitly.

Incidental fixes surfaced while porting tests

  • pre.fund_eoa default bumped from 10**21 to 10**27 to fix deposit tests that were under-funded.
  • EIP-7883 was not being activated in Osaka; this is now fixed.

🔗 Related Issues or PRs

Closes #2248:

✅ Checklist

  • All: Ran fast static checks to avoid unnecessary CI fails, see also Code Standards and Enabling Pre-commit Checks:
    just static
  • All: PR title adheres to the repo standard - it will be used as the squash commit message and should start type(scope):.
  • All: Considered updating the online docs in the ./docs/ directory.
  • All: Set appropriate labels for the changes (only maintainers can apply labels).
  • Tests: Ran mkdocs serve locally and verified the auto-generated docs for new tests in the Test Case Reference are correctly formatted.
  • Tests: For PRs implementing a missed test case, update the post-mortem document to add an entry the list.
  • Ported Tests: All converted JSON/YML tests from ethereum/tests or tests/static have been assigned @ported_from marker.

Cute Animal Picture

Put a link to a cute animal picture inside the parenthesis-->

@kclowes kclowes left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Berlin-Frontier look good to me. This is such a nice change!

@leolara

leolara commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

@marioevz I think there are places where we are setting Transaction(gas_limit=None) but setting it to a variable that can be None.

@leolara

leolara commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

@marioevz I think there are places where we are setting Transaction(gas_limit=None) but setting it to a variable that can be None.

Like here: https://github.com/marioevz/execution-specs/blob/f82f657ed2ff7f4d0a5ecd39b3aadb4aaae41d16/tests/prague/eip7002_el_triggerable_withdrawals/helpers.py#L117

@leolara

leolara commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

I think there are no unit tests for with_gas_limit, _calculate_implicit_gas_limit, calculate_max_gas_limit and state_gas_reservoir. Perhaps we don't need it because they are called from other tests.

@marioevz

Copy link
Copy Markdown
Member Author

@marioevz I think there are places where we are setting Transaction(gas_limit=None) but setting it to a variable that can be None.

Like here: https://github.com/marioevz/execution-specs/blob/f82f657ed2ff7f4d0a5ecd39b3aadb4aaae41d16/tests/prague/eip7002_el_triggerable_withdrawals/helpers.py#L117

Nice catch, uploaded a new commit a2e13fb. Just as a side note, I think this specific tests need an update because they are no longer really accurate (they hard-code assumed gas costs for the system contracts, which no longer hold).

@marioevz

Copy link
Copy Markdown
Member Author

Once @spencer-tb signs off we can proceed to merge.

@spencer-tb spencer-tb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a final check this morning! All LGTM :)
#2984 feels appropriate to me too. Lets merge!

@spencer-tb
spencer-tb merged commit ae18430 into ethereum:forks/amsterdam Jun 15, 2026
26 checks passed
danceratopz added a commit to danceratopz/execution-specs that referenced this pull request Jun 16, 2026
`test_code_deposit_oog_preserves_parent_reservoir` forwards a fixed
`child_gas` to the factory via `Op.CALL`. After the factory's CREATE
takes 63/64, the retained 1/64 (~15k from 1M) no longer covers its
repriced post-CREATE SSTOREs (~21k), so the factory halted before its
`parent_sstore` and the expected storage write disappeared.

Raise the in-bytecode `child_gas` 1000000 -> 1500000; the factory then
retains ~23k. The code-deposit OOG premise (deploy size 4096, deposit
state gas 6.27M) is unaffected. PR ethereum#2969 auto-sizes the transaction gas
limit but not a `CALL` gas argument inside contract code, so this budget
is re-tuned by hand.
leolara added a commit to leolara/execution-specs that referenced this pull request Jun 17, 2026
PR ethereum#2969 computes the transaction gas limit automatically and adds a
state-gas reservoir on EIP-8037 forks. These 71 files set an explicit
transaction gas_limit that was incidental: it sufficed on older forks,
but on Amsterdam the state-gas component pushed the transaction over its
budget and broke the test. Leaving the gas limit unset gives each
transaction the implicit limit plus reservoir, which restores the
recorded full-execution behaviour.

Drop the transaction gas_limit from these files, together with the
helpers it orphaned (tx_gas lists, an intrinsic-gas computation) and
three now-unused fork-conditional bumps that the unset limit supersedes.
Filling them on Amsterdam passes every variant with no residual
failures, so remove their 123 entries from amsterdam_skip_list.txt.
Total 437 down to 314.
leolara added a commit to leolara/execution-specs that referenced this pull request Jun 17, 2026
PR ethereum#2969 computes the transaction gas limit automatically and adds a
state-gas reservoir on EIP-8037 forks. These 69 files set an explicit
transaction gas_limit that was incidental: it sufficed on older forks,
but on Amsterdam the state-gas component pushed the transaction over its
budget and broke the test. Leaving the gas limit unset gives each
transaction the implicit limit plus reservoir, which restores the
recorded full-execution behaviour.

Drop the transaction gas_limit from these files, together with the
helpers it orphaned (tx_gas lists, an intrinsic-gas computation) and
three now-unused fork-conditional bumps that the unset limit supersedes.
Filling them across every valid fork passes all variants with no
residual failures, so remove their 116 entries from
amsterdam_skip_list.txt. Total 437 down to 321.
danceratopz added a commit to danceratopz/execution-specs that referenced this pull request Jun 18, 2026
`test_code_deposit_oog_preserves_parent_reservoir` forwards a fixed
`child_gas` to the factory via `Op.CALL`. After the factory's CREATE
takes 63/64, the retained 1/64 (~15k from 1M) no longer covers its
repriced post-CREATE SSTOREs (~21k), so the factory halted before its
`parent_sstore` and the expected storage write disappeared.

Raise the in-bytecode `child_gas` 1000000 -> 1500000; the factory then
retains ~23k. The code-deposit OOG premise (deploy size 4096, deposit
state gas 6.27M) is unaffected. PR ethereum#2969 auto-sizes the transaction gas
limit but not a `CALL` gas argument inside contract code, so this budget
is re-tuned by hand.
danceratopz added a commit to danceratopz/execution-specs that referenced this pull request Jun 18, 2026
`test_code_deposit_oog_preserves_parent_reservoir` forwards a fixed
`child_gas` to the factory via `Op.CALL`. After the factory's CREATE
takes 63/64, the retained 1/64 (~15k from 1M) no longer covers its
repriced post-CREATE SSTOREs (~21k), so the factory halted before its
`parent_sstore` and the expected storage write disappeared.

Raise the in-bytecode `child_gas` 1000000 -> 1500000; the factory then
retains ~23k. The code-deposit OOG premise (deploy size 4096, deposit
state gas 6.27M) is unaffected. PR ethereum#2969 auto-sizes the transaction gas
limit but not a `CALL` gas argument inside contract code, so this budget
is re-tuned by hand.
leolara added a commit to leolara/execution-specs that referenced this pull request Jun 19, 2026
PR ethereum#2969 made the transaction gas limit automatic. On EIP-8037 forks
the implicit limit now carries a state-gas reservoir, so tests that
leave the transaction gas limit unset receive enough headroom and stop
running out of gas under the two-dimensional gas model.

Filling tests/ported_static/ for Amsterdam without the skip list shows
33 of these entries pass across all fixture variants. Remove them from
amsterdam_skip_list.txt and recompute the per-section counts and the
total, from 480 down to 447. Two sections become empty and are dropped,
stCallDelegateCodesHomestead and stRecursiveCreate.

Most of the cleared cases are the _suicide_end family. They set only
the environment gas limit and rely on the implicit transaction gas
limit, so the reservoir restores their pre-Amsterdam behaviour.
leolara added a commit to leolara/execution-specs that referenced this pull request Jun 19, 2026
PR ethereum#2969 computes the transaction gas limit automatically and adds a
state-gas reservoir on EIP-8037 forks. These 69 files set an explicit
transaction gas_limit that was incidental: it sufficed on older forks,
but on Amsterdam the state-gas component pushed the transaction over its
budget and broke the test. Leaving the gas limit unset gives each
transaction the implicit limit plus reservoir, which restores the
recorded full-execution behaviour.

Drop the transaction gas_limit from these files, together with the
helpers it orphaned (tx_gas lists, an intrinsic-gas computation) and
three now-unused fork-conditional bumps that the unset limit supersedes.
Filling them across every valid fork passes all variants with no
residual failures, so remove their 116 entries from
amsterdam_skip_list.txt. Total 437 down to 321.
marioevz added a commit that referenced this pull request Jun 24, 2026
…2996)

* chore(tests): unskip ported_static cases now passing on Amsterdam

PR #2969 made the transaction gas limit automatic. On EIP-8037 forks
the implicit limit now carries a state-gas reservoir, so tests that
leave the transaction gas limit unset receive enough headroom and stop
running out of gas under the two-dimensional gas model.

Filling tests/ported_static/ for Amsterdam without the skip list shows
33 of these entries pass across all fixture variants. Remove them from
amsterdam_skip_list.txt and recompute the per-section counts and the
total, from 480 down to 447. Two sections become empty and are dropped,
stCallDelegateCodesHomestead and stRecursiveCreate.

Most of the cleared cases are the _suicide_end family. They set only
the environment gas limit and rely on the implicit transaction gas
limit, so the reservoir restores their pre-Amsterdam behaviour.

* chore(tests): drop obsolete ported_static skip entries on Amsterdam

These 10 entries match no test collected on Amsterdam, so the conftest
never skips anything for them. They are dead weight in the list.

Eight are size-limit tests under stCodeSizeLimit and
stEIP3860_limitmeterinitcode. They carry valid_before("EIP7954"), and
Amsterdam includes EIP-7954, so pytest deselects them and they never
reach the skip step. Both sections empty out and are dropped.

Two are stCreateTest/test_create_address_warm_after_fail cases pinned
to a create-code-too-big-v1 parametrization that no longer exists after
the test was reparametrized.

Recompute the section counts and the total, from 447 down to 437.

* chore(tests): unset incidental tx gas_limit on 69 ported_static files

PR #2969 computes the transaction gas limit automatically and adds a
state-gas reservoir on EIP-8037 forks. These 69 files set an explicit
transaction gas_limit that was incidental: it sufficed on older forks,
but on Amsterdam the state-gas component pushed the transaction over its
budget and broke the test. Leaving the gas limit unset gives each
transaction the implicit limit plus reservoir, which restores the
recorded full-execution behaviour.

Drop the transaction gas_limit from these files, together with the
helpers it orphaned (tx_gas lists, an intrinsic-gas computation) and
three now-unused fork-conditional bumps that the unset limit supersedes.
Filling them across every valid fork passes all variants with no
residual failures, so remove their 116 entries from
amsterdam_skip_list.txt. Total 437 down to 321.

* chore(tests): unset g1 gas_limit on Amsterdam for no_src_account_create

test_no_src_account_create and its 1559 variant assert an
INSUFFICIENT_ACCOUNT_FUNDS rejection at gas index g1 (210000). On
Amsterdam EIP-8037 raises the creation intrinsic gas above that budget,
so the transaction is rejected for gas before the funds check and the
assertion fails.

Make the g1 budget fork-conditional: leave the gas limit unset on
EIP-8037 forks, where the implicit limit plus reservoir clears the
intrinsic and the intended funds rejection fires again. Earlier forks
keep the original 210000. Filling across every valid fork passes all
variants, so remove the 16 g1 entries from amsterdam_skip_list.txt.
Total 321 down to 305.

* chore(tests): fork-conditional gas unset for 8 more ported_static files

Extend the per-parametrization fix from no_src_account_create to eight
more mixed files. Each sets the skip-listed gas slot to None on EIP-8037,
so that parametrization gets the implicit limit plus reservoir, while the
other slots and earlier forks keep their explicit gas.

These slots recorded a full-execution result that broke on Amsterdam when
the state-gas component pushed the transaction over its budget. The
stZeroKnowledge point_mul tests run out of gas because they SSTORE the
precompile result, which EIP-8037 makes more expensive; the precompile
call itself is unchanged, so unsetting restores the recorded success
state.

Filling across every valid fork passes all variants with no failures, so
remove the 47 entries from amsterdam_skip_list.txt. Total 305 down to 258.

* chore(tests): Port and remove failing stZeroKnowledge tests

---------

Co-authored-by: marioevz <marioevz@gmail.com>
gurukamath pushed a commit to gurukamath/execution-specs that referenced this pull request Jun 25, 2026
LouisTsai-Csie pushed a commit that referenced this pull request Jun 26, 2026
Co-authored-by: Leo Lara <leolara@users.noreply.github.com>
Co-authored-by: danceratopz <danceratopz@gmail.com>
Co-authored-by: spencer-tb <spencer.tb@ethereum.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Suggestion: For tests, let the default tx gas limit be the maximum allowed value

7 participants