Skip to content

feat(benchmarks): add runtime delegated EIP-7702 benchmarks#2598

Draft
jochem-brouwer wants to merge 2 commits intoethereum:forks/amsterdamfrom
jochem-brouwer:stateful-7702-tests
Draft

feat(benchmarks): add runtime delegated EIP-7702 benchmarks#2598
jochem-brouwer wants to merge 2 commits intoethereum:forks/amsterdamfrom
jochem-brouwer:stateful-7702-tests

Conversation

@jochem-brouwer
Copy link
Copy Markdown
Member

🗒️ Description

This adds benchmarks plus a template for future use. On perf-devnet-3, there are (so far) three EOAs being targeted with their bytecode being delegated to:

0x5f541515600b5760015f555b5f54805b818155600101906001019061ffff5a11600f575f55

This (22866e0):

This is example code to delegate to (runtime code):
0x5f541515600b5760015f555b5f54805b818155600101906001019061ffff5a11600f575f55
This works even on empty accounts: if the first slot is 0, set it to 1,
then: initialize stack to SLOAD(0) and proceed to write stack key as value
to that slot. It will keep looping until the gasleft is lower than or equal
to 0xffff (65535), i.e. currently enough to write to an empty slot (about
22100 gas as of Fusaka)
The cleanup phase at the end of the loop writes next key to slot 0.
Note that this key is either already dirty (initial call on empty storage)
or is already set. So the max gas cost of
cleanup is 5000 + 2100 as of Fusaka.

We now use the fact that we have pkey of these accounts to overwrite runtime bytecode via EIP-7702 to target these for our needs. The bloatnet/perf-devnet-3 accounts are thus bloated with a range from 1-N (1/10/20Gb currently) which we can now target for storage related benchmarks.
So far this PR accounts for the NO-CACHE variant with existing/non-existing keys in the case we run the setup bytecode as provided and then start the attack block.

To add:

  • The SLOAD variant which does not do SSTORE, only SLOAD
  • The CACHE_TX variant which does SLOAD before either SSTORE/SLOAD of the target
  • The CACHE_PREVIOUS_BLOCK variant which does ONLY SLOADs before the attack block. This also resets the start slot to the same key it started before doing the cache initialization.

Draft, but want to finish this ASAP as we are super interested in the benchmark results.

🔗 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 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
Copy Markdown

codecov bot commented Mar 30, 2026

Codecov Report

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

Additional details and impacted files
@@               Coverage Diff                @@
##           forks/amsterdam    #2598   +/-   ##
================================================
  Coverage            86.24%   86.24%           
================================================
  Files                  599      599           
  Lines                36984    36984           
  Branches              3795     3795           
================================================
  Hits                 31895    31895           
  Misses                4525     4525           
  Partials               564      564           
Flag Coverage Δ
unittests 86.24% <ø> (ø)

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.

@misilva73
Copy link
Copy Markdown

I am wondering if we could merge this with only the NO_CACHE variant for SLOAD and SSTORE to see results faster. What do you think, @jochem-brouwer ? Will this save us significant time?

@jochem-brouwer jochem-brouwer marked this pull request as ready for review March 31, 2026 15:55
@jochem-brouwer
Copy link
Copy Markdown
Member Author

No cache variants added, open for review! I checked locally against a client and it looks fine.

Copy link
Copy Markdown

@misilva73 misilva73 left a comment

Choose a reason for hiding this comment

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

After a first-level look at the code, I cannot find any issues. However, I do have two comments:

  1. A big part of the code in test_sload_bloated and test_sstore_bloated is duplicated (Same setup, same gas-loop to build transactions and same benchmark_test() call). I wonder is logic should be moved to a helper function to help with code maintainability. This is a nitpick and should definitely not block merging this.
  2. The EIP-7702 delegation trick is quire clever 👏 However, I worry it could lead to different measurements. From a specification perspective, it shouldn't - every SLOAD/SSTORE should execute against the EOA's storage trie exactly the same way it would for a normal contract. The trie structure and lookup path should be identical regardless of how the code got there. However, individual client implementations could have different code paths or caching behavior for delegated accounts vs. native contracts. Can we double-check with clients that this is not the case?

@jochem-brouwer
Copy link
Copy Markdown
Member Author

@misilva73, (2) this is a good point and also a good exercise, I will do this today. I would be surprised if the code paths diverge here, I assume that most clients have simply swapped out the runtime code for a EIP-7702 delegated account and keep the account code (the 0xef0100..) (where EXTCODECOPY refers to). This is a good sanity check. I will check with the 5 clients to see if there is a mechanism which might cause problems for this approach.

Regarding the duplication (1): you are right, I should refactor this.

@jochem-brouwer
Copy link
Copy Markdown
Member Author

Converted to draft, figuring out what goes wrong here.

I have a local instance of Nethermind running to test, this is the error:

Encountered exception Nethermind.Blockchain.InvalidTransactionException: Transaction 0xe0e9193fca0011926f1ae82d53b70698ee28c9fbc5cc57cae035dec3a776c39a at index 0 failed with error transaction nonce is too high

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