Skip to content

Conversation

@marioevz
Copy link
Member

@marioevz marioevz commented Jan 10, 2026

🗒️ Description

Implements bytecode.gas_cost(fork) to calculate the exact gas cost of a bytecode for a given fork.

This is done by adding "metadata" to the opcodes that are used within bytecode.

Example:

expand_memory_code = Op.MSTORE8(
    # Expand memory first
    offset=deposited_len - 1,
    value=0,
    new_memory_size=deposited_len,  # For gas accounting
)
sstore_code = Op.SSTORE(0, 1, key_warm=True)
return_code = Op.RETURN(
    offset=0,
    size=deposited_len,
    code_deposit_size=deposited_len,  # For gas accounting
)
initcode = expand_memory_code + sstore_code + return_code
initcode.gas_cost(fork)  # Returns the total gas cost

In this example Op.MSTORE8 now contains new_memory_size metadata to include the memory expansion calculation when bytecode.gas_cost operates.

Also Op.RETURN includes a code_deposit_size to signal that this opcode is supposed to operate within initcode and returns bytecode that creates a contract, because otherwise the cost would be zero.

Updated tests

Includes updates to the following tests:

  • tests/berlin/eip2929_gas_cost_increases/test_call.py
  • tests/cancun/eip5656_mcopy/test_mcopy_memory_expansion.py
  • tests/frontier/create/test_create_deposit_oog.py
  • tests/frontier/opcodes/test_all_opcodes.py
  • tests/frontier/opcodes/test_exp.py
  • tests/frontier/opcodes/test_log.py
  • tests/osaka/eip7939_count_leading_zeros/test_count_leading_zeros.py

These were ported in order to debug and validate the gas calculations in the bytecodes included in them.

Bytecode retains list of opcodes

The bytecode instances now include a list of all opcodes they contain.

It was done by creating an Opcode prototype to avoid circular dependencies.

This list is used to calculate the gas cost of each opcode.

fork.opcode_gas_calculator

The fork base type now contains a method that returns a calculator which returns the gas cost of a given opcode, which inspects the opcode object "metadata" to be able to determine dynamic gas calculation.

🔗 Related Issues or PRs

N/A.

✅ 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 adding an entry to CHANGELOG.md.
  • 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-->

@codecov
Copy link

codecov bot commented Jan 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.33%. Comparing base (2c83b84) to head (31316fe).
⚠️ Report is 1 commits behind head on forks/amsterdam.

Additional details and impacted files
@@               Coverage Diff                @@
##           forks/amsterdam    #2002   +/-   ##
================================================
  Coverage            86.33%   86.33%           
================================================
  Files                  538      538           
  Lines                34557    34557           
  Branches              3222     3222           
================================================
  Hits                 29835    29835           
  Misses                4148     4148           
  Partials               574      574           
Flag Coverage Δ
unittests 86.33% <ø> (ø)

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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@marioevz marioevz marked this pull request as ready for review January 11, 2026 00:42
@LouisTsai-Csie LouisTsai-Csie self-requested a review January 12, 2026 06:40
@fselmo fselmo self-requested a review January 12, 2026 21:24
@LouisTsai-Csie
Copy link
Collaborator

Awesome! I’ve partially reviewed the PR, and the framework changes look good so far. I still need to take a closer look at the per-opcode gas cost calculations and the updated opcode tests.

I’ve also spent some time thinking about how to continue with our original plan for gas repricing and integrate it into this feature.

@fselmo
Copy link
Contributor

fselmo commented Jan 13, 2026

Note, we decided not to go with an implementation that uses the exact gas returned from the specs / EVM trace. I will leave this commit hash diff here so we have a reference to it in case it is ever useful in the future: 858336e...fselmo:execution-specs:7770f14daa131b25450a67059f055978f1fe3e3d.

@marioevz marioevz force-pushed the forks-op-gas-cost branch 2 times, most recently from 5baf0cb to d07b076 Compare January 14, 2026 00:55
@marioevz
Copy link
Member Author

@fselmo I think I've applied all comments plus other fixes, thanks for the review!

Copy link
Contributor

@fselmo fselmo left a comment

Choose a reason for hiding this comment

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

Can't wait to use this 😄. Great changes here.

I left some very minor remaining thoughts to think about. As I scrolled through forks.py I can't help but feel there's a better way to organize things rather than overloading forks indefinitely. I'm good with the state of this PR so I'm going to approve it in order to not block... but I do wonder if starting a mixin design pattern here wouldn't help us trend in the right direction. I also left a nit on the "Prototype" naming you can feel free to take or leave 😃.



class BaseFork(ABC, metaclass=BaseForkMeta):
class BaseFork(ForkPrototype, metaclass=BaseForkMeta):
Copy link
Contributor

@fselmo fselmo Jan 15, 2026

Choose a reason for hiding this comment

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

Hmm my only thought left in this PR is that Frontier (first fork impl) is getting really busy. And that's not going anywhere. I was trying to brainstorm some ways where we could abstract not just abstract classes out that Frontier then implements... but what if we refactored some code out of Frontier entirely into Mixin classes that it then inherits and can still override methods based on the fork?

This is a larger refactoring thought, out of scope for this PR... but I created an issue to look into this #2025.

If we do like this idea of mixins, though, we could start with this PR and name ForkPrototype -> OpcodeGasMixin - then have the actual base opcode gas implementations the gas calculator, refund calculator, gas map, etc in that class. We could have something like forks/mixins.py to abstract this out.

I think even with this mixin architecture, forks.py will still grow quite a bit as different forks require different overrides of mixin methods... but at least some of the setup can be abstracted out.

Curious on your thoughts here.


edited: BaseFork -> Frontier that's the first impl class so this is where mixins would be inherited.

@marioevz marioevz merged commit 5eb4e7b into ethereum:forks/amsterdam Jan 15, 2026
18 checks passed
jsign pushed a commit to jsign/execution-specs that referenced this pull request Jan 20, 2026
…g contract size to below allowed max (ethereum#2002)

* fixed push stack_overflow test by reducing contract size below allowed max

* use push1(0) instead of push0

* make coverage yaml more robust

* chore(ci): fix coverage when modifying existing ported tests.

* fix

---------

Co-authored-by: spencer-tb <spencer@spencertaylorbrown.uk>
@marioevz marioevz changed the title feat(testing/forks): Implement bytecode.gas_cost(fork) feat(testing/forks): Implement bytecode.gas_costs(fork) Jan 20, 2026
@marioevz marioevz changed the title feat(testing/forks): Implement bytecode.gas_costs(fork) feat(testing/forks): Implement bytecode.gas_cost(fork) Jan 20, 2026
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.

3 participants