Contribution Guidelines¶
Help is always welcome and there are plenty of options to contribute to the Ethereum Execution Layer Specifications (EELS).
In particular, we appreciate support in the following areas:
- Reporting issues
- Fixing and responding to issues, especially those tagged as E-easy which are meant as introductory issues for external contributors.
- Improving the documentation.
[!IMPORTANT] Generally, we do not assign issues to external contributors. If you want to work on an issue, you are very welcome to go ahead and make a pull request. We would, however, be happy to answer questions you may have before you start implementing.
For details about EELS usage and building, please refer to the README
Contribution Guidelines¶
This specification aims to be:
- Correct - Describe the intended behavior of the Ethereum blockchain, and any deviation from that is a bug.
- Complete - Capture the entirety of consensus critical parts of Ethereum.
- Accessible - Prioritize readability, clarity, and plain language over performance and brevity.
Style¶
Spelling and Naming¶
- Attempt to use descriptive English words (or very common abbreviations) in documentation and identifiers.
- Avoid using EIP numbers in identifiers, and prefer descriptive text instead (eg.
FeeMarketTransactioninstead ofEip1559Transaction). - If necessary, there is a custom dictionary
whitelist.txt. - Avoid uninformative prefixes in identifiers (like
get_orcompute_). They don't add useful meaning and take up valuable real estate.
Comments¶
- Don't repeat what is obvious from the code.
-
(expand) Consider how future changes will interleave with yours, especially when creating semantic blocks.
Consider:Fork T Fork T+1 # EIP-1234: The dingus is the rate of fleep dingus = a + b dingus += c ^ d dingus /= fleep(e)# EIP-1234: The dingus is the rate of fleep dingus = a + b # EIP-4567: Frobulate the dingus dingus = frobulate(dingus) dingus += c ^ d # <- dingus /= fleep(e) # <-The marked lines (
<-) are now incorrectly attributed to EIP-4567 in Fork+1. Instead, omit the EIP identifier in the comments, and describe the changes introduced by the EIP in the function's docstrings. The rendered diffs will make it pretty obvious what's changed.
Docstrings¶
- Write in complete sentences, providing necessary background and context for the associated code.
- Function and method docstrings must use the imperative mood in the summary line.
- Good: Build the house using the provided lumber.
- Bad: Builds the house using the provided lumber.
-
Always start with a single-line summary. When more detail is needed, use a multi-line docstring with a blank line after the summary line.
- One-line summary:
"""Return the pathname of the KOS root directory."""- Multi-line:
""" Add a bloom entry to the bloom filter. The number of hash functions used is 3. They are calculated by taking the least significant 11 bits from the first 3 16-bit words of the `keccak_256()` hash of `bloom_entry`. """ -
Format using markdown.
- Links to relevant standards and EIPs may be specified using reference-style links.
"""
Minimum gas cost per byte of calldata as per [EIP-7976].
[EIP-7976]: https://eips.ethereum.org/EIPS/eip-7976
"""
- Avoid beginning docstrings with an article ("the"/"a") or a pronoun ("it", "they", etc.).
- Don't include the function's signature.
Constants¶
- Do not include constant values in docstrings, neither as literals nor as expressions. It's too easy to change a constant's value and forget to update its docstring.
- Construct the constant's value from other constants or meaningful expressions in order to provide meaningful context.
- Great:
TARGET_BLOB_GAS_PER_BLOCK = GAS_PER_BLOB * BLOB_SCHEDULE_TARGET- Composed from named constants; the reader immediately understands what the value represents.
- Acceptable:
TX_MAX_GAS = Uint(2 ** 24)- More readable than a raw number, but still a literal expression that doesn't convey why this value was chosen.
- Bad:
TX_MAX_GAS = Uint(16_777_216)- A magic number with no context.
- Great:
Changes across various Forks¶
Many contributions require changes across multiple forks, organized under src/ethereum/forks/*. When making such changes, please ensure that differences between the forks are minimal and consist only of necessary differences. This will help with getting cleaner diff outputs.
When creating pull requests affecting multiple forks, we recommended submitting your PR in two steps:
- Apply the changes on a single fork, open a draft pull request, and get feedback; then
- Apply the changes across the other forks, push them, and mark the pull request as ready for review.
This saves you having to apply code review feedback repeatedly for each fork.
Development¶
Running the tests necessary to merge into the repository requires:
uvpackage manager,justcommand runner (install via your package manager,uv tool install just-bin, or pre-built binaries),- Python 3.11.x,
- PyPy 7.3.19 or later.
gethinstalled and present in$PATH.
execution-specs depends on a submodule that contains common tests that are run across all clients, so we need to clone the repo with the --recursive flag. Example:
git clone --recursive https://github.com/ethereum/execution-specs.git
Or, if you've already cloned the repository, you can fetch the submodules with:
git submodule update --init --recursive
The static checks can be run with:
just static
Individual checks and auto-fix are also available as recipes:
just fix # Auto-fix formatting and lint issues.
just lint # Lint with ruff.
just format-check # Check formatting with ruff.
just typecheck # Run type checking with mypy.
just spellcheck # Check spelling with codespell.
Run just to see all available recipes. Recipes that accept arguments can be passed extra flags, for example:
just typecheck --warn-unreachable
[!TIP] If you use multiple git worktrees, set
MYPY_CACHE_DIRto share a single mypy cache across all of them and avoid cold-start delays:export MYPY_CACHE_DIR=~/path/to/execution-specs/.mypy_cache
Shell Auto-Completion¶
just provides tab-completion for recipe names and arguments. To enable it for your shell:
Bash — add to ~/.bashrc:
eval "$(just --completions bash)"
Zsh — add to ~/.zshrc:
eval "$(just --completions zsh)"
Fish — run once (fish auto-loads completions from this directory):
just --completions fish > ~/.config/fish/completions/just.fish
After restarting your shell (or sourcing the config), just <Tab> will complete recipe names.
A trace of the EVM execution for any test case can be obtained by providing the --evm-trace argument to pytest.
Note: Make sure to run the EVM trace on a small number of tests at a time. The log might otherwise get very big.
Below is an example.
uv run pytest \
'tests/json_loader/test_state_tests.py::test_state_tests_frontier[stAttackTest - ContractCreationSpam - 0]' \
--evm_trace
CLI Utilities ethereum_spec_tools¶
The EELS repository has various CLI utilities that can help in the development process.
New Fork Tool¶
This tool can be used to create the base code for a new fork by using the existing code from a given fork.
The command takes 4 arguments, 2 of which are optional
- from_fork: The fork name from which the code is to be duplicated. Eg. - "Tangerine Whistle"
- to_fork: The fork name of the new fork Eg - "Spurious Dragon"
- from_test (Optional): Name of the from fork within the test fixtures in case it is different from fork name. Eg. - "EIP150"
- to_test (Optional): Name of the to fork within the test fixtures in case it is different from fork name Eg - "EIP158"
As an example, if one wants to create baseline code for the Spurious Dragon fork from the Tangerine Whistle one
uv run ethereum-spec-new-fork --from_fork="Tangerine Whistle" --to_fork="Spurious Dragon" --from_test=EIP150 --to_test=EIP158
The following will have to however, be updated manually
- The fork number and
MAINNET_FORK_BLOCKin__init__.py. If you are proposing a new EIP, please setMAINNET_FORK_BLOCKtoNone. - Any absolute package imports from other forks eg. in
trie.py - Package names under
setup.cfg - Add the new fork to the
monkey_patch()function insrc/ethereum_optimized/__init__.py - Adjust the underline in
fork/__init__.py
Sync Tool¶
The sync tool allows one to use an RPC provider to fetch and validate blocks against EELS. The state can also be stored in a local DB after validation. Since syncing directly with the specs can be very slow, one can also leverage the optimized module. This contains alternative implementations of routines in EELS that have been optimized for speed rather than clarity/readability.
The tool can be called using the ethereum-spec-sync command which takes the following arguments
- rpc-url: Endpoint providing the Ethereum RPC API. Defaults to
http://localhost:8545/ - unoptimized: Don't use the optimized state/ethash (this can be extremely slow)
- persist: Store the state in a db in this file
- geth: Use geth specific RPC endpoints while fetching blocks
- reset: Delete the db and start from scratch
- gas-per-commit: Commit to db each time this much gas is consumed. Defaults to 1_000_000_000
- initial-state: Start from the state in this db, rather than genesis
-
stop-at: After syncing this block, exit successfully
-
The following options are not supported WITH
--unoptimized->--persist,--initial-state,--reset - The following options are not supported WITHOUT
--persist->--initial_state,--reset
Patch Tool¶
This tool can be used to apply the unstaged changes in SOURCE_FORK to each of the TARGET_FORKS. If some
of the change didn't apply, '.rej' files listing the unapplied changes will be left in the TARGET_FORK.
The tool takes the following command line arguments
- The fork name where the changes have been made. Eg:-
frontier(only a single fork name) - The fork names where the changes have to be applied. Eg:-
homestead(multiple values can be provided separated by space) - optimized: Patch the optimized code instead
- tests: Patch the tests instead
As an example, if one wants to apply changes made in Frontier fork to Homestead and Tangerine Whistle
uv run python src/ethereum_spec_tools/patch_tool.py frontier homestead tangerine_whistle
Lint Tool¶
This tool checks for style and formatting issues specific to EELS and emits diagnostics when issues are found
The tool currently performs the following checks
- The order of the identifiers between each hardfork is consistent.
- Import statements follow the relevant import rules in modules.
The command to run the tool is just lint-spec (or uv run ethereum-spec-lint).