Skip to content

feat(ci): validate filled blockchain fixtures with EELS - #3552

Merged
danceratopz merged 4 commits into
ethereum:forks/amsterdamfrom
danceratopz:ci-block-validation
Sep 10, 2026
Merged

danceratopz merged 4 commits into
ethereum:forks/amsterdamfrom
danceratopz:ci-block-validation

Conversation

@danceratopz

@danceratopz danceratopz commented Sep 9, 2026 •

Copy link
Copy Markdown
Member

Description

Filling never runs the EELS block validator. The t8n path calls process_transaction and the block-level helpers, but the test framework assembles the block header itself, so state_transition, execute_block, validate_header and apply_body are only reached when a fixture is consumed. Header-level invalid blocks (rlp_modifier, requests and BAL overrides) are also exempt from t8n exception verification during fill.

This PR takes @spencer-tb's idea from #3526 to add a just validate-blocks <blockchain_tests dir> [pytest args] recipe, which runs an existing blockchain_tests directory through the tests/json_loader path. In contrast to #3526, the conftest is loaded as a pytest plugin, so any fixture directory can be passed directly and no symlink under tests/json_loader is needed. Coverage data is written under .just/validate-blocks/; --no-cov skips it.

This PR then additionally makes these CI changes:

  • test.yaml: each fill matrix job validates the fixtures it just filled and uploads the validation coverage next to the fill coverage, so codecov sees the union.
  • build-fixtures action: each split EELS release build validates its blockchain_tests before uploading the artifact. Unsplit (tarball) and non-EELS builds are skipped.

Cost on the CI runners

The tables compare the fill matrix jobs of the Python Specification workflow without and with the new step. The two baseline runs are the two most recent forks/amsterdam push runs, which have no validation step. The two PR runs are the runs on this branch; both commits have the same tree and differ only in the first commit message. Durations are job and step started_at to completed_at from the Actions API, so runner queue time is excluded. Fixture counts are the pytest summary lines of the new step.

Fill job duration:

label push 80edd40 push 903b48f PR run 1 PR run 2
pre-shanghai 2m21s 2m23s 2m47s 2m37s
shanghai-cancun 2m13s 2m11s 2m37s 2m23s
prague 2m17s 2m12s 2m29s 2m26s
osaka 2m20s 2m15s 2m36s 2m32s
amsterdam 3m09s 3m04s 3m36s 3m40s

The "Run fill" step itself is unchanged within noise (amsterdam 2m48s to 2m52s across the four runs), and the codecov upload with two files still takes 3 to 4 s.

The new step on its own:

label step, PR run 1 step, PR run 2 fixtures validated share of the fill step, run 2
pre-shanghai 14 s 13 s 2,475 10%
shanghai-cancun 14 s 12 s 1,706 11%
prague 16 s 14 s 2,318 12%
osaka 18 s 17 s 2,402 14%
amsterdam 33 s 33 s 5,781 19%

All 14,682 fixtures passed EELS block validation. Coverage instrumentation is most of the step's cost: locally a 95-fixture sample took 16 s with coverage and 5 s with --no-cov.

Coverage change

Codecov merges the uploads of all fill jobs and the json-loader job under the unittests flag. The tables compare the codecov report of the base commit with the report of this PR's head, both read from the codecov API (/report/?sha=<commit>). No source or test file changed, so the whole difference comes from the validation upload. The PR report shows the same totals.

base 0cc100e head 6ac3c33 delta
coverage 94.01% 94.38% +0.37
hits 34,717 34,855 +138
misses 1,533 1,460 -73
partials 677 612 -65

Every file whose hit count changed:

file base head delta hits partials
src/ethereum/forks/amsterdam/fork.py 80.20% 85.57% +5.37 +16 -8
src/ethereum/forks/osaka/fork.py 79.86% 85.32% +5.46 +16 -8
src/ethereum/forks/prague/fork.py 80.63% 85.56% +4.93 +14 -7
src/ethereum/forks/cancun/fork.py 76.73% 81.63% +4.90 +12 -6
src/ethereum/forks/shanghai/fork.py 70.64% 75.62% +4.98 +10 -5
src/ethereum/forks/london/fork.py 61.17% 64.10% +2.93 +8 -4
src/ethereum/forks/paris/fork.py 68.94% 73.15% +4.21 +8 -4
src/ethereum/forks/berlin/fork.py 61.60% 64.13% +2.53 +6 -3
src/ethereum/forks/byzantium/fork.py 60.26% 62.88% +2.62 +6 -3
src/ethereum/forks/constantinople/fork.py 60.26% 62.88% +2.62 +6 -3
src/ethereum/forks/frontier/fork.py 60.08% 62.78% +2.70 +6 -3
src/ethereum/forks/homestead/fork.py 59.72% 62.44% +2.72 +6 -3
src/ethereum/forks/istanbul/fork.py 60.26% 62.88% +2.62 +6 -3
src/ethereum/forks/cancun/transactions.py 95.02% 97.23% +2.21 +4 -2
src/ethereum/forks/prague/transactions.py 95.94% 97.74% +1.80 +4 -2
src/ethereum/forks/berlin/transactions.py 94.06% 95.76% +1.70 +2 +0
src/ethereum/forks/london/transactions.py 92.51% 93.87% +1.36 +2 +0
src/ethereum/forks/paris/transactions.py 92.51% 93.87% +1.36 +2 +0
src/ethereum/forks/shanghai/transactions.py 92.66% 94.00% +1.34 +2 +0
src/ethereum/state_mpt.py 95.18% 97.59% +2.41 +2 -1

Every gain is in fork.py (the header comparison branches, validate_header and apply_body), in transaction RLP decoding, or in the MPT state root path. The gain is smaller than a fill-only comparison would suggest because the json-loader job already reports the base-coverage subset through state_transition. The new step adds the lines reached only by fixtures outside that subset, for every fork range.

Notes:

  • Transition fixtures (network such as OsakaToAmsterdamAtTime15k) are skipped by the loader, as before.
  • The release step only runs on release_fixtures dispatch or the nightly schedule, so this PR's checks do not exercise it.

Related Issues or PRs

Complements #3526, which uses the same json_loader path for per-EIP coverage measurement.

Checklist

  • Ran fast static checks to avoid CI fails, see Code Standards & Verifying Changes: just static
  • PR title has the form <type>(<area>): <title>, where <type> and <area> come from an appropriate C-<type>, respectively A-<area>, label. The title should match the target squash commit message.

Cute Animal Picture

image

@codecov

codecov Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.39%. Comparing base (0cc100e) to head (46f4870).
⚠️ Report is 3 commits behind head on forks/amsterdam.

Additional details and impacted files
@@                 Coverage Diff                 @@
##           forks/amsterdam    #3552      +/-   ##
===================================================
+ Coverage            94.01%   94.39%   +0.37%     
===================================================
  Files                  624      624              
  Lines                36927    36927              
  Branches              3326     3326              
===================================================
+ Hits                 34717    34857     +140     
+ Misses                1533     1459      -74     
+ Partials               677      611      -66     
Flag Coverage Δ
unittests 94.39% <ø> (+0.37%) ⬆️

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

☔ View full report in Codecov by Harness.
📢 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.

danceratopz and others added 3 commits September 9, 2026 10:21
Run an existing blockchain_tests fixture directory through the
json_loader path, which calls state_transition for every block and so
exercises execute_block, validate_header and apply_body. Fill's t8n
never reaches these; the test framework assembles the block header
itself.

The json_loader conftest is loaded as a plugin with -p so any fixture
directory can be passed directly, without symlinking it under
tests/json_loader. `python -m pytest` puts the working directory on
sys.path so the plugin module resolves. Coverage data is isolated under
.just/validate-blocks so the fill data file read by `just coverage` is
left intact; pass --no-cov to skip coverage.

Based on the block-coverage recipe proposed by spencer-tb in ethereum#3526.

Co-authored-by: spencer-tb <spencer.tb@ethereum.org>
Add a step to each fill matrix job that runs the fixtures just written
to .just/fill/fixtures/blockchain_tests through validate-blocks, and
upload its coverage report alongside the fill report. The step checks
on every push that framework-built headers and declared block
exceptions agree with the EELS block validator.
Run validate-blocks on the blockchain_tests directory of every split
EELS release build before its artifact is uploaded. Unsplit builds
write a tarball and non-EELS builds use another t8n, so both are
skipped. Coverage is disabled; the step is a consistency gate only.
@danceratopz
danceratopz marked this pull request as ready for review September 9, 2026 11:11
@spencer-tb

Copy link
Copy Markdown
Contributor

I need more cats and puzzles in my life :)

@spencer-tb spencer-tb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!! Really nice with Codecov :)

One suggestion from Codex I agree with copied below!

Comment thread Justfile Outdated
Required to avoid a `ValueError` when the specified fixtures directory is located under `tests/json_loader`.

Co-authored-by: spencer <spencer.tb@ethereum.org>
@danceratopz danceratopz added C-feat Category: an improvement or new feature A-ci Area: Continuous Integration labels Sep 10, 2026
@danceratopz
danceratopz merged commit ffc7e04 into ethereum:forks/amsterdam Sep 10, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ci Area: Continuous Integration C-feat Category: an improvement or new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants