diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a52caf7ea9..d67f9a8f70 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,13 +13,12 @@ N/A. ```console uvx --with=tox-uv tox -e lint,typecheck,spellcheck,markdownlint ``` -- [ ] All: PR title adheres to the [repo standard](https://eest.ethereum.org/main/getting_started/contributing/?h=contri#commit-messages-issue-and-pr-titles) - it will be used as the squash commit message and should start `type(scope):`. -- [ ] All: Considered adding an entry to [CHANGELOG.md](/ethereum/execution-spec-tests/blob/main/docs/CHANGELOG.md). -- [ ] All: Considered updating the online docs in the [./docs/](/ethereum/execution-spec-tests/blob/main/docs/) directory. +- [ ] All: Considered adding an entry to [CHANGELOG.md](/ethereum/execution-specs/blob/main/docs/CHANGELOG.md). +- [ ] All: Considered updating the online docs in the [./docs/](/ethereum/execution-specs/blob/main/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](https://eest.ethereum.org/main/tests/) are correctly formatted. -- [ ] Tests: For PRs implementing a missed test case, update the [post-mortem document](/ethereum/execution-spec-tests/blob/main/docs/writing_tests/post_mortems.md) to add an entry the list. -- [ ] Ported Tests: All converted JSON/YML tests from [ethereum/tests](/ethereum/tests) or [tests/static](/ethereum/execution-spec-tests/blob/main/tests/static) have been assigned `@ported_from` marker. +- [ ] Tests: For PRs implementing a missed test case, update the [post-mortem document](/ethereum/execution-specs/blob/main/docs/writing_tests/post_mortems.md) to add an entry the list. +- [ ] Ported Tests: All converted JSON/YML tests from [ethereum/tests](/ethereum/tests) or [tests/eest/static](/ethereum/execution-specs/blob/main/tests/eest/static) have been assigned `@ported_from` marker. #### Cute Animal Picture diff --git a/.github/actions/eest/build-evm-base/action.yaml b/.github/actions/eest/build-evm-base/action.yaml new file mode 100644 index 0000000000..06dda0d8db --- /dev/null +++ b/.github/actions/eest/build-evm-base/action.yaml @@ -0,0 +1,76 @@ +name: 'Build EVM' +description: 'Resolves and builds the requested EVM binary by name' +inputs: + type: + description: 'Type of EVM binary to build' + required: true + default: 'main' +outputs: + impl: + description: "Implementation of EVM binary to build" + value: ${{ steps.config-evm-reader.outputs.impl }} + repo: + description: "Repository to use to build the EVM binary" + value: ${{ steps.config-evm-reader.outputs.repo }} + ref: + description: "Reference to branch, commit, or tag to use to build the EVM binary" + value: ${{ steps.config-evm-reader.outputs.ref }} + evm-bin: + description: "Binary name of the evm tool to use" + value: ${{ steps.config-evm-impl-config-reader.outputs.evm-bin }} + x-dist: + description: "Binary name of the evm tool to use" + value: ${{ steps.config-evm-impl-config-reader.outputs.x-dist }} +runs: + using: "composite" + steps: + - name: Get the selected EVM version from the .github/configs/evm.yaml + id: config-evm-reader + shell: bash + run: | + awk "/^${{ inputs.type }}:/{flag=1; next} /^[[:alnum:]]/{flag=0} flag" ./.github/configs/evm.yaml \ + | sed 's/ //g' | sed 's/:/=/g' >> "$GITHUB_OUTPUT" + - name: Get the EVM implementation configuration from .github/configs/evm-impl-config.yaml + id: config-evm-impl-config-reader + shell: bash + run: | + awk "/^${{ steps.config-evm-reader.outputs.impl }}:/{flag=1; next} /^[[:alnum:]]/{flag=0} flag" ./.github/configs/evm-impl.yaml \ + | sed 's/ //g' | sed 's/:/=/g' >> "$GITHUB_OUTPUT" + - name: Print Variables for the selected EVM type + shell: bash + run: | + echo "Implementation: ${{ steps.config-evm-reader.outputs.impl }}" + echo "Repository: ${{ steps.config-evm-reader.outputs.repo }}" + echo "Reference: ${{ steps.config-evm-reader.outputs.ref }}" + echo "EVM Binary: ${{ steps.config-evm-impl-config-reader.outputs.evm-bin }}" + echo "X-Dist parameter: ${{ steps.config-evm-impl-config-reader.outputs.x-dist }}" + - name: Skip building for EELS + if: steps.config-evm-reader.outputs.impl == 'eels' + shell: bash + run: echo "Skipping build for EELS" + - name: Build the EVM using Geth action + if: steps.config-evm-reader.outputs.impl == 'geth' + uses: ./.github/actions/eest/build-evm-client/geth + with: + repo: ${{ steps.config-evm-reader.outputs.repo }} + ref: ${{ steps.config-evm-reader.outputs.ref }} + - name: Build the EVM using EVMONE action + if: steps.config-evm-reader.outputs.impl == 'evmone' + uses: ./.github/actions/eest/build-evm-client/evmone + with: + repo: ${{ steps.config-evm-reader.outputs.repo }} + ref: ${{ steps.config-evm-reader.outputs.ref }} + # `targets` in the evm.yaml must be an inline array to not interfere with `config-evm-reader`'s parsing + targets: ${{ join(fromJSON(steps.config-evm-reader.outputs.targets), ' ') }} + - name: Build the EVM using Besu action + if: steps.config-evm-reader.outputs.impl == 'besu' + uses: ./.github/actions/eest/build-evm-client/besu + with: + repo: ${{ steps.config-evm-reader.outputs.repo }} + ref: ${{ steps.config-evm-reader.outputs.ref }} + - name: Build the EVM using EthJS action + if: steps.config-evm-reader.outputs.impl == 'ethjs' + uses: ./.github/actions/eest/build-evm-client/ethjs + with: + repo: ${{ steps.config-evm-reader.outputs.repo }} + ref: ${{ steps.config-evm-reader.outputs.ref }} \ No newline at end of file diff --git a/.github/actions/eest/build-evm-client/besu/action.yaml b/.github/actions/eest/build-evm-client/besu/action.yaml new file mode 100644 index 0000000000..1c8ca0726f --- /dev/null +++ b/.github/actions/eest/build-evm-client/besu/action.yaml @@ -0,0 +1,40 @@ +name: 'Build Besu EVM' +description: 'Builds the Besu EVM binary' +inputs: + repo: + description: 'Source repository to use to build the EVM binary' + required: true + default: 'hyperledger/besu' + ref: + description: 'Reference to branch, commit, or tag to use to build the EVM binary' + required: true + default: 'main' + java: + description: 'Java version to use to build Besu' + required: false + default: '21' + java-distro: + description: 'Java distribution to use to build Besu' + required: false + default: 'temurin' +runs: + using: "composite" + steps: + - name: Checkout Besu + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + repository: ${{ inputs.repo }} + ref: ${{ inputs.ref }} + path: besu + - name: Setup Java + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + with: + java-version: ${{ inputs.java }} + distribution: ${{ inputs.java-distro }} + cache: 'gradle' + - name: Build evm cmd + shell: bash + run: | + cd $GITHUB_WORKSPACE/besu + ./gradlew installDist + echo $GITHUB_WORKSPACE/besu/build/install/besu/bin/ >> $GITHUB_PATH \ No newline at end of file diff --git a/.github/actions/eest/build-evm-client/ethjs/action.yml b/.github/actions/eest/build-evm-client/ethjs/action.yml new file mode 100644 index 0000000000..b359e30631 --- /dev/null +++ b/.github/actions/eest/build-evm-client/ethjs/action.yml @@ -0,0 +1,37 @@ +name: 'Build EthereumJS monorepo' +description: 'Builds the EthereumJS monorepo' +inputs: + repo: + description: 'Source repository to use to build EthereumJS' + required: true + default: 'ethereumjs/ethereumjs-monorepo' + ref: + description: 'Reference to branch, commit, or tag to use to build EthereumJS' + required: true + default: 'master' +runs: + using: "composite" + steps: + - name: Checkout EthereumJS monorepo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + repository: ${{ inputs.repo }} + ref: ${{ inputs.ref }} + path: ethereumjs + + - name: Setup node + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version: 18 + + - name: Build monorepo + shell: bash + run: | + cd $GITHUB_WORKSPACE/ethereumjs + npm ci + + - name: Add t8ntool to $PATH + shell: bash + run: | + echo $GITHUB_WORKSPACE/ethereumjs/packages/vm/test/t8n/ >> $GITHUB_PATH + echo $GITHUB_WORKSPACE/ethereumjs/node_modules/.bin >> $GITHUB_PATH \ No newline at end of file diff --git a/.github/actions/eest/build-evm-client/evmone/action.yaml b/.github/actions/eest/build-evm-client/evmone/action.yaml new file mode 100644 index 0000000000..ab642511df --- /dev/null +++ b/.github/actions/eest/build-evm-client/evmone/action.yaml @@ -0,0 +1,46 @@ +name: 'Build evmone EVM' +description: 'Builds the evmone EVM binary' +inputs: + repo: + description: 'Source repository to use to build the EVM binary' + required: true + default: 'ethereum/evmone' + ref: + description: 'Reference to branch, commit, or tag to use to build the EVM binary' + required: true + default: 'master' + targets: + description: 'Which targets to build from evmone repo' + required: false + default: 'all' +runs: + using: "composite" + steps: + - name: Checkout evmone + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + repository: ${{ inputs.repo }} + ref: ${{ inputs.ref }} + path: evmone + submodules: true + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@802fa1a2c4e212495c05bf94dba2704a92a472be + with: + cmake-version: '3.x' + - name: "Install GMP Linux" + if: runner.os == 'Linux' + shell: bash + run: sudo apt-get -q update && sudo apt-get -qy install libgmp-dev + - name: Install GMP macOS + if: runner.os == 'macOS' + shell: bash + run: | + brew update && brew install gmp + - name: Build evmone binary + shell: bash + run: | + mkdir -p $GITHUB_WORKSPACE/bin + cd $GITHUB_WORKSPACE/evmone + cmake -S . -B build -DEVMONE_TESTING=ON -DEVMONE_PRECOMPILES_SILKPRE=1 + cmake --build build --parallel --target ${{ inputs.targets }} + echo $GITHUB_WORKSPACE/evmone/build/bin/ >> $GITHUB_PATH diff --git a/.github/actions/eest/build-evm-client/geth/action.yaml b/.github/actions/eest/build-evm-client/geth/action.yaml new file mode 100644 index 0000000000..2cdb333926 --- /dev/null +++ b/.github/actions/eest/build-evm-client/geth/action.yaml @@ -0,0 +1,36 @@ +name: 'Build Go-Ethereum EVM' +description: 'Builds the Go-Ethereum EVM binary' +inputs: + repo: + description: 'Source repository to use to build the EVM binary' + required: true + default: 'ethereum/go-ethereum' + ref: + description: 'Reference to branch, commit, or tag to use to build the EVM binary' + required: true + default: 'master' + golang: + description: 'Golang version to use to build Geth' + required: false + default: '1.21.x' +runs: + using: "composite" + steps: + - name: Checkout go-ethereum + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + repository: ${{ inputs.repo }} + ref: ${{ inputs.ref }} + path: go-ethereum + - name: Setup golang + uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b + with: + go-version: ${{ inputs.golang }} + cache-dependency-path: go-ethereum/go.sum + - name: Build evm cmd + shell: bash + run: | + mkdir -p $GITHUB_WORKSPACE/bin + cd $GITHUB_WORKSPACE/go-ethereum/cmd/evm + go build . + echo $GITHUB_WORKSPACE/go-ethereum/cmd/evm >> $GITHUB_PATH \ No newline at end of file diff --git a/.github/actions/eest/build-fixtures/action.yaml b/.github/actions/eest/build-fixtures/action.yaml new file mode 100644 index 0000000000..f6d094dfa3 --- /dev/null +++ b/.github/actions/eest/build-fixtures/action.yaml @@ -0,0 +1,59 @@ +name: Build and Package Fixture Release +inputs: + release_name: + description: "Name of the fixture release" + required: true + uv_version: + description: "Version of UV to install" + required: true + python_version: + description: "Version of Python to install" + required: true +runs: + using: "composite" + steps: + - name: Install uv ${{ inputs.uv_version }} and python ${{ inputs.python_version }} + uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + with: + enable-cache: false + version: ${{ inputs.uv_version }} + python-version: ${{ inputs.python_version }} + - name: Install EEST + shell: bash + run: uv sync --no-progress + - name: Extract fixture release properties from config + id: properties + shell: bash + run: | + echo "release_name=${{ inputs.release_name }}" + uv run -q .github/scripts/get_release_props.py ${{ inputs.release_name }} >> "$GITHUB_OUTPUT" + - uses: ./.github/actions/eest/build-evm-base + id: evm-builder + with: + type: ${{ steps.properties.outputs.evm-type }} + - name: Generate fixtures using fill + shell: bash + run: | + uv run fill -n ${{ steps.evm-builder.outputs.x-dist }} --evm-bin=${{ steps.evm-builder.outputs.evm-bin }} ${{ steps.properties.outputs.fill-params }} --output=fixtures_${{ inputs.release_name }}.tar.gz --build-name ${{ inputs.release_name }} + - name: Wrap ethereum/tests fixtures with eofwrap tool + shell: bash + if: ${{ steps.properties.outputs.eofwrap }} + run: | + curl -L ${tests_url}${tests_version}.tar.gz | tar -xz + ls -l + uv run eofwrap tests-${tests_version}/BlockchainTests/GeneralStateTests/ fixtures/${output_path} + rm -rf fixtures_${{ inputs.release_name }} + mkdir -p ./fixtures/.meta + mv fixtures/${output_path}/metrics.json ./fixtures/.meta/eofwrap_metrics.json + gunzip fixtures_${{ inputs.release_name }}.tar.gz + tar rf fixtures_${{ inputs.release_name }}.tar fixtures + gzip fixtures_${{ inputs.release_name }}.tar + rm -rf fixtures + env: + tests_url: https://github.com/ethereum/tests/archive/refs/tags/v + tests_version: 14.1 + output_path: blockchain_tests/unscheduled/eofwrap + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: fixtures_${{ inputs.release_name }} + path: fixtures_${{ inputs.release_name }}.tar.gz diff --git a/.github/actions/eest/fetch-binary/action.yaml b/.github/actions/eest/fetch-binary/action.yaml new file mode 100644 index 0000000000..b69b452c4a --- /dev/null +++ b/.github/actions/eest/fetch-binary/action.yaml @@ -0,0 +1,31 @@ +name: "Fetch and Install Binary" +description: "Downloads a binary from a GitHub release, verifies its checksum, and installs it to PATH. Only works for Linux." +inputs: + version: + description: "Release version (e.g., v0.8.24)" + required: true + repo_owner: + description: "GitHub repository owner" + required: true + repo_name: + description: "GitHub repository name" + required: true + remote_name: + description: "Binary filename in the release" + required: true + binary_name: + description: "Name to install the binary as" + required: true + expected_sha256: + description: "Expected SHA256 checksum" + required: true +runs: + using: "composite" + steps: + - name: Download binary + shell: bash + run: | + curl -sSL "https://github.com/${{ inputs.repo_owner }}/${{ inputs.repo_name }}/releases/download/${{ inputs.version }}/${{ inputs.remote_name }}" -o ./${{ inputs.binary_name }} + echo "${{ inputs.expected_sha256 }} ${{ inputs.binary_name }}" | sha256sum -c - + sudo mv ./${{ inputs.binary_name }} /usr/local/bin/${{ inputs.binary_name }} + sudo chmod +x /usr/local/bin/${{ inputs.binary_name }} \ No newline at end of file diff --git a/.github/configs/evm-impl.yaml b/.github/configs/evm-impl.yaml new file mode 100644 index 0000000000..bd33536671 --- /dev/null +++ b/.github/configs/evm-impl.yaml @@ -0,0 +1,15 @@ +eels: + evm-bin: ethereum-spec-evm-resolver + x-dist: auto +geth: + evm-bin: evm + x-dist: auto +evmone: + evm-bin: evmone-t8n + x-dist: auto +besu: + evm-bin: evmtool + x-dist: 0 +ethjs: + evm-bin: ethereumjs-t8ntool.sh + x-dist: auto diff --git a/.github/configs/evm.yaml b/.github/configs/evm.yaml new file mode 100644 index 0000000000..1018923af6 --- /dev/null +++ b/.github/configs/evm.yaml @@ -0,0 +1,18 @@ +stable: + impl: eels + repo: null + ref: null +develop: + impl: eels + repo: null + ref: null +static: + impl: evmone + repo: ethereum/evmone + ref: master + targets: ["evmone-t8n"] +benchmark: + impl: evmone + repo: ethereum/evmone + ref: master + targets: ["evmone-t8n"] \ No newline at end of file diff --git a/.github/configs/feature.yaml b/.github/configs/feature.yaml new file mode 100644 index 0000000000..964883b837 --- /dev/null +++ b/.github/configs/feature.yaml @@ -0,0 +1,22 @@ +# Unless filling for special features, all features should fill for previous forks (starting from Frontier) too +stable: + evm-type: stable + fill-params: --until=Prague --fill-static-tests --ignore=tests/eest/static/state_tests/stQuadraticComplexityTest + +develop: + evm-type: develop + fill-params: --until=BPO4 --fill-static-tests --ignore=tests/eest/static/state_tests/stQuadraticComplexityTest + +benchmark: + evm-type: benchmark + fill-params: --fork=Prague --gas-benchmark-values 1,10,30,45,60,100,150 -m "benchmark and not state_test" ./tests/eest/benchmark + +benchmark_develop: + evm-type: develop + fill-params: --fork=Osaka --gas-benchmark-values 1,10,30,45,60,100,150 -m "benchmark and not state_test" ./tests/eest/benchmark + feature_only: true + +bal: + evm-type: develop + fill-params: --fork=Amsterdam ./tests/eest/amsterdam/eip7928_block_level_access_lists + feature_only: true \ No newline at end of file diff --git a/.github/scripts/fill_introduced_tests.sh b/.github/scripts/fill_introduced_tests.sh new file mode 100755 index 0000000000..eb8882acf2 --- /dev/null +++ b/.github/scripts/fill_introduced_tests.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Fill introduced test sources +# Usage: fill_introduced_tests.sh +# Exit codes: +# 0 - Success +# 1 - Failure to generate tests or no tests found + +set -e + +CHANGED_TEST_FILES="$1" +PATCH_TEST_PATH="$2" +BLOCK_GAS_LIMIT="${3:-45000000}" +FILL_UNTIL="${4:-Cancun}" + +# Include basic evm operations into coverage by default +# As when we translate from yul/solidity some dup/push opcodes could become untouched +files="$CHANGED_TEST_FILES tests/eest/homestead/coverage/test_coverage.py" + +uv run fill $files --clean --until=$FILL_UNTIL --evm-bin evmone-t8n --block-gas-limit $BLOCK_GAS_LIMIT -m "state_test or blockchain_test" --output $PATCH_TEST_PATH > >(tee -a filloutput.log) 2> >(tee -a filloutput.log >&2) + +if grep -q "FAILURES" filloutput.log; then + echo "Error: failed to generate .py tests." + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/.github/scripts/fill_prepatched_tests.sh b/.github/scripts/fill_prepatched_tests.sh new file mode 100755 index 0000000000..2a6ce651c8 --- /dev/null +++ b/.github/scripts/fill_prepatched_tests.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# Fill pre-patched test sources from before the PR +# Usage: fill_prepatched_tests.sh +# Exit codes: +# 0 - Success +# 1 - Failure to generate tests + +set -e + +MODIFIED_DELETED_FILES="$1" +BASE_TEST_PATH="$2" +PATCH_TEST_PATH="$3" +BLOCK_GAS_LIMIT="${4:-45000000}" +FILL_UNTIL="${5:-Cancun}" + +echo "--------------------" +echo "converted-ethereum-tests.txt seem untouched, try to fill pre-patched version of .py files:" + +PATCH_COMMIT=$(git rev-parse HEAD) +git checkout mainnet +BASE_COMMIT=$(git rev-parse HEAD) +echo "Checkout head $BASE_COMMIT" + +echo "Select files that were changed and exist on the main branch:" +echo "$MODIFIED_DELETED_FILES" + +rm -rf fixtures + +set +e +uv run fill $MODIFIED_DELETED_FILES --clean --until=$FILL_UNTIL --evm-bin evmone-t8n --block-gas-limit $BLOCK_GAS_LIMIT -m "state_test or blockchain_test" --output $BASE_TEST_PATH +FILL_RETURN_CODE=$? +set -e +if [ $FILL_RETURN_CODE -eq 5 ]; then + echo "any_modified_fixtures=false" >> "$GITHUB_OUTPUT" + exit 0 +elif [ $FILL_RETURN_CODE -ne 0 ]; then + echo "Error: failed to generate .py tests from before the PR." + exit 1 +fi + +git checkout $PATCH_COMMIT +echo "Checkout back to patch $PATCH_COMMIT" +# abort-on-empty-patch is used to ensure that the patch folder is not empty after fixture removal. +# If the patch folder would be empty, it means that fixtures were removed in the PR, in which case we still want to run the coverage check. +uv run compare_fixtures --abort-on-empty-patch $BASE_TEST_PATH $PATCH_TEST_PATH + +if [ -d $BASE_TEST_PATH ]; then + # If the base folder is not empty, it means there's at least one fixture that was modified in the PR, continue with the coverage check. + echo "Base folder is not empty after fixture comparison, continuing with coverage check." + echo "any_modified_fixtures=true" >> "$GITHUB_OUTPUT" +else + # If the base folder is empty, it means there were no fixtures that were modified in the PR, or fixtures were only added, so we can skip the coverage check. + echo "Base folder is empty after fixture comparison, skipping coverage check." + echo "any_modified_fixtures=false" >> "$GITHUB_OUTPUT" +fi +exit 0 \ No newline at end of file diff --git a/.github/scripts/generate_eip_report.py b/.github/scripts/generate_eip_report.py new file mode 100644 index 0000000000..03411ea98a --- /dev/null +++ b/.github/scripts/generate_eip_report.py @@ -0,0 +1,151 @@ +""" +Generate a markdown report of outdated EIP references from the EIP version +checker output. +""" + +import os +import re +import sys +import textwrap +from string import Template +from typing import List, Tuple + +# Report template using textwrap.dedent for clean multiline strings +REPORT_TEMPLATE = Template( + textwrap.dedent("""\ + # EIP Version Check Report + + This automated check has detected that some EIP references in test files are outdated. This means that the EIPs have been updated in the [ethereum/EIPs](https://github.com/ethereum/EIPs) repository since our tests were last updated. + + ## Outdated EIP References + + ### Summary Table + + | File | EIP Link | Referenced Version | Latest Version | + | ---- | -------- | ------------------ | -------------- | + $summary_table + + ### Verbatim Failures + + ``` + $fail_messages + ``` + + ### Verbatim Errors + + ``` + $error_messages + ``` + + ## Action Required + + 1. Please verify whether the affected tests need updating based on changes in the EIP spec. + 2. Update the `REFERENCE_SPEC_VERSION` in each file with the latest version shown above. + 3. For detailed instructions, see the [reference specification documentation](https://eest.ethereum.org/main/writing_tests/reference_specification/). + + ## Workflow Information + + For more details, see the [workflow run](https://github.com/ethereum/execution-specs/actions/runs/$run_id). +""") # noqa: E501 +) + + +def extract_failures(output: str) -> List[Tuple[str, str, str, str, str, str]]: + """Extract failure information from the output using regex.""" + failures = [] + + for line in output.split("\n"): + if not line.startswith("FAILED"): + continue + + # Extract test file path + file_match = re.search(r"FAILED (tests/[^:]+\.py)", line) + if not file_match: + continue + file_path = file_match.group(1) + + # Extract EIP number + eip_match = re.search(r"eip(\d+)", file_path, re.IGNORECASE) + eip_num = f"EIP-{eip_match.group(1)}" if eip_match else "Unknown" + + # Extract full path + full_path_match = re.search(r"from '([^']+)'", line) + full_path = full_path_match.group(1) if full_path_match else "Unknown" + + # Extract EIP link + eip_link_match = re.search(r"Spec: (https://[^ ]+)\.", line) + eip_link = eip_link_match.group(1) if eip_link_match else "" + eip_link = eip_link.replace("blob/", "commits/") if eip_link else "" + + # Extract versions + ref_version_match = re.search(r"Referenced version: ([a-f0-9]+)", line) + ref_version = ref_version_match.group(1) if ref_version_match else "Unknown" + + latest_version_match = re.search(r"Latest version: ([a-f0-9]+)", line) + latest_version = latest_version_match.group(1) if latest_version_match else "Unknown" + + failures.append((file_path, eip_num, full_path, eip_link, ref_version, latest_version)) + + return failures + + +def generate_summary_table(failures: List[Tuple[str, str, str, str, str, str]]) -> str: + """Generate a markdown summary table from the failures.""" + rows = [] + for file_path, eip_num, _, eip_link, ref_version, latest_version in failures: + rows.append( + f"| `{file_path}` | [{eip_num}]({eip_link}) | `{ref_version}` | `{latest_version}` |" + ) + return "\n".join(rows) + + +def main() -> None: + """Generate the report.""" + if len(sys.argv) < 2: + print("Usage: uv run python generate_eip_report.py [output_file]") + sys.exit(1) + + input_file = sys.argv[1] + output_file = sys.argv[2] if len(sys.argv) > 2 else "./reports/outdated_eips.md" + + try: + with open(input_file, "r") as f: + output = f.read() + except Exception as e: + print(f"Error reading input file: {e}") + sys.exit(1) + + failures = extract_failures(output) + + fail_messages = "\n".join(line for line in output.split("\n") if line.startswith("FAILED")) + if not fail_messages: + fail_messages = ( + "No test failures were found in the pytest output: No lines start with 'FAILED'." + ) + + error_messages = "\n".join(line for line in output.split("\n") if line.startswith("ERROR")) + if not error_messages: + error_messages = ( + "No test errors were found in the pytest output: No lines start with 'ERROR'." + ) + + report_content = REPORT_TEMPLATE.substitute( + summary_table=generate_summary_table(failures), + fail_messages=fail_messages, + error_messages=error_messages, + run_id=os.environ.get("GITHUB_RUN_ID", ""), + ) + + try: + with open(output_file, "w") as report: + report.write(report_content) + except Exception as e: + print(f"Error writing output file: {e}") + sys.exit(1) + + print(f"Report generated successfully: {output_file}") + print(f"Found {len(failures)} outdated EIP references") + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/get_release_props.py b/.github/scripts/get_release_props.py new file mode 100644 index 0000000000..577979c31b --- /dev/null +++ b/.github/scripts/get_release_props.py @@ -0,0 +1,31 @@ +# /// script +# requires-python = ">=3.12" +# dependencies = [ +# "pyyaml", +# "click", +# ] +# /// +"""Extract the properties of a configured EEST release from a YAML file.""" + +import sys + +import click +import yaml + +RELEASE_PROPS_FILE = "./.github/configs/feature.yaml" + + +@click.command() +@click.argument("release", required=True) +def get_release_props(release: str) -> None: + """Extract the properties from the YAML file for a given release.""" + with open(RELEASE_PROPS_FILE) as f: + data = yaml.safe_load(f) + if release not in data: + print(f"Error: Release {release} not found in {RELEASE_PROPS_FILE}.") + sys.exit(1) + print("\n".join(f"{key}={value}" for key, value in data[release].items())) + + +if __name__ == "__main__": + get_release_props() diff --git a/.github/scripts/parse_ported_tests.sh b/.github/scripts/parse_ported_tests.sh new file mode 100755 index 0000000000..7c7353b992 --- /dev/null +++ b/.github/scripts/parse_ported_tests.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# Parse ported_from markers from introduced .py tests +# Usage: parse_ported_tests.sh +# Exit codes: +# 0 - Found converted tests, continue processing +# 1 - No converted tests found, but updates detected + +set -e + +CHANGED_TEST_FILES="$1" +WORKSPACE_PATH="${2:-$GITHUB_WORKSPACE}" + +echo "Changed or new test files: $CHANGED_TEST_FILES" + +FILTERED_FILES="" +for file in $CHANGED_TEST_FILES; do + if git diff origin/mainnet -- "$file" | grep -q "^+.*@pytest.mark.ported_from"; then + FILTERED_FILES="$FILTERED_FILES $file" + fi +done + +if [[ -z "$FILTERED_FILES" ]]; then + echo "No new ported_from markers found." + echo "any_ported=false" >> "$GITHUB_OUTPUT" + exit 0 +fi + +uv run fill $FILTERED_FILES --show-ported-from --clean --quiet --links-as-filled --skip-coverage-missed-reason --ported-from-output-file ported_from_files.txt +files=$(cat ported_from_files.txt) +echo "Extracted converted tests:" +echo "$files" + +if [[ -z "$files" ]]; then + echo "No ported fillers found, check updates instead." + echo "any_ported=false" >> "$GITHUB_OUTPUT" + exit 0 +fi + +echo "any_ported=true" >> "$GITHUB_OUTPUT" + +echo "----------------" +echo "Discovered existing json tests that will be BASE files:" + +BASE_TESTS_PATH="${WORKSPACE_PATH}/evmtest_coverage/coverage/BASE_TESTS" +mkdir -p "$BASE_TESTS_PATH" + +for file in $files; do + # Make sure each file exist at least in develop or legacy tests + file_found=0 + + if [[ "$file" == *"BlockchainTests"* ]]; then + destination_path="$BASE_TESTS_PATH/blockchain_tests" + elif [[ "$file" == *"GeneralStateTests"* ]]; then + destination_path="$BASE_TESTS_PATH/state_tests" + else + echo "Error: $file is not a valid test file" + exit 1 + fi + + + # Try ethereum/tests + source_path="${WORKSPACE_PATH}/testpath/$file" + if [ -e "$source_path" ]; then + file_found=1 + mkdir -p "$destination_path" + cp "$source_path" "$destination_path" + echo "$source_path -> $destination_path" + fi + + # Try ethereum/legacytests + source_path="${WORKSPACE_PATH}/legacytestpath/Cancun/$file" + base_name=$(basename "$file") + legacy_file_name="legacy_$base_name" + if [ -e "$source_path" ]; then + file_found=1 + mkdir -p "$destination_path" + cp "$source_path" "$destination_path/$legacy_file_name" + echo "$source_path -> $destination_path" + fi + + if [ $file_found -eq 0 ]; then + echo "Error: Failed to find the test file $file in test repo" + exit 1 + fi +done + +exit 0 \ No newline at end of file diff --git a/.github/workflows/check-eip-versions.yaml b/.github/workflows/check-eip-versions.yaml new file mode 100644 index 0000000000..868ff3e66d --- /dev/null +++ b/.github/workflows/check-eip-versions.yaml @@ -0,0 +1,61 @@ +name: Check EIP Versions In EEST Tests + +on: + repository_dispatch: + workflow_dispatch: + schedule: + - cron: "00 12 * * 1" # Run weekly on Mondays at 12:00 UTC + +jobs: + check_eip_versions: + runs-on: ubuntu-latest + permissions: + issues: write # required for peter-evans/create-issue-from-file + contents: read # needed for API access to GitHub + steps: + - name: Checkout ethereum/execution-specs + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + + - name: Install uv ${{ vars.UV_VERSION }} and python ${{ vars.DEFAULT_PYTHON_VERSION }} + uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + version: ${{ vars.UV_VERSION }} + python-version: ${{ vars.DEFAULT_PYTHON_VERSION }} + + - name: Run EIP Version Checker + id: check-eip + continue-on-error: true + env: + # GitHub token provides API access for EIP version checking + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + mkdir -p ./reports + uv run check_eip_versions 2>&1 | tee ./reports/eip_check_output.txt + # Save the exit code but don't fail the workflow + exit_code=${PIPESTATUS[0]} + echo "exit_code=$exit_code" >> $GITHUB_OUTPUT + # Always return success to GitHub Actions + exit 0 + + - name: Generate report file + if: steps.check-eip.outputs.exit_code != 0 + run: | + uv run python .github/scripts/generate_eip_report.py ./reports/eip_check_output.txt ./reports/outdated_eips.md + + - name: Create Issue From File + if: steps.check-eip.outputs.exit_code != 0 + uses: peter-evans/create-issue-from-file@e8ef132d6df98ed982188e460ebb3b5d4ef3a9cd + with: + title: "chore(tests): eip spec references outdated" + content-filepath: ./reports/outdated_eips.md + labels: report, automated issue, scope:tests, type:chore + + - name: Upload test report as artifact + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: eip-check-report + path: ./reports/ + retention-days: 30 diff --git a/.github/workflows/check-links.yaml b/.github/workflows/check-links.yaml new file mode 100644 index 0000000000..b1b710ab93 --- /dev/null +++ b/.github/workflows/check-links.yaml @@ -0,0 +1,40 @@ +name: Check Links + +on: + repository_dispatch: + workflow_dispatch: + schedule: + - cron: "00 12 * * 1" # Run weekly on Mondays at 12:00 UTC + +jobs: + linkChecker: + runs-on: ubuntu-latest + permissions: + issues: write # required for peter-evans/create-issue-from-file + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + + - name: Link Checker + id: lychee + uses: lycheeverse/lychee-action@1d97d84f0bc547f7b25f4c2170d87d810dc2fb2c + # We use --exclude '\.md(#.*)?$' to skip internal links in markdown like [See test](../tests/.../test_case.md) + # otherwise we get false positives due to links pointing to content that gets generated during our mkdocs flow. + # These links are checked during `mkdocs build --strict` + with: + args: > + README.md + src/**/*.py + src/**/*.md + tests/**/*.py + tests/**/*.md + docs/**/*.md + --exclude '\.md(#.*)?$' + fail: false + + - name: Create Issue From File + if: steps.lychee.outputs.exit_code != 0 + uses: peter-evans/create-issue-from-file@e8ef132d6df98ed982188e460ebb3b5d4ef3a9cd + with: + title: Link Checker Report + content-filepath: ./lychee/out.md + labels: report, automated issue diff --git a/.github/workflows/test.yaml b/.github/workflows/eels-test.yaml similarity index 99% rename from .github/workflows/test.yaml rename to .github/workflows/eels-test.yaml index a01e2c8964..d30006d2ab 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/eels-test.yaml @@ -3,7 +3,6 @@ name: Python Specification on: push: branches: - - master - mainnet - 'forks/**' workflow_dispatch: diff --git a/.github/workflows/eest-test.yaml b/.github/workflows/eest-test.yaml new file mode 100644 index 0000000000..b6ec42f669 --- /dev/null +++ b/.github/workflows/eest-test.yaml @@ -0,0 +1,215 @@ +name: Spec Tests & Framework + +on: + push: + branches: + - mainnet + - 'forks/**' + paths: + - 'src/ethereum_spec_tests/**' + - 'tests/eest/**' + - '.github/workflows/eest-test.yaml' + - '.github/actions/eest/**' + - '.github/configs/**' + - 'tox.ini' + - 'pyproject.toml' + - 'ruff.eest.toml' + - 'mypy.eest.ini' + pull_request: + paths: + - 'src/ethereum_spec_tests/**' + - 'tests/eest/**' + - '.github/workflows/eest-test.yaml' + - '.github/actions/eest/**' + - '.github/configs/**' + - 'tox.ini' + - 'pyproject.toml' + - 'ruff.eest.toml' + - 'mypy.eest.ini' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + lint: + name: Lint python sources with ruff + runs-on: ubuntu-latest + steps: + - name: Checkout ethereum/execution-specs + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Install uv ${{ vars.UV_VERSION }} and python ${{ vars.DEFAULT_PYTHON_VERSION }} + uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + version: ${{ vars.UV_VERSION }} + python-version: ${{ vars.DEFAULT_PYTHON_VERSION }} + - name: Run ruff linter via tox + run: uvx --with=tox-uv tox -e eest-lint + + typecheck: + name: Typecheck python sources with mypy + runs-on: ubuntu-latest + steps: + - name: Checkout ethereum/execution-specs + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Install uv ${{ vars.UV_VERSION }} and python ${{ vars.DEFAULT_PYTHON_VERSION }} + uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + version: ${{ vars.UV_VERSION }} + python-version: ${{ vars.DEFAULT_PYTHON_VERSION }} + - name: Run mypy typechecker via tox + run: uvx --with=tox-uv tox -e eest-typecheck + + spellcheck: + name: Spellcheck sources with codespell + runs-on: ubuntu-latest + steps: + - name: Checkout ethereum/execution-specs + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Install uv ${{ vars.UV_VERSION }} and python ${{ vars.DEFAULT_PYTHON_VERSION }} + uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + version: ${{ vars.UV_VERSION }} + python-version: ${{ vars.DEFAULT_PYTHON_VERSION }} + - name: Run spellcheck with codespell via tox + run: uvx --with=tox-uv tox -e eest-spellcheck + env: + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + + # Todo: requires EEST docs/ folder. + # changelog: + # name: Validate changelog entries + # runs-on: ubuntu-latest + # steps: + # - name: Checkout ethereum/execution-specs + # uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + # - name: Install uv ${{ vars.UV_VERSION }} and python ${{ vars.DEFAULT_PYTHON_VERSION }} + # uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + # with: + # enable-cache: true + # cache-dependency-glob: "uv.lock" + # version: ${{ vars.UV_VERSION }} + # python-version: ${{ vars.DEFAULT_PYTHON_VERSION }} + # - name: Run changelog validation via tox + # run: uvx --with=tox-uv tox -e eest-changelog + + # Todo: requires EEST docs/ folder. + # Consider adding to README.md, REALEASING.md, SECURITY.md and CONTRIBUTING.md + # markdownlint: + # name: Lint markdown files with markdownlint + # runs-on: ubuntu-latest + # steps: + # - name: Checkout ethereum/execution-specs + # uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + # - uses: DavidAnson/markdownlint-cli2-action@05f32210e84442804257b2a6f20b273450ec8265 + # with: + # globs: | + # docs/**/*.md + + mkdocs: + name: Build html documentation with mkdocs + runs-on: ubuntu-latest + steps: + - name: Checkout ethereum/execution-specs + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Install uv ${{ vars.UV_VERSION }} and python ${{ vars.DEFAULT_PYTHON_VERSION }} + uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + version: ${{ vars.UV_VERSION }} + python-version: ${{ vars.DEFAULT_PYTHON_VERSION }} + - name: Build html documentation with mkdocs via tox + run: uvx --with=tox-uv tox -e eest-mkdocs + + pytest_framework: + name: Run unit tests, ${{ matrix.os }}, ${{ matrix.python }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: macos-15 + python: "3.11" + - os: macos-15 + python: "pypy3.11" + - os: ubuntu-latest + python: "3.12" + steps: + - name: Checkout ethereum/execution-specs + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Install uv ${{ vars.UV_VERSION }} and python ${{ matrix.python }} + uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + version: ${{ vars.UV_VERSION }} + python-version: ${{ matrix.python }} + - name: Build EVMONE EVM + uses: ./.github/actions/eest/build-evm-client/evmone + with: + targets: "evmone-t8n" + - name: Build GETH EVM + uses: ./.github/actions/eest/build-evm-client/geth + - name: Run tox - run framework unit tests with pytest + run: uvx --with=tox-uv tox -e eest-pytest + + tests_deployed: + name: Fill tests, deployed, ${{ matrix.name }}, ${{ matrix.python }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: [self-hosted-ghr, size-chungus-x64] + name: self-hosted-ghr-chungus-x64 + python: "3.11" + - os: macos-15 + name: macos-15 + python: "3.12" + steps: + - name: Checkout ethereum/execution-specs + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Install uv ${{ vars.UV_VERSION }} and python ${{ matrix.python }} + uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + version: ${{ vars.UV_VERSION }} + python-version: ${{ matrix.python }} + - name: Run tox - fill tests for deployed forks + run: uvx --with=tox-uv tox -e eest-tests-deployed + + tests_deployed_benchmark: + name: Fill benchmark test cases, deployed, ${{ matrix.os }}, ${{ matrix.python }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + python: "3.11" + - os: macos-15 + python: "3.12" + - os: ubuntu-latest + python: "pypy3.11" + steps: + - name: Checkout ethereum/execution-specs + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Install uv ${{ vars.UV_VERSION }} and python ${{ matrix.python }} + uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + version: ${{ vars.UV_VERSION }} + python-version: ${{ matrix.python }} + - name: Build EVMONE EVM + uses: ./.github/actions/eest/build-evm-client/evmone + with: + targets: "evmone-t8n" + - name: Run tox - fill tests for deployed forks + run: uvx --with=tox-uv tox -e eest-tests-deployed-benchmark diff --git a/.github/workflows/evmone-coverage.yaml b/.github/workflows/evmone-coverage.yaml new file mode 100644 index 0000000000..0a9bb0e8e8 --- /dev/null +++ b/.github/workflows/evmone-coverage.yaml @@ -0,0 +1,209 @@ +name: Evmone EEST Tests Coverage Report + +on: + pull_request: + paths: + - "tests/eest/**" + - "!tests/eest/osaka/**" + - "!tests/eest/unscheduled/**" + +jobs: + evmone-coverage-diff: + runs-on: ubuntu-latest + + env: + BLOCK_GAS_LIMIT: "45000000" + FILL_UNTIL: "Cancun" + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + + - name: Debug GitHub context + run: | + echo "Git reference: ${{ github.ref }}" + echo "Git head ref: ${{ github.head_ref }}" + echo "Git base ref: ${{ github.base_ref }}" + echo "Node Version: $(node -v)" + echo "NPM Version: $(npm -v)" + + - name: Get all changed python files in tests/ and changes to coverted-ethereum-tests.txt + id: changed-tests + uses: tj-actions/changed-files@48d8f15b2aaa3d255ca5af3eba4870f807ce6b3c + with: + # TODO: non-test modules such as __init__.py or spec.py could effect coverage - in this case we should + # fill all applicable tests (i.e., all the test_*.py files in or under the changed module's directory) + include_all_old_new_renamed_files: true + output_renamed_files_as_deleted_and_added: true + files_yaml: | + tests: + - tests/eest/**/test_*.py + - '!tests/eest/prague/**' + - '!tests/eest/unscheduled/**' + + - name: Exit workflow if there are no changed python files + if: steps.changed-tests.outputs.tests_any_changed != 'true' + run: | + echo "No python files were changed in ./tests/eest/ - no action necessary" + exit 0 + + - name: Report changed python test modules + if: steps.changed-tests.outputs.tests_any_changed == 'true' + run: | + echo "${{ toJson(steps.changed-tests.outputs) }}" + echo "Changed python test modules: ${{ steps.changed-tests.outputs.tests_all_modified_files }}" + + - name: Debug GitHub context + run: | + echo "Git reference: ${{ github.ref }}" + echo "Git head ref: ${{ github.head_ref }}" + echo "Git base ref: ${{ github.base_ref }}" + + - name: Log in to Docker Hub + if: ${{ steps.changed-tests.outputs.tests_any_changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository }} + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 + with: + username: marioeth + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Install deps + if: steps.changed-tests.outputs.tests_any_changed == 'true' + run: | + echo $(pwd) + echo ${{ github.workspace }} + + - name: Install uv ${{ vars.UV_VERSION }} and python ${{ vars.DEFAULT_PYTHON_VERSION }} + if: steps.changed-tests.outputs.tests_any_changed == 'true' + uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 + with: + enable-cache: false + version: ${{ vars.UV_VERSION }} + python-version: ${{ vars.DEFAULT_PYTHON_VERSION }} + + - name: Install EEST + if: steps.changed-tests.outputs.tests_any_changed == 'true' + run: | + uv sync --no-progress + uv run python --version + + # Required to fill .py tests + - name: Build EVMONE EVM + uses: ./.github/actions/eest/build-evm-client/evmone + if: steps.changed-tests.outputs.tests_any_changed == 'true' + id: evm-builder2 + with: + targets: "evmone-t8n" + + - name: Checkout ethereum/tests + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + if: steps.changed-tests.outputs.tests_any_changed == 'true' + with: + repository: ethereum/tests + path: testpath + sparse-checkout: | + BlockchainTests + EOFTests + + - name: Checkout ethereum/legacytests + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + if: steps.changed-tests.outputs.tests_any_changed == 'true' + with: + repository: ethereum/legacytests + path: legacytestpath + sparse-checkout: | + Cancun/GeneralStateTests + Cancun/BlockchainTests + + # This command diffs the file and filters in new lines + - name: Parse ported_from markers from introduced .py tests + id: ported-from + if: steps.changed-tests.outputs.tests_any_changed == 'true' + env: + CHANGED_TEST_FILES: ${{ steps.changed-tests.outputs.tests_all_changed_files }} + run: | + ./.github/scripts/parse_ported_tests.sh "$CHANGED_TEST_FILES" "${{ github.workspace }}" || true + + # This command checks and fills python test sources introduced by a PR + - name: Parse and fill introduced test sources + if: steps.changed-tests.outputs.tests_any_changed == 'true' + env: + CHANGED_TEST_FILES: ${{ steps.changed-tests.outputs.tests_all_changed_files }} + run: | + ./.github/scripts/fill_introduced_tests.sh "$CHANGED_TEST_FILES" "${{ github.workspace }}/evmtest_coverage/coverage/PATCH_TESTS" "${{ env.BLOCK_GAS_LIMIT }}" "${{ env.FILL_UNTIL }}" + + - name: Parse and fill introduced test sources from before the PR + if: ${{ (steps.changed-tests.outputs.tests_modified_files_count != '0' || steps.changed-tests.outputs.tests_deleted_files_count != '0') && steps.ported-from.outputs.any_ported == 'false' }} + id: pre-patch-fill + env: + MODIFIED_TEST_FILES: ${{ steps.changed-tests.outputs.tests_modified_files }} + DELETED_TEST_FILES: ${{ steps.changed-tests.outputs.tests_deleted_files }} + run: | + ./.github/scripts/fill_prepatched_tests.sh "$MODIFIED_TEST_FILES $DELETED_TEST_FILES" "${{ github.workspace }}/evmtest_coverage/coverage/BASE_TESTS" "${{ github.workspace }}/evmtest_coverage/coverage/PATCH_TESTS" "${{ env.BLOCK_GAS_LIMIT }}" "${{ env.FILL_UNTIL }}" + + - name: Print Unique Test IDs that will be covered + if: ${{ steps.pre-patch-fill.outputs.any_modified_fixtures == 'true' || steps.ported-from.outputs.any_ported == 'true' }} + run: | + echo "=== Original BASE (main) test IDs ===" + if [ -f "${{ github.workspace }}/evmtest_coverage/coverage/BASE_TESTS/.meta/index.json" ]; then + uv run python -c "import json; data=json.load(open('${{ github.workspace }}/evmtest_coverage/coverage/BASE_TESTS/.meta/index.json')); [print(tc['id']) for tc in data['test_cases']]" + else + echo "No BASE_TESTS/.meta/index.json found" + fi + echo "=== Ported PATCH test IDs ===" + if [ -f "${{ github.workspace }}/evmtest_coverage/coverage/PATCH_TESTS/.meta/index.json" ]; then + uv run python -c "import json; data=json.load(open('${{ github.workspace }}/evmtest_coverage/coverage/PATCH_TESTS/.meta/index.json')); [print(tc['id']) for tc in data['test_cases']]" + else + echo "No PATCH_TESTS/.meta/index.json found" + fi + echo "=== SUMMARY ===" + if [ -f "${{ github.workspace }}/evmtest_coverage/coverage/BASE_TESTS/.meta/index.json" ] && [ -f "${{ github.workspace }}/evmtest_coverage/coverage/PATCH_TESTS/.meta/index.json" ]; then + uv run python -c "import json; base=json.load(open('${{ github.workspace }}/evmtest_coverage/coverage/BASE_TESTS/.meta/index.json')); patch=json.load(open('${{ github.workspace }}/evmtest_coverage/coverage/PATCH_TESTS/.meta/index.json')); print(f'BASE: {len(base[\"test_cases\"])} tests, PATCH: {len(patch[\"test_cases\"])} tests, Difference: {len(patch[\"test_cases\"]) - len(base[\"test_cases\"])}')" + else + echo "No BASE_TESTS/.meta/index.json or PATCH_TESTS/.meta/index.json found" + fi + + - name: Run coverage of the BASE tests + uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 + if: ${{ steps.pre-patch-fill.outputs.any_modified_fixtures == 'true' || steps.ported-from.outputs.any_ported == 'true' }} + with: + image: marioeth/evmone-coverage-script:v0.0.1 + options: -v ${{ github.workspace }}/evmtest_coverage/coverage:/tests + run: /entrypoint.sh --mode=cover --testpath=/tests/BASE_TESTS --outputname=BASE + + - name: Run coverage of the PATCH tests + uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 + if: ${{ steps.pre-patch-fill.outputs.any_modified_fixtures == 'true' || steps.ported-from.outputs.any_ported == 'true' }} + with: + image: marioeth/evmone-coverage-script:v0.0.1 + options: -v ${{ github.workspace }}/evmtest_coverage/coverage:/tests + run: /entrypoint.sh --mode=cover --testpath=/tests/PATCH_TESTS --outputname=PATCH + + - name: Run coverage DIFF of the PATCH tests compared to BASE tests + uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 + if: ${{ steps.pre-patch-fill.outputs.any_modified_fixtures == 'true' || steps.ported-from.outputs.any_ported == 'true' }} + with: + image: marioeth/evmone-coverage-script:v0.0.1 + options: -v ${{ github.workspace }}/evmtest_coverage/coverage:/tests + run: /entrypoint.sh --mode=diff --basefile=coverage_BASE.lcov --patchfile=coverage_PATCH.lcov + + - name: Chmod coverage results + if: ${{ steps.pre-patch-fill.outputs.any_modified_fixtures == 'true' || steps.ported-from.outputs.any_ported == 'true' }} + run: | + user=$(whoami) + sudo chown -R $user:$user ${{ github.workspace }}/evmtest_coverage/coverage + + - name: Upload coverage results + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + if: ${{ steps.pre-patch-fill.outputs.any_modified_fixtures == 'true' || steps.ported-from.outputs.any_ported == 'true' }} + with: + name: coverage-diff-native-${{ github.run_id }}-${{ github.run_attempt }} + path: ${{ github.workspace }}/evmtest_coverage/coverage + compression-level: 6 # Default compression level for optimal balance + + - name: Verify coverage results + uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 + if: ${{ steps.pre-patch-fill.outputs.any_modified_fixtures == 'true' || steps.ported-from.outputs.any_ported == 'true' }} + with: + image: marioeth/evmone-coverage-script:v0.0.1 + options: -v ${{ github.workspace }}/evmtest_coverage/coverage:/tests + run: /check.sh diff --git a/mypy.eest.ini b/mypy.eest.ini new file mode 100644 index 0000000000..e34c804d43 --- /dev/null +++ b/mypy.eest.ini @@ -0,0 +1,4 @@ +[mypy] +disable_error_code = import-untyped +mypy_path = src, $MYPY_CONFIG_FILE_DIR/stubs +plugins = pydantic.mypy \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 5407127960..dd7cb632a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -287,6 +287,7 @@ fill = [ "eth-abi>=5.2.0", "joblib>=1.4.2", "ckzg>=2.1.1", + "platformdirs>=4.2,<5", ] lint = [ @@ -501,6 +502,7 @@ skip = [ # Don't check these files/folders "tests/fixtures", "tests/json_infra/fixtures", "eest_tests", + "tests/eest/static", # Ignore `ethereum/tests` GeneralStateTests "src/ethereum_spec_tools/evm_tools/t8n" # Temporary while being re-written ] count = true # Display counts of errors @@ -508,3 +510,6 @@ check-hidden = false # Don't check hidden files (starting with .) [tool.uv] required-version = ">=0.7.0" + +[tool.mypy] +plugins = ["pydantic.mypy"] \ No newline at end of file diff --git a/ruff.eest.toml b/ruff.eest.toml new file mode 100644 index 0000000000..ee004eace0 --- /dev/null +++ b/ruff.eest.toml @@ -0,0 +1,6 @@ +line-length = 99 + +[lint] +select = ["E", "F", "B", "W", "I", "A", "N", "D", "C"] +fixable = ["I", "B", "E", "F", "W", "D", "C"] +ignore = ["D205", "D203", "D212", "D415", "C901", "A005", "C420"] \ No newline at end of file diff --git a/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-check-eip-versions.ini b/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-check-eip-versions.ini index e06dfc40df..89af790087 100644 --- a/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-check-eip-versions.ini +++ b/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-check-eip-versions.ini @@ -2,9 +2,13 @@ console_output_style = count minversion = 7.0 python_files = *.py +testpaths = tests/eest/ # Note: register new markers via src/pytest_plugins/shared/execute_fill.py or # src/pytest_plugins/spec_version_checker/spec_version_checker.py -testpaths = tests/ +markers = + slow + pre_alloc_modify + eip_version_check addopts = -p pytest_plugins.spec_version_checker.spec_version_checker -p pytest_plugins.concurrency diff --git a/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-execute-hive.ini b/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-execute-hive.ini index 72c18f218b..4f5c530adb 100644 --- a/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-execute-hive.ini +++ b/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-execute-hive.ini @@ -2,8 +2,12 @@ console_output_style = count minversion = 7.0 python_files = *.py -testpaths = tests/ +testpaths = tests/eest/ # Note: register new markers via src/pytest_plugins/shared/execute_fill.py +markers = + slow + pre_alloc_modify + ported_from addopts = -p pytest_plugins.concurrency -p pytest_plugins.execute.sender diff --git a/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-execute.ini b/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-execute.ini index 8e476ead0c..59f26f530b 100644 --- a/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-execute.ini +++ b/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-execute.ini @@ -2,8 +2,13 @@ console_output_style = count minversion = 7.0 python_files = *.py -testpaths = tests/ +testpaths = tests/eest/ # Note: register new markers via src/pytest_plugins/shared/execute_fill.py +markers = + slow + pre_alloc_modify + ported_from + pre_alloc_group: Control shared pre-allocation grouping (use "separate" for isolated group or custom string for named groups) addopts = -p pytest_plugins.concurrency -p pytest_plugins.execute.sender diff --git a/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-fill.ini b/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-fill.ini index 72476e7e5a..6928770c8a 100644 --- a/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-fill.ini +++ b/src/ethereum_spec_tests/cli/pytest_commands/pytest_ini_files/pytest-fill.ini @@ -2,8 +2,13 @@ console_output_style = count minversion = 7.0 python_files = *.py -testpaths = tests/ +testpaths = tests/eest/ # Note: register new markers via src/pytest_plugins/shared/execute_fill.py +markers = + slow + pre_alloc_modify + ported_from + pre_alloc_group: Control shared pre-allocation grouping (use "separate" for isolated group or custom string for named groups) addopts = -p pytest_plugins.concurrency -p pytest_plugins.filler.pre_alloc diff --git a/stubs/joblib/__init__.pyi b/stubs/joblib/__init__.pyi new file mode 100644 index 0000000000..7756352f49 --- /dev/null +++ b/stubs/joblib/__init__.pyi @@ -0,0 +1,5 @@ +from typing import Any, Callable + +class Memory: + def __init__(self, location: str, verbose: int = ...) -> None: ... + def cache(self, func: Callable[..., Any]) -> Callable[..., Any]: ... diff --git a/stubs/jwt/__init__.pyi b/stubs/jwt/__init__.pyi new file mode 100644 index 0000000000..dee1918afd --- /dev/null +++ b/stubs/jwt/__init__.pyi @@ -0,0 +1,3 @@ +from .encode import encode + +__all__ = ("encode",) diff --git a/stubs/jwt/encode.pyi b/stubs/jwt/encode.pyi new file mode 100644 index 0000000000..3bfe608a1a --- /dev/null +++ b/stubs/jwt/encode.pyi @@ -0,0 +1,3 @@ +from typing import Any, Dict + +def encode(payload: Dict[Any, Any], key: bytes, algorithm: str) -> str: ... diff --git a/stubs/trie/__init__.pyi b/stubs/trie/__init__.pyi new file mode 100644 index 0000000000..c7b52113ff --- /dev/null +++ b/stubs/trie/__init__.pyi @@ -0,0 +1,3 @@ +from .hexary import HexaryTrie as HexaryTrie + +__all__ = ("HexaryTrie",) diff --git a/stubs/trie/hexary.pyi b/stubs/trie/hexary.pyi new file mode 100644 index 0000000000..d464ac3564 --- /dev/null +++ b/stubs/trie/hexary.pyi @@ -0,0 +1,8 @@ +from typing import Dict + +class HexaryTrie: + db: Dict + root_hash: bytes + + def __init__(self, db: Dict) -> None: ... + def set(self, key: bytes, value: bytes) -> None: ... diff --git a/stubs/xdist/__init__.pyi b/stubs/xdist/__init__.pyi new file mode 100644 index 0000000000..1ec85655d4 --- /dev/null +++ b/stubs/xdist/__init__.pyi @@ -0,0 +1,3 @@ +from .methods import is_xdist_worker + +__all__ = ("is_xdist_worker",) diff --git a/stubs/xdist/methods.pyi b/stubs/xdist/methods.pyi new file mode 100644 index 0000000000..c3e9d5bbad --- /dev/null +++ b/stubs/xdist/methods.pyi @@ -0,0 +1,3 @@ +import pytest + +def is_xdist_worker(session: pytest.Session) -> bool: ... diff --git a/tox.ini b/tox.ini index de618d1c50..ec624101e5 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ min_version = 4.0 requires = tox-uv >=0.2.0 -envlist = py3,pypy3,json_infra,static +envlist = py3,pypy3,json_infra,static,eest-lint,eest-typecheck,eest-pytest,eest-tests-deployed,eest-tests-deployed-benchmark,eest-tests-develop [testenv] package = uv @@ -95,3 +95,110 @@ extras = doc commands = docc --output "{toxworkdir}/docs" python -c 'import pathlib; print("documentation available under file://\{0\}".format(pathlib.Path(r"{toxworkdir}") / "docs" / "index.html"))' + + +[eest] +runner = uv-venv-lock-runner +package = editable +wheel_build_env = .pkg +python_source_dirs = src/ethereum_spec_tests tests/eest # .github/scripts +ruff_config = ruff.eest.toml +mypy_config = mypy.eest.ini + +[testenv:eest-lint] +runner = {[eest]runner} +package = {[eest]package} +wheel_build_env = {[eest]wheel_build_env} +description = Lint and code formatting checks (ruff) +extras = lint +commands = + ruff check --config {[eest]ruff_config} --no-fix --show-fixes {[eest]python_source_dirs} + ruff format --config {[eest]ruff_config} --check {[eest]python_source_dirs} + +[testenv:eest-typecheck] +runner = {[eest]runner} +package = {[eest]package} +wheel_build_env = {[eest]wheel_build_env} +description = Run type checking (mypy) +extras = test,fill,lint +commands = mypy --config-file {[eest]mypy_config} {[eest]python_source_dirs} + +[testenv:eest-spellcheck] +runner = {[eest]runner} +package = {[eest]package} +wheel_build_env = {[eest]wheel_build_env} +description = Spellcheck EEST code and documentation (codespell) +extras = lint +passenv = + GITHUB_ACTIONS + GITHUB_STEP_SUMMARY +commands = + codespell --ignore-words=whitelist-eest.txt {[eest]python_source_dirs} # TODO: Add EEST docs/ + +; TODO: requires EEST docs/ +; [testenv:eest-changelog] +; runner = {[eest]runner} +; package = {[eest]package} +; wheel_build_env = {[eest]wheel_build_env} +; description = Validate EEST changelog entries +; extras = docs +; commands = python -c "import src.cli.tox_helpers; src.cli.tox_helpers.validate_changelog()" + +; TODO: requires EEST docs/ configuration +; [testenv:eest-mkdocs] +; runner = {[eest]runner} +; package = {[eest]package} +; wheel_build_env = {[eest]wheel_build_env} +; description = Build EEST documentation with mkdocs +; extras = docs +; commands = mkdocs build --strict + +[testenv:eest-pytest] +runner = {[eest]runner} +package = {[eest]package} +wheel_build_env = {[eest]wheel_build_env} +description = Run library and framework unit tests (pytest) +setenv = + # Use custom EELS_RESOLUTIONS_FILE if it is set via the environment (eg, in CI) + EELS_RESOLUTIONS_FILE = {env:EELS_RESOLUTIONS_FILE:} + CI = {env:CI:} +extras = + test + fill + lint # Required `gentest` for formatting tests +commands = + pytest -n auto ./src/ethereum_spec_tests + + +[forks] +develop = Osaka + +[testenv:eest-tests-deployed] +runner = {[eest]runner} +package = {[eest]package} +wheel_build_env = {[eest]wheel_build_env} +description = Fill test cases in ./tests/ for deployed mainnet forks, except for slow/benchmark tests. +extras = test,fill +setenv = + # Use custom EELS_RESOLUTIONS_FILE if it is set via the environment (eg, in CI) + EELS_RESOLUTIONS_FILE = {env:EELS_RESOLUTIONS_FILE:} +commands = fill -n auto -m "not slow" --output=/tmp/fixtures-tox --clean + +[testenv:eest-tests-deployed-benchmark] +runner = {[eest]runner} +package = {[eest]package} +wheel_build_env = {[eest]wheel_build_env} +description = Fill benchmarking test cases in ./tests/ for deployed mainnet forks, using evmone-t8n. +extras = test,fill +commands = fill -n auto -m "benchmark" --gas-benchmark-values 5 --output=/tmp/fixtures-tox --clean --evm-bin=evmone-t8n + +[testenv:eest-tests-develop] +runner = {[eest]runner} +package = {[eest]package} +wheel_build_env = {[eest]wheel_build_env} +description = Fill test cases in ./tests/ for deployed and development mainnet forks +setenv = + # Use custom EELS_RESOLUTIONS_FILE if it is set via the environment (eg, in CI) + EELS_RESOLUTIONS_FILE = {env:EELS_RESOLUTIONS_FILE:} +extras = test,fill +commands = fill -n auto --until={[forks]develop} -k "not slow" --output=/tmp/fixtures-tox --clean diff --git a/uv.lock b/uv.lock index 6c0aad0575..73c87f1dad 100644 --- a/uv.lock +++ b/uv.lock @@ -536,6 +536,7 @@ fill = [ { name = "ethereum-types" }, { name = "gitpython" }, { name = "joblib" }, + { name = "platformdirs" }, { name = "pydantic" }, { name = "pyjwt" }, { name = "pytest-custom-report" }, @@ -603,6 +604,7 @@ requires-dist = [ { name = "libcst", marker = "extra == 'test'", specifier = ">=1.8,<2" }, { name = "libcst", marker = "extra == 'tools'", specifier = ">=1.8,<2" }, { name = "mypy", marker = "extra == 'lint'", specifier = "==1.17.0" }, + { name = "platformdirs", marker = "extra == 'fill'", specifier = ">=4.2,<5" }, { name = "platformdirs", marker = "extra == 'tools'", specifier = ">=4.2,<5" }, { name = "py-ecc", specifier = ">=8.0.0b2,<9" }, { name = "pycryptodome", specifier = ">=3.22,<4" }, diff --git a/whitelist-eest.txt b/whitelist-eest.txt new file mode 100644 index 0000000000..bfeaba6ba5 --- /dev/null +++ b/whitelist-eest.txt @@ -0,0 +1,1078 @@ +uv +femtoether +Picoether +microether +milliether +milli +nanoether +0xaa +AccessListType +Account1 +Account2 +acl +addr +address +address2 +AddressType +alloc +api +apis +arcname +argcount +argnames +argvalues +ase +asm +at5 +AuthorizationInvalidityType +AutoSection +auxdata +balance +base64 +basefee +basename +bb +BerlinToLondonAt +besu +pyspecs +big0 +big1 +bigint +blake2f +blobgasfee +blockchain +BlockchainFixtures +BlockchainTest +BlockchainTestFiller +BlockchainTests +BlockException +blockhash +blockhashes +blocknum +blocktest +bls +bls12 +blueswen +bool +boolean +br +bytecode +byteorder +bytes20 +bytes32 +bytes8 +calc +calldata +calldatacopy +calldataload +calldatasize +callee +caller +callvalue +cancun +cd +chainid +ChainIDType +changelog +chfast +classdict +cli +clis +CLIs +cli2 +cmd +codeAddr +codebase +codecopy +codesize +coinbase +coincurve +commonpath +compilable +config +configs +conftest +contractAddr +controlflow +cp +CPUs +crypto +cryptographic +currentframe +customizations +Customizations +danceratopz +dao +dataclasses +datacopy +dataload +dataloadn +datasize +datastructures +delitem +Dencun +designator +deprecations +deserialization +deserialized +dev +devnet +difficulty +dir +dirname +dirs +discordapp +dockerdocs +docstring +docstrings +doseq +dunder +dup +eip +eip123 +eip3540 +eip4844 +eip6110 +eip7002 +eip7069 +eip7620 +eip7692 +P7692 +eip7251 +eips +EIPs +EIP's +el +endianness +EngineAPI +enum +env +envvar +eoa +EOA +EOAs +eof +eofwrap +EOF1 +EOFException +eofparse +epilog +esbenp +eest +EEST's +ERC +eth +ethash +ethereum +ethereum's +EthereumJS +evaluatable +evm +EVMCodeType +evmone +Evmone +executables +extcodecopy +extcodehash +extcodesize +exitstatus +F00 +fcu +filelock +filesystem +fillvalue +firstlineno +fmt +fn +fname +forkchoice +formatOnSave +formatter +fp +fp2 +fromhex +func +g1 +g1add +g1msm +g1mul +g2 +g2add +g2msm +g2mul +gaslimit +gasprice +gcc +GeneralStateTestsFiller +gentest +getframeinfo +geth +geth's +getitem +getmtime +gh +GHSA +git's +github +Github +Github's +glightbox +globals +go-ethereum's +Golang +gwei +hacky +hardfork +hash32 +Hashable +hasher +HeaderNonce +hexary +hexbytes +HexNumber +hexsha +hiveview +homebrew +html +htmlpath +https +hyperledger +iat +idx +ignoreRevsFile +img +imm +immediates +incr +incrementing +ini +init +initcode +initcodes +inputdata +instantiation +io +isfile +isidentifier +islice +isort +isort's +ispkg +itemName +javascripts +jimporter +joinpath +jpg +jq +js +json +JSON +keccak +keccak256 +keepends +key2 +kzg +kzgs +lastblockhash +len +Levenshtein +linux +listdir +lll +lllc +london +LTS +macOS +mainnet +makefiles +makereport +makeconftest +marioevz +markdownlint +md +mem +mempool +metaclass +mixin +mixins +mixhash +mkdocs +mkdocstrings +mortem +mortems +msm +mypy +ctx +namespace +natively +nav +ncheck +nektos +nethermind +Nethermind's +nexternal +nGo +nJSON +nonreturning +nop +NOP +NOPs +nPython +nSHA +t8ntool +NOTSET +num +number +ommer +ommers +oneliner +oob +OpenSSL +opc +oprypin +ori +origin +P1 +P2 +p256verify +parseable +passthrough +pathlib +pdb +Pectra +perf +permalink +petersburg +pformat +platformdirs +pluginmanager +png +Pomerantz +POS +ppa +ppas +prague +pre +Pre +precompile +predeploy +prepend +prestateTracer +PrevRandao +PRs +programmatically +pubkey +px +py +pydantic +pyproject +Pyspec +pyspelling +pytest +Pytest +pytest's +pytestArgs +qGpsxSA +questionary +quickstart +radd +randao +readme +readthedocs +reentrancy +reentrant +repo +repo's +repos +returncode +returncontract +returndata +returndatacopy +returndatasize +reusability +rlp +rootdir +rpc +ruleset +runtestloop +runtime +S12 +sandboxed +secp256k1 +secp256k1n +selfbalance +ser +serializable +servable +setenv +setitem +sha +SHA +sharding +SignerType +simlimit +solc +soliditylang +Spec4844 +spencer +spencertaylorbrown +spencertb +splitext +squidfunk +src +ssz +stackoverflow +stateful +StateTest +StateTestFiller +StateTests +staticcalled +stdin +stdout +stExample +str +streetsidesoftware +subcall +subclasscheck +subcommands +subdir +subdirectories +subdirectory +subgraph +subprocess +subscriptable +substring +sudo +sysconfig +t8n +tamasfe +tbd +terminalreporter +testability +TestAddress +testscollected +TestContractCreationGasUsage +TestMultipleWithdrawalsSameAddress +testrun +textwrap +ThreeHrSleep +time15k +timestamp +tmp +todo +toml +toplevel +tox +Tox +traceback +TransactionException +trie +triggerable +tstorage +tx +tx1 +tx2 +txs +txt +ty +typehints +u256 +ubuntu +ukiyo +uid +uncomment +underflow +undersize +unlink +usr +unixsocket +util +utils +v0 +v1 +v2 +v3 +v4 +validator +validators +ValidAt +ValidAtTransitionTo +ValidFrom +ValidUntil +venv +visualstudio +vm +VRS +vscode +vv +wd +wds +wei +wikipedia +wordlist +workspaces +www +xdist +xF +xFA +xFD +xFF +xpassed +yaml +YAML +yml +yParity +yul +zfill +addoption +addinivalue +argname +autouse +basedir +callspec +collectonly +copyfile +copytree +dedent +dest +exc +extractall +fg +fixturenames +fspath +funcargs +getfixturevalue +getgroup +getoption +Golang +groupby +hookimpl +hookwrapper +IEXEC +IGNORECASE +inifile +isatty +iterdir +ljust +longreprtext +makepyfile +makereport +metafunc +modifyitems +monkeypatching +neth +nethtest +nodeid +noop +oog +optparser +originalname +parametrized +param +params +parametrize +parametrizes +parametrizer +parametrizers +parametrization +parametrizing +popen +prevrandao +prover +provers +pytestconfig +pytester +pytestmark +readline +regexes +removesuffix +reportinfo +ret +rglob +ripemd +rjust +rootpath +runpytest +runtest +startdir +subclasses +subcommand +subcontainer +substring +substrings +teardown +tempdir +testdir +teststatus +tmpdir +toc +tryfirst +trylast +usefixtures +verifier +verifiers +writelines +xfail +ZeroPaddedHexNumber +P7692 + +stop +add +mul +sub +div +sdiv +mod +smod +addmod +mulmod +exp +signextend +lt +gt +slt +sgt +eq +iszero +and +or +xor +not +byte +shl +shr +sar +sha3 +address +balance +origin +caller +callvalue +calldataload +calldatasize +calldatacopy +codesize +codecopy +gasprice +extcode +extcodesize +extcodecopy +returndata +returndatasize +returndatacopy +returndataload +extcodehash +blockhash +coinbase +timestamp +number +difficulty +gaslimit +pop +mload +mstore +mstore8 +sload +sstore +jump +jumpi +jumpf +pc +msize +gas +jumpdest +rjump +rjumpi +rjumpkind +rjumps +rjumpv +RJUMPV +callf +retf +push0 +push1 +push2 +push3 +push4 +push5 +push6 +push7 +push8 +push9 +push10 +push11 +push12 +push13 +push14 +push15 +push16 +push17 +push18 +push19 +push20 +push21 +push22 +push23 +push24 +push25 +push26 +push27 +push28 +push29 +push30 +push31 +push32 +dup1 +dup2 +dup3 +dup4 +dup5 +dup6 +dup7 +dup8 +dup9 +dup10 +dup11 +dup12 +dup13 +dup14 +dup15 +dup16 +dupn +swap1 +swap2 +swap3 +swap4 +swap5 +swap6 +swap7 +swap8 +swap9 +swap10 +swap11 +swap12 +swap13 +swap14 +swap15 +swap16 +swapn +log0 +log1 +log2 +log3 +log4 +dataload +dataloadn +datasize +datacopy +tload +tstore +create +create2 +create3 +create4 +call +callcode +return +delegatecall +delegatecalling +eofcreate +eoftest +extcall +extcalls +extdelegatecall +staticcall +extstaticcall +revert +selfdestruct +selfdestructing +sendall +blobhash +blobbasefee +mcopy +precompile +precompiles +deployer +0x +modexp +ModExp +0x00 +0x01 +0x10 + +fi +url +gz +tT +istanbul +berlin + +0A +0B +0C +0D +0E +0F +1A +1B +1C +1D +1E +1F +2A +2B +2C +2D +2E +2F +3A +3B +3C +3D +3E +3F +4A +4B +4C +4D +4E +4F +5A +5B +5C +5D +5E +5F +6A +6B +6C +6D +6E +6F +7A +7B +7C +7D +7E +7F +8A +8B +8C +8D +8E +8F +9A +9B +9C +9D +9E +9F +A0 +A1 +A2 +A3 +A4 +A5 +A6 +A7 +A8 +A9 +AA +AB +AC +AD +AE +AF +B0 +B1 +B2 +B3 +B4 +B5 +B6 +B7 +B8 +B9 +BA +BB +BC +BD +BE +BF +C0 +C1 +C2 +C3 +C4 +C5 +C6 +C7 +C8 +C9 +CA +CB +CC +CD +CE +CF +D0 +D1 +D2 +D3 +D4 +D5 +D6 +D7 +D8 +D9 +DA +DB +DC +DE +DF +DF +E0 +E1 +E2 +E3 +E4 +E5 +E6 +E7 +E8 +E9 +EA +EB +EC +ED +EE +EF +F0 +F1 +F2 +F3 +F4 +F5 +F6 +F7 +F8 +F9 +FA +FB +FC +FD +FE +FF +ef01 +uv +EthereumCLI +TransitionTool +rebase +deps +datatables +jinja +templating +EOFv +subcontainers +ize +nectos +fibonacci +CPython +legacytests +pycryptodome +pytest_sessionfinish +levelname +pytest_runtest_logstart +pytest_runtest_logreport +pytest_runtest_logfinish +eestloglevel +caplog +workerinput +dockerenv +stderr +EESTLogger +Formatters +UI +asctime +petertdavies +cofactor +cofactors +coeffs +uncomp +isogeny +codomain +nametag +CFI'd +opcode's +rf +buildarg +buildoutput +nocache +checktimelimit +loglevel +ci +netstat +tlnp +golang +newPayload +baseimage +nametags +liveness +paradigmxyz +lightclient +PreviousFork +NewFork +ps +forkchoiceUpdated +ps +ufw +benchmarking +validium +rollup +JWT +WebSocket +Prysm +xargs +ghcr +Erigon +Dockerfiles +dockerized +tgz +reth +http +PID +xml +junit +dockerfile +args +PowerShell +pectra +uname +Zsh +usermod +newgrp +systemctl +backticked +Typecheck +autoformat +Typechecking +groupstats +SharedPreStateGroup +qube +aspell +codespell +nagydani +guido +marcin +peerdas +ckzg +CLZ +Fujisan +zkevm +jsign +Glamsterdam +Watcherfall +uint +Uint +master +Lets \ No newline at end of file