Skip to content

refactor(spec-tests): simplify fork-transition handling in the json-loader - #9

Open
danceratopz wants to merge 3 commits into
jochem-brouwer:json-loader/fork-transition-fixturesfrom
danceratopz:json-loader/fork-transition-fixtures-suggestions
Open

danceratopz wants to merge 3 commits into
jochem-brouwer:json-loader/fork-transition-fixturesfrom
danceratopz:json-loader/fork-transition-fixtures-suggestions

Conversation

@danceratopz

@danceratopz danceratopz commented Sep 15, 2026

Copy link
Copy Markdown

Description

Three follow-ups to the fork-transition support in the json-loader, one commit each. They do not change which fixtures run or how they run: just static, the json-loader unit tests, the transition fixtures under tests/json_loader/fixtures, and a fresh fill of the Amsterdam transition tests pass before and after each commit.

  1. Resolve transition networks from the testing forks. The loader parsed a fixture's network name with a regex to recover the starting fork, the activating fork and the activation point. The testing package already declares every transition fork with exactly those facts in forks/transition.py, and the transition fork's name is the fixture's network, so the loader now looks the name up in get_transition_forks(). This removes the regex and the k suffix expansion. A unit test checks that both ends of every transition fork map to a fork of the spec, so a transition fork the spec cannot run fails loudly instead of being dropped at collection.

  2. Read the decoded header of invalid transition blocks. To decide which fork an invalid block belongs to, the loader decoded the block's RLP itself and read the number and the timestamp at fixed header positions. The filler writes the decoded block under rlp_decoded for every invalid block whose RLP decodes (see InvalidFixtureBlock in fixtures/blockchain.py), which is exactly the case the manual decoding covered, so the loader reads that header instead. A block without a decoded header stays with the fork before it, as before. A header whose timestamp does not fit in a U256 now stays there too; previously it raised an OverflowError before the expected exception was checked.

  3. Schedule transition forks in the fork package as well. The loader set the activating fork's FORK_CRITERIA only in the fork's fork module, and only when that module binds the name, which today is the case for London and the DAO fork alone. For every other fork the helper did nothing, and the fork tooling, which reads the constant from the fork package, kept reporting the mainnet schedule during the test. The helper now patches the package attribute unconditionally and the module's own binding when there is one. Two unit tests cover both shapes and check that the patches are undone.

The unit tests grow from 9 to 23.

Related Issues or PRs

Suggestions for ethereum#3564. The third change is the seam ethereum#3535 relies on, since its Amsterdam fork module imports FORK_CRITERIA the way London's does.

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

Put a link to a cute animal picture inside the parenthesis-->

The json-loader parsed a transition fixture's network name with a regex to
recover the starting fork, the activating fork and the activation
criteria. The testing package already declares every transition fork with
exactly those three facts, and the transition fork's name is the fixture's
network, so look the name up there instead. A network that names no
transition fork is a plain fork name, as before. A unit test checks that
both ends of every transition fork map to a fork of the spec.
…locks

To decide which fork an invalid block of a transition fixture belongs to,
the json-loader decoded the block's RLP itself and read the number and the
timestamp at fixed header positions. The filler already writes the decoded
block under `rlp_decoded` for every invalid block whose RLP decodes, which
is exactly the case the manual decoding covered, so read that header
instead. A block without a decoded header stays with the fork before it,
as before, and so does a header whose timestamp does not fit in a `U256`,
which previously escaped as an `OverflowError` before the expected
exception was checked.
For a transition fixture, the json-loader sets the activating fork's
`FORK_CRITERIA` to the fixture's schedule so that the fork detects its
own fork block. It patched the name only in the fork's `fork` module, and
only when that module binds it, which today is the case for London and
the DAO fork alone. For every other fork the helper did nothing, and the
fork tooling that reads the constant from the fork package kept reporting
the mainnet schedule. Patch the package attribute as well, unconditionally,
and keep patching the module's own binding when there is one. Unit tests
cover both shapes and check that the patches are undone.

@jochem-brouwer jochem-brouwer left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

One question on this one, thanks @danceratopz 😄 👍

if header is None:
return False
number = Uint(int(header["number"], 16))
timestamp = int(header["timestamp"], 16)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Why is the conversion here different? number has Uint(int( and timestamp int(. What is the reason for this? 🤔 Why are these not the same?

Is this some kind of overflow error? Then number should also have this check, right?

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.

2 participants