Skip to content

refactor(spec-specs): add opcode gas constants#2396

Merged
SamWilsn merged 63 commits intoethereum:forks/amsterdamfrom
Carsons-Eels:intermediate_opcodes
Apr 20, 2026
Merged

refactor(spec-specs): add opcode gas constants#2396
SamWilsn merged 63 commits intoethereum:forks/amsterdamfrom
Carsons-Eels:intermediate_opcodes

Conversation

@Carsons-Eels
Copy link
Copy Markdown
Contributor

@Carsons-Eels Carsons-Eels commented Mar 3, 2026

🗒️ Description

Currently, multiple opcodes share the same gas tier constant (e.g. DIV, SDIV, MOD, SMOD, MUL all reference GAS_LOW = Uint(5)). This makes it impossible to reprice individual opcodes because changing GAS_LOW would affect all of them at once. EIP-7904 - General Repricing requires per-opcode repricing (e.g. DIV=15, SDIV=20, MOD=12 while MUL stays at 5).

This PR adds per-opcode gas constants (OPCODE_*) to all 24 fork gas.py modules and updates instruction files to use them instead of shared tier constants (VERY_LOW, LOW, MID, HIGH), and likewise makes the same changes to the test code by adding counterpart gas constants to the GasCosts dataclass.

It introduces intermediate variables like OPCODE_DIV = LOW in each fork's gas.py and updates instruction files to charge against the opcode-specific constant. The existing apply_spec_repricing() mechanism (from #2331) can then target OPCODE_DIV directly in the JSON config, enabling per-opcode repricing without changing spec code.

What changed:

Spec side:

  • added 30–38 OPCODE_* variable definitions (varying by fork based on available opcodes)
  • added ~20 OPCODE_* constants for BASE-tier opcodes (OPCODE_POP, OPCODE_MSIZE, OPCODE_ADDRESS, OPCODE_TIMESTAMP, OPCODE_GASLIMIT, etc.) plus fork-specific constants (OPCODE_DIFFICULTY pre-Paris, OPCODE_PREVRANDAO Paris+, OPCODE_RETURNDATASIZE Byzantium+, OPCODE_CHAINID Istanbul+, OPCODE_BASEFEE London+, OPCODE_BLOBBASEFEE Cancun+, OPCODE_PUSH0 Shanghai+)
  • 168 instruction files (7 per fork): updated imports and charge_gas() calls to use opcode-specific constants
  • 24 fork.py files: moved GAS_LIMIT_ADJUSTMENT_FACTOR and GAS_LIMIT_MINIMUM into GasCosts (minus GAS_ prefix); updated check_gas_limit() references
  • 24 transactions.py files: moved GAS_TX_BASE, GAS_TX_CREATE, GAS_TX_DATA_PER_*, GAS_TX_ACCESS_LIST_* into GasCosts (minus GAS_ prefix); removed module-level definitions and RST docstrings; used lazy imports where circular dependencies exist
  • 8 vm/eoa_delegation.py files (Prague+): moved GAS_AUTH_PER_EMPTY_ACCOUNT into GasCosts as AUTH_PER_EMPTY_ACCOUNT
  • 120 instruction files (5 per fork): replaced all charge_gas(evm, GasCosts.BASE) calls with named GasCosts.OPCODE_* constants

Testing side:

  • gas_costs.py: added 39 OPCODE_* fields to GasCosts dataclass
  • forks.py: updated Frontier.gas_costs() initialization and opcode_gas_map() in Frontier + 4 fork overrides (Byzantium, Constantinople, Cancun, Osaka) to reference per-opcode fields

No behavioural changes — all variables default to their original tier constant values

Fork groups by opcode availability:

Forks Variables
frontier → spurious_dragon 30 (baseline)
byzantium +RETURNDATACOPY
constantinople → shanghai +SHL, SHR, SAR
cancun, prague +MCOPY
osaka → amsterdam +CLZ
Forks Tx Constants
frontier → amsterdam TX_BASE, LIMIT_ADJUSTMENT_FACTOR, LIMIT_MINIMUM
frontier No TX_CREATE
homestead → amsterdam + TX_CREATE
frontier → constantinople TX_DATA_PER_NON_ZERO = 68
istanbul → amsterdam TX_DATA_PER_NON_ZERO = 16
berlin → amsterdam + TX_ACCESS_LIST_ADDRESS, TX_ACCESS_LIST_STORAGE_KEY
prague → amsterdam TX_DATA_TOKEN_FLOOR, TX_DATA_TOKEN_STANDARD (replace PER_ZERO/PER_NON_ZERO), AUTH_PER_EMPTY_ACCOUNT

🔗 Related Issues or PRs

  • Precursor to #2331 (gas repricing tool)
  • Enables per-opcode repricing needed by #2200
  • Required for EIP-7904 general repricing implementation

❓ Further Questions

  • With constants like OPCODE_BALANCE, post-Berlin they go from a static cost to a dynamic cost. I've left them in the dynamic section for now, but with the static naming. Treating them differently depending on the fork will cause ordering issues that ethereum-spec-lint will complain about. Which to pick, or is there a better option?
  • Should the transaction max gas limit also be re-pricable? Currently let it in the module code.

✅ Checklist

  • All: Ran fast tox checks to avoid unnecessary CI fails, see also Code Standards and Enabling Pre-commit Checks:
    uvx tox -e 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).
  • All: PR title adheres to the repo standard
    • it will be used as the squash commit message and should start
      type(scope):.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 3, 2026

Codecov Report

❌ Patch coverage is 96.69492% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.47%. Comparing base (554bd41) to head (732a3aa).
⚠️ Report is 5 commits behind head on forks/amsterdam.

Files with missing lines Patch % Lines
src/ethereum/forks/bpo1/transactions.py 0.00% 5 Missing ⚠️
src/ethereum/forks/bpo2/transactions.py 0.00% 5 Missing ⚠️
src/ethereum/forks/bpo3/transactions.py 0.00% 5 Missing ⚠️
src/ethereum/forks/bpo4/transactions.py 0.00% 5 Missing ⚠️
src/ethereum/forks/bpo5/transactions.py 0.00% 5 Missing ⚠️
src/ethereum/forks/arrow_glacier/transactions.py 0.00% 4 Missing ⚠️
src/ethereum/forks/berlin/fork.py 33.33% 2 Missing ⚠️
...c/ethereum/forks/berlin/vm/instructions/storage.py 80.00% 2 Missing ⚠️
src/ethereum/forks/byzantium/fork.py 33.33% 2 Missing ⚠️
src/ethereum/forks/constantinople/fork.py 33.33% 2 Missing ⚠️
... and 2 more
Additional details and impacted files
@@                 Coverage Diff                 @@
##           forks/amsterdam    #2396      +/-   ##
===================================================
+ Coverage            86.26%   86.47%   +0.21%     
===================================================
  Files                  599      599              
  Lines                37038    37632     +594     
  Branches              3795     3795              
===================================================
+ Hits                 31949    32542     +593     
- Misses                4525     4526       +1     
  Partials               564      564              
Flag Coverage Δ
unittests 86.47% <96.69%> (+0.21%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@marioevz
Copy link
Copy Markdown
Member

marioevz commented Mar 3, 2026

Awesome!

cc @raxhvl, since this was directly mentioned in the gas-lighters call today.

@marioevz marioevz requested a review from SamWilsn March 3, 2026 18:52
@Carsons-Eels Carsons-Eels added A-spec-specs Area: Specification—The Ethereum specification itself (eg. `src/ethereum/*`) E-easy Experience: easy, good for newcomers P-high C-refactor Category: refactor A-test-tests Area: tests for packages/testing labels Mar 3, 2026
@Carsons-Eels Carsons-Eels marked this pull request as ready for review March 3, 2026 21:25
@marioevz
Copy link
Copy Markdown
Member

marioevz commented Mar 4, 2026

All failing tests are due to #2109, and unrelated to the changes in this PR:

2026-03-03T22:40:39.1715358Z FAILED tests/json_infra/fixtures/amsterdam_tests/fixtures/blockchain_tests/frontier/validation/test_gas_limit_below_minimum.json::tests/frontier/validation/test_header.py::test_gas_limit_below_minimum[fork_Amsterdam-blockchain_test-gas_limit_5000]
2026-03-03T22:40:39.1719512Z FAILED tests/json_infra/fixtures/amsterdam_tests/fixtures/blockchain_tests/static/state_tests/stStaticCall/static_InternalCallHittingGasLimit.json::tests/static/state_tests/stStaticCall/static_InternalCallHittingGasLimitFiller.json::static_InternalCallHittingGasLimit[fork_Amsterdam-blockchain_test_from_state_test-]
2026-03-03T22:40:39.1723591Z FAILED tests/json_infra/fixtures/amsterdam_tests/fixtures/blockchain_tests/static/state_tests/stTransactionTest/InternalCallHittingGasLimit.json::tests/static/state_tests/stTransactionTest/InternalCallHittingGasLimitFiller.json::InternalCallHittingGasLimit[fork_Amsterdam-blockchain_test_from_state_test-]

It should be ok for us to proceed to review -> merge.

Copy link
Copy Markdown
Member

@marioevz marioevz left a comment

Choose a reason for hiding this comment

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

Looks good to me, considering we are not touching opcodes that use GAS_BASE, which I'm guessing is because the amount of changes would grow even more, and it's not necessary for the gas repricings at the moment, we could leave it for a future PR if it becomes necessary IMO.

I just left a couple of comments and leave the approval to Sam when he returns.

Comment thread packages/testing/src/execution_testing/forks/gas_costs.py Outdated
Comment thread src/ethereum/forks/amsterdam/vm/gas.py Outdated
Comment thread packages/testing/src/execution_testing/forks/forks/forks.py Outdated
Comment thread packages/testing/src/execution_testing/forks/forks/forks.py Outdated
Copy link
Copy Markdown
Contributor

@SamWilsn SamWilsn left a comment

Choose a reason for hiding this comment

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

Looks good to me! Is the intent to make these constants modifiable at runtime? If so, there's some additional nonsense you need to do because Python.

@Carsons-Eels Carsons-Eels force-pushed the intermediate_opcodes branch from bcf06ad to 4fb062e Compare March 10, 2026 02:56
Copy link
Copy Markdown
Contributor Author

@Carsons-Eels Carsons-Eels left a comment

Choose a reason for hiding this comment

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

Regarding Louis' comment about constants in Frontier; I pushed a change that moves the 8 GAS_OPCODE_* fields that don't exist until after Frontier (SHL, SHR, SAR, RETURNDATACOPY, BLOBHASH, MCOPY, CLZ) to dataclass defaults and then initialize them via replace() where the fork introduces new terms. Frontier no longer references them.

I think we could do the same thing for the other 23 non-opcode constants that are zero-initialised in Frontier but aren't used until later forks:

Byzantium: GAS_PRECOMPILE_ECADD, GAS_PRECOMPILE_ECMUL, GAS_PRECOMPILE_ECPAIRING_BASE, GAS_PRECOMPILE_ECPAIRING_PER_POINT
Istanbul: GAS_PRECOMPILE_BLAKE2F_BASE, GAS_PRECOMPILE_BLAKE2F_PER_ROUND
Berlin: GAS_TX_ACCESS_LIST_ADDRESS, GAS_TX_ACCESS_LIST_STORAGE_KEY
Cancun: GAS_PRECOMPILE_POINT_EVALUATION
Prague: GAS_TX_DATA_TOKEN_STANDARD, GAS_TX_DATA_TOKEN_FLOOR, GAS_AUTH_PER_EMPTY_ACCOUNT, REFUND_AUTH_PER_EXISTING_ACCOUNT, GAS_PRECOMPILE_BLS_G1ADD, GAS_PRECOMPILE_BLS_G1MUL, GAS_PRECOMPILE_BLS_G1MAP, GAS_PRECOMPILE_BLS_G2ADD, GAS_PRECOMPILE_BLS_G2MUL, GAS_PRECOMPILE_BLS_G2MAP, GAS_PRECOMPILE_BLS_PAIRING_BASE, GAS_PRECOMPILE_BLS_PAIRING_PER_PAIR
Osaka: GAS_PRECOMPILE_P256VERIFY
Amsterdam: GAS_BLOCK_ACCESS_LIST_ITEM

None of these are referenced in Frontier, and they're already set by replace() by the fork that introduces them; they'd just also get = 0 defaults on the dataclass. Frontier doesn't need to mention them at all. This would follow along the same lines as the adjustment I made for Louis' comment, but that predates this PR, so I'd rather get feedback first. We're kind of already here though, so might as well if it feels right.

Thoughts? @LouisTsai-Csie @marioevz

@Carsons-Eels
Copy link
Copy Markdown
Contributor Author

Carsons-Eels commented Mar 10, 2026

Looks good to me! Is the intent to make these constants modifiable at runtime? If so, there's some additional nonsense you need to do because Python.

I'll address that in #2331 scratch that, already modifying those files here. Might as well make the changes here and keep the diff of #2331 smaller.

@raxhvl
Copy link
Copy Markdown
Member

raxhvl commented Mar 17, 2026

opitonal nit: rename GAS_OPCODE_PUSH_N to GAS_OPCODE_PUSH

To ensure consistency with GAS_OPCODE_DUP/SWAP which are now named after their opcodes

@Carsons-Eels Carsons-Eels force-pushed the intermediate_opcodes branch 3 times, most recently from 30525d8 to e9f2c37 Compare March 25, 2026 16:12
@Carsons-Eels
Copy link
Copy Markdown
Contributor Author

I updated GAS_OPCODE_PUSH_N to GAS_OPCODE_PUSH and also added GAS_OPCODE_COINBASE too. I'm validating that no more have been missed for any of the forks, and will add those opcodes as well.

Copy link
Copy Markdown
Contributor

@SamWilsn SamWilsn left a comment

Choose a reason for hiding this comment

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

This is packages/** and src/ethereum/forks/amsterdam/**, and the occasional comment elsewhere.

Comment thread src/ethereum/forks/arrow_glacier/vm/gas.py Outdated
Comment thread src/ethereum/forks/amsterdam/fork.py Outdated
Comment thread src/ethereum/forks/arrow_glacier/vm/gas.py
Comment thread src/ethereum/forks/amsterdam/vm/instructions/environment.py Outdated
Comment thread src/ethereum/forks/amsterdam/vm/instructions/environment.py Outdated
Comment thread src/ethereum/forks/amsterdam/vm/instructions/environment.py Outdated
Comment thread packages/testing/src/execution_testing/forks/gas_costs.py Outdated
Comment thread src/ethereum/forks/amsterdam/vm/gas.py Outdated
Comment thread src/ethereum/forks/amsterdam/vm/interpreter.py Outdated
Copy link
Copy Markdown
Contributor

@SamWilsn SamWilsn left a comment

Choose a reason for hiding this comment

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

That's all of it, quickly read.

Comment thread src/ethereum/forks/arrow_glacier/vm/instructions/block.py Outdated
Comment thread src/ethereum/forks/arrow_glacier/vm/gas.py Outdated
@Carsons-Eels Carsons-Eels force-pushed the intermediate_opcodes branch from 24762c1 to c612ae4 Compare April 20, 2026 11:51
@SamWilsn SamWilsn dismissed LouisTsai-Csie’s stale review April 20, 2026 16:30

Looks like the change was made.

@SamWilsn SamWilsn merged commit 5502bd8 into ethereum:forks/amsterdam Apr 20, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-spec-specs Area: Specification—The Ethereum specification itself (eg. `src/ethereum/*`) A-test-tests Area: tests for packages/testing C-refactor Category: refactor E-easy Experience: easy, good for newcomers P-high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants