Skip to content

test: load the frame transaction fixtures by mapping their fork name - #12854

Open
Marchhill wants to merge 4 commits into
eip8141-frame-txs-devnet7from
claude/eip8141-fixture-suite
Open

test: load the frame transaction fixtures by mapping their fork name#12854
Marchhill wants to merge 4 commits into
eip8141-frame-txs-devnet7from
claude/eip8141-fixture-suite

Conversation

@Marchhill

@Marchhill Marchhill commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Changes

  • Map Bogota in SpecNameParser, so the EIP-8141 fixture suites — which declare that network — load instead of failing every file.
  • Narrow the HalfBlockchainTestJson shape fallback in ConvertToBlockchainTests to deserialization only. It previously wrapped conversion too, so an unmapped fork name was reported as an unrelated Hash256 conversion error against the trimmed shape rather than as itself. A fixture matching neither shape now surfaces both causes instead of discarding the standard-shape one.
  • Name the offending fork in the NotSupportedException instead of throwing it bare.

Before this change the whole suite failed to load; after it, all 159 blockchain cases load and execute.

Fixtures come from the tests-frames-devnet@v0.0.0 release (fixtures_frames-devnet.tar.gz). Note that EIP-8141 merged to the eips/amsterdam/eip-8141 branch rather than master, and the pyspec DEFAULT_ARCHIVE_VERSION pin contains none of these tests, so wiring this into CI needs a second archive pin.

Types of changes

What types of changes does your code introduce?

  • Bugfix (a non-breaking change that fixes an issue)
  • New feature (a non-breaking change that adds functionality)
  • Breaking change (a change that causes existing functionality not to work as expected)
  • Optimization
  • Refactoring
  • Documentation update
  • Build-related changes
  • Other: Description

Testing

Requires testing

  • Yes
  • No

If yes, did you write tests?

  • Yes
  • No

Notes on testing

SpecNameParserTests covers both the mapping and the exception message. Both assertions were revert-checked: removing the mapping and restoring the bare throw fails them.

Documentation

Requires documentation update

  • Yes
  • No

Requires explanation in Release Notes

  • Yes
  • No

Remarks

The state-test path does not gate anything — do not wire it into CI

TransactionJson has no frames/signatures fields, so a frame transaction cannot be represented in a state-test fixture. The 40 EIP-8141 state-test files therefore load to zero cases, and the runner exits 0. Anything that runs that path today reports a pass while asserting nothing. Only the blockchain-test path, which decodes block RLP and so exercises the real frame decoder, is meaningful. Supporting state tests means adding the frame fields to the loader; until then that path should be treated as unwired, not as green.

Loading is only half the story

This branch composes Bogota on Osaka, whereas the fixtures compose it on Amsterdam, so 96 of the 159 cases are still rejected at BlockLevelAccessListHashNotEnabled before they execute. That fork-composition question is deliberately left open here.

Frame targets under EIP-7702 delegation

With Bogota composed on Amsterdam for measurement, the delegated-target cases fail on every branch tried, including one carrying devnet-8 gas and the frame entry charge. ExecuteFrame resolves the target's code with

CodeInfo codeInfo = _codeInfoRepository.GetCachedCodeInfo(resolvedTarget, spec, out _);

which follows the delegation for code but discards the delegation address, so the delegation target's access cost is never charged. Affects test_delegated_target_entry_charge (cold and warm), test_delegated_to_precompile_target and test_verify_frame_delegated_to_precompile_target.

The EIP-8141 fixture suites declare their network as Bogota, which
SpecNameParser did not map, so every file failed to load.

The failure surfaced as an unrelated Hash256 conversion error because
ConvertToBlockchainTests wrapped both deserialization and conversion in
the HalfBlockchainTestJson shape fallback: the unmapped fork name threw
during conversion, and the retry against the trimmed shape then failed
on its differently typed postState. Narrow the fallback to
deserialization and name the fork in the exception.
@claude

claude Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Marchhill's task in 5m 19s —— View job


Review: PR #12854

  • Gather context (diff, changed files, base branch)
  • Review SpecNameParser change
  • Review ConvertToBlockchainTests fallback narrowing
  • Review new test file / project wiring
  • Post findings (3 inline comments)

Verdict

The core change is right, and the diagnosis in the commit message checks out. I traced the masking path: HalfBlockchainTestJson.PostState is Hash256 shadowing the base Dictionary<Address, AccountState>?, so a standard-shaped fixture deserializes fine, LoadSpecSpecNameParser.Parse throws bare on Bogota during conversion, and the old catch-all then retried the whole thing against the trimmed shape, where postState as a dict blows up on Hash256. The narrowing is the correct fix.

I also checked the narrowing doesn't lose a load-bearing path. For the fallback to have been useful across conversion, the standard deserialize would have to succeed and the half-shape deserialize succeed on the same JSON — mutually exclusive, since a dict postState fails Hash256 and a scalar postState fails the dict. Convert (line 351) already discriminates via as HalfBlockchainTestJson, so CoerceFromHalf preserving the runtime type still works. Both overloads are non-iterator and eagerly return a List, so exceptions still surface inside FileTestsSource's per-file try and become a FailedToLoadTest rather than aborting a directory. No regression found.

One Medium, on test strength rather than production behaviour. Nothing Critical or High. No security or performance surface — all three files are test-path or a cold parser behind a ConcurrentDictionary cache.

Sev Finding
Medium Parse_maps_Bogota_to_the_frame_transactions_fork asserts IsEip8141Enabled, which Eip8141Prototype also satisfies — the mapping the test names is not actually pinned (inline)
Low catch (Exception) still discards the standard-shape error, so a fixture matching neither shape is still reported only against the trimmed type (inline)
Low Exception message interpolates specName but the switch matches unambiguousSpecName (inline)
Low Rationale comment should be <remarks> per AGENTS.md, applies to both overloads but sits on one, and is phrased as change history (inline)
Low Assert.Throws<T> returns TActual? under <Nullable>enable</Nullable> → CS8600; sibling test uses ! (inline)
Low No drift test — the recurring failure mode is the switch diverging from Nethermind.Specs.Forks, and this project already has the reflection pattern for it in ChainSpecHardforkLabelTests.ForkFor (inline)

Two things about scope, for the reviewer

Neither is a defect — both follow from decisions the PR body already states — but they change what "loads" means here.

No fixture class is registered. Ethereum.Blockchain.Pyspec.Test/Tests.cs has no BogotaBlockchainTests, and Constants.DEFAULT_ARCHIVE_NAME is still fixtures_glamsterdam-devnet.tar.gz. So nothing in CI exercises the new switch arm beyond the unit test, and the 96 BlockLevelAccessListHashNotEnabled rejections in the Remarks can't turn CI red. Given Bogota() : NamedReleaseSpec<Bogota>(Osaka.Instance) versus the fixtures' Amsterdam composition, holding the registration back is the right call — worth stating explicitly so a later reader doesn't assume the suite is wired.

The description contradicts itself. "all 159 blockchain cases load and execute" sits against Remarks' "96 of the 159 cases are still rejected … before they execute." The Remarks version is the accurate one; the summary line reads as a stronger claim than the branch supports.

What I could not verify

Build and test execution were blocked — dotnet build on Nethermind.Specs.Test requires approval in this environment, so both findings about that project are static reads. I'm confident on the CS8600 (NUnit 4's Throws<TActual> returns TActual?; the csproj sets <Nullable>enable</Nullable> with no TreatWarningsAsErrors, so it warns rather than breaks the build) and on name resolution (SpecNameParser resolves through the enclosing Nethermind.Specs namespace without an explicit using). If you want the suite actually run, re-trigger with dotnet in --allowedTools.

Blocking on the Medium only in the sense that the new test doesn't yet guard what its name promises — a one-line change to Is.SameAs(Bogota.Instance).
· branch claude/eip8141-fixture-suite

Comment thread src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs Outdated
Comment thread src/Nethermind/Nethermind.Specs/SpecNameParser.cs Outdated
Comment thread src/Nethermind/Nethermind.Specs.Test/SpecNameParserTests.cs Outdated
Bind both deserialization failures so a fixture matching neither shape reports
both causes, and include the substituted fork name in the parser error.
@Marchhill
Marchhill marked this pull request as ready for review August 18, 2026 17:43
@Marchhill
Marchhill requested review from AnkushinDaniil and wurdum and removed request for smartprogrammer93 August 18, 2026 17:44
@claude

claude Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Marchhill's task in 2m 59s —— View job


Review: PR #12854 (follow-up round)

  • Re-read the three changed files at HEAD
  • Verify the Merge+9999Paris+9999 substitution claim
  • Check the AggregateException change against callers
  • Check test coverage / project wiring
  • Post findings (1 inline comment)

Verdict

All three points from the last round are addressed, and I re-verified each rather than taking the replies at face value:

  • Is.SameAs(Bogota.Instance)NamedReleaseSpec<TSelf>.Instance (NamedReleaseSpec.cs:91) is a static auto-property initialized once per closed type, and SpecNameParser.Parse returns exactly that object, so reference equality is a stable, exact contract. The test now fails if "Bogota" is mapped to Eip8141Prototype or any other 8141-bearing spec — which the old IsEip8141Enabled assertion did not.
  • Merge+9999Paris+9999 — traced through the substitution chain: "Merged" doesn't match, "Merge""Paris" yields "Paris+9999", which hits no switch arm. So the new test case genuinely exercises the specName != unambiguousSpecName branch, and the "NotAFork" case pins the collapsed single-name form. Both assertions are load-bearing.
  • Both causes chainedFileTestsSource.cs:39 formats the failure as $"Failed to load: {e}", and AggregateException.ToString() prints every inner, so both shape errors actually reach the FailedToLoadTest string. The fix is real, not just structural.

I also re-checked the narrowing for a lost path, since the follow-up commit restructured it: ConvertToBlockchainTests(Dictionary<...>) is still called outside both catch blocks, both overloads still return an eagerly-built List, and Convert (line 351) still discriminates via as HalfBlockchainTestJson, so CoerceFromHalf preserving the runtime type continues to work. No behavioural regression found.

On the declined drift test: the reasoning holds up. ChainSpecHardforkLabelTests.ForkFor is keyed on IHardforkLabel/HardforkLabels.All, not on the Forks namespace, so it isn't reusable as-is — and Eip8141Prototype does exist in Nethermind.Specs/Forks/ as an unmapped NamedReleaseSpec, so the skip set would need three entries on day one. Deferring that to its own PR is the right call.

Nothing Critical, High, or Medium. One Low, on the shape this round left behind.

Sev Finding
Low The chained-exception rewrite duplicates a 17-line try/catch verbatim across both overloads; the string overload has one caller and could just forward (inline)
Low No regression test for the new two-cause path — see below
Low NeitherShapeMessage const sits between methods rather than with _serializer/_overriddenSpecs (inline)

On the missing test: the PR body's claim — "A fixture matching neither shape now surfaces both causes instead of discarding the standard-shape one" — is the one assertion in this PR with no test behind it. Ethereum.Basic.Test/BlockchainTestConversionTests.cs is already the home for exactly this (it calls ConvertToBlockchainTests(string) with inline JSON), so it's a short addition: feed JSON that matches neither shape, assert AggregateException.InnerExceptions has both. Worth doing, since the whole point of the change is diagnostic output that nothing else exercises.

What I could not verify

Build and test execution are still blocked in this environment — dotnet build on Nethermind.Specs.Test requires approval, so the CS8600 fix (! suffix) and the new [TestCase] parameters are static reads, not a green run. Re-trigger with dotnet in --allowedTools if you want the suite actually executed.

Not blocking. The two scope notes from the last round still stand and are unchanged by this commit: no fixture class is registered in Ethereum.Blockchain.Pyspec.Test/Tests.cs, so the new switch arm is exercised only by the unit test; and the summary's "all 159 blockchain cases load and execute" still reads stronger than Remarks' "96 … are still rejected before they execute."
· branch claude/eip8141-fixture-suite

Comment thread src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs
Both overloads carried the same shape-fallback block verbatim; the string
overload has a single caller, so it can transcode and forward instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant