Skip to content

Implement EIP-7805 (FOCIL)#8003

Draft
Marchhill wants to merge 194 commits into
masterfrom
feature/eip-7805
Draft

Implement EIP-7805 (FOCIL)#8003
Marchhill wants to merge 194 commits into
masterfrom
feature/eip-7805

Conversation

@Marchhill
Copy link
Copy Markdown
Contributor

@Marchhill Marchhill commented Jan 3, 2025

Changes

  • Implement EIP-7805
  • engine api changes
    • implement engine_getInclusionList
    • add inclusion lists to PayloadAttributes and newPayload parameters
  • validate that valid IL txs were included up to gas limit (see here)
  • use IL in block building

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

Documentation

Requires documentation update

  • Yes
  • No

Requires explanation in Release Notes

  • Yes
  • No

Marchhill and others added 2 commits June 3, 2026 13:58
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The spec evaluates the inclusion-list rule against state after all block.transactions
have executed, not the parent (pre-execution) state. Checking against parent state
is stricter than spec and would refuse blocks accepted by other clients.

- InclusionListValidator.IsSatisfied now takes IAccountStateProvider (post-exec)
- Drop the snapshot-capture / two-phase InclusionListValidation token
- Collapse IBlockValidator.BeginInclusionListValidation + ValidateInclusionList
  into a single CheckInclusionList(processed, suggested, worldState, options) hook
- BlockProcessor calls the hook once after ValidateProcessedBlock
- Tests switch to IAccountStateProvider via TryGetAccount stubs and the
  same-nonce replacement case is inverted to match the new (spec) semantics
@Marchhill
Copy link
Copy Markdown
Contributor Author

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jun 3, 2026

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


EIP-7805 (FOCIL) PR Review — Round 8

Tasks:

  • Load repo rules and context
  • Read key changed files (new commits since Round 7)
  • Check Round 7 Low findings
  • Post inline comments
  • Write summary

Progress since Round 7

Both Round 7 Low findings are resolved cleanly (commit 5b933956):

Round 7 finding Status
Stackalloc threshold 128 < MaxTransactionsPerInclusionList 256 (Low) ✅ Fixed — threshold raised to Eip7805Constants.MaxTransactionsPerInclusionList
Dead ValidateInclusionList stub in BalRecordingBlockProcessorTests.StubBlockProcessor (Low) ✅ Removed

New commits reviewed

Commit Summary Issues
5b933956 Drop orphan IL stub + raise stackalloc threshold ✅ Resolves Round 7 Lows
a0cfe7f5 tidy ✅ No issues — removes now-superseded comments and dead code
d1a2b167 Encapsulate IL validation lifecycle behind IBlockValidator ✅ Clean architectural refactor; two-phase BeginInclusionListValidation/Commit moves IL plumbing off BlockProcessor
84df88dd Doc: BeginInclusionListValidation two-phase contract ✅ Documentation only
43dfec6a Revert IL satisfaction check to post-execution state ⚠️ See Medium finding below

Remaining issues

Medium

IL satisfaction uses post-execution state — spec citation needed (third reversal)discussion_r3349038010

Commit 43dfec6a reverts IsSatisfied from the pre-execution (parent state + included IL txs) approach implemented in Rounds 3–4 back to fully post-execution state (all block transactions applied). The commit message claims this is what the spec requires. The test Same_nonce_replacement_tx_advances_nonce_and_satisfies_il explicitly documents the consequence: a builder who includes a same-nonce replacement tx from an IL sender can advance that sender's post-execution nonce, making the original IL tx appear non-appendable, and the block is accepted.

If the EIP-7805 spec genuinely uses post-execution state for the appendability check, this is correct and my Rounds 2–4 analysis was wrong. Given this is the third position change on the same question, a direct spec reference (EIP-7805 §Execution Layer or execution-apis PR #609) confirming post-execution semantics is needed before merge to close the loop definitively.

Low

Stale doc comment on engine_newPayloadV6discussion_r3349036216

EngineRpcModule.Bogota.cs:29–32 still says "validated against the parent-block state snapshot" — this was updated in Round 7 to match the then-current pre-execution implementation, but after 43dfec6a reverted to post-execution, the comment is wrong again. One-line suggestion attached to the inline comment.


Verdict: not mergeable. The Medium finding (spec citation for post-execution IL semantics) needs to be resolved: either add a direct spec reference confirming post-execution is correct, or revert to the pre-execution approach if the spec text says otherwise. The Low (stale doc) is a one-line fix.

Comment thread src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Bogota.cs
@Marchhill
Copy link
Copy Markdown
Contributor Author

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jun 4, 2026

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


EIP-7805 (FOCIL) PR Review — Round 9

Tasks:

  • Load repo rules and context
  • Read key changed files (new commits since Round 8)
  • Check Round 8 findings (stale doc comment Low, spec citation Medium)
  • Review new commits for regressions or new issues
  • Write summary

Progress since Round 8

Both Round 8 findings are resolved:

Round 8 finding Status
Post-execution state for IL check: spec citation needed (Medium) ✅ Closed — Marchhill confirmed post-execution is correct per EIP-7805 §Execution Layer; doc comment updated in d6d444b0
Stale doc comment in EngineRpcModule.Bogota.cs "parent-block state snapshot" (Low) ✅ Fixed in d6d444b0 — now reads "post-execution state"

New commits reviewed

Commit Summary Issues
d6d444b0 Refactor: throw InclusionListUnsatisfiedException instead of flag ✅ Clean — removes mutable InclusionListUnsatisfied flag from Block.cs; exception correctly propagates through ProcessBranch (which only catches InvalidBlockException) up to ProcessBlocks; InclusionListUnsatisfiedException : BlockchainException is a sibling of InvalidBlockException, not a subtype, so no accidental catch-widening
96e72c1a Remove commented-out specprovider test ✅ No issues
02fdfd11 Use ArrayPoolList for oversized IL bitmap and reservoir sampler new ArrayPoolList<bool>(il.Length, il.Length) zeroes the rented buffer via AsSpan(0, startingCount).Clear() before use; ReduceCount correctly trims null tail entries; try-finally in DecodeTransactionsUpToLimit guarantees ArrayPoolList<Transaction> disposal even on early exit or exception via enumerator Dispose()
11bdfcfa Tidy test comment ✅ No issues
672b1955 Simplify IEngineRpcModule.Bogota.cs JSON-RPC descriptions ✅ No issues
5b358b9e + f744cbc1 Tidy/whitespace in MergePlugin.cs ✅ No issues

Verdict: mergeable. All Critical, High, and Medium findings across all nine review rounds are resolved. No new issues found in the latest commits.

@Marchhill
Copy link
Copy Markdown
Contributor Author

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jun 5, 2026

Claude finished @Marchhill's task in 9m 29s —— View job


EIP-7805 (FOCIL) PR Review — Round 10

Tasks:

  • Load repo rules and context
  • Read new commits since Round 9
  • Check for any new issues
  • Post inline comments
  • Write summary

Progress since Round 9

Round 9 reviewed up to f744cbc1. The following commits have landed since then (daf80efe was present at Round 9 but not listed in that review's table):

Commit Summary Issues
daf80efe cleanup and optimise: raise parallel threshold to 16, use ArrayPoolList for slots, remove _rebuildSlots / GetPayloadBuildCount from PayloadPreparationService ✅ No issues
c23c8ef1 optimisations: O(N+M) IL hash-lookup, sender-state dedup cache, Random.Shared in InclusionListBuilder ✅ No issues
42de652c reduce allocations: plain Transaction[] slots (in-place compact), Transaction[] reservoir, drop ArrayPoolList in InclusionListValidator ✅ No issues
53059f63 simplify IL decoding: InclusionListDecoder.DecodeAndRecover delegates entirely to TxsDecoder.DecodeTxs + RecoverSignatures.RecoverData(skipErrors: true) ⚠️ See Low finding
4022f386 remove RecoverOne (no longer needed after 53059f63) ✅ No issues
99df16c3 txsdecoder reuse: ExecutionPayload.TryGetTransactions now uses TxsDecoder.DecodeTxs; InclusionListTxSource reverts to lazy decoder init (avoids null-dep throw in test stubs) ✅ No issues

The 53059f63 simplification is a meaningful improvement: it removes ~90 lines of custom parallel decode/recover logic from InclusionListDecoder and reuses the battle-tested TxsDecoder + RecoverSignatures infrastructure that serves normal block transactions.


Remaining issues

Low

skipErrors: false for IL-tx recovery in the block preprocessordiscussion_r3362195537

ExecutionPayloadV3.TryGetBlock() decodes IL transactions via TxsDecoder.DecodeTxs(..., skipErrors: true) — decode-only, no sender recovery — so all IL txs enter block processing with SenderAddress = null. The block preprocessor (RecoverSignatures.RecoverData(Block)) then calls RecoverData(block.InclusionListTransactions, spec) with skipErrors defaulting to false. Because txs[0].SenderAddress is null, the early-return optimisation does not fire and recovery is attempted for every IL tx.

For a well-formed IL this is harmless. For an adversarially crafted engine_newPayloadV6 with an IL tx that has valid RLP but an invalid signature, ECDSA recovery throws, block processing fails with an exception, and the CL receives UnknownBlockError instead of a clean INVALID status. The skipErrors parameter added in 53059f63 was purpose-built for this case; it just wasn't applied to this call site:

RecoverData(block.InclusionListTransactions, spec, skipErrors: true);

Verdict: mergeable. All Critical, High, and Medium findings from Rounds 1–9 remain resolved. The sole remaining Low item is a one-word fix that converts an adversarial-input exception path into a clean INVALID response.

Comment thread src/Nethermind/Nethermind.Consensus/Processing/RecoverSignatures.cs Outdated
Marchhill and others added 4 commits June 5, 2026 12:29
The Zisk stateless guest builds with --no-pthread, so System.Threading.Tasks.Parallel
isn't available. The earlier dedup that routed TryGetTransactions through TxsDecoder
introduced a reference to Parallel.For (via TxsDecoder.DecodeParallel) into the
stateless code path, breaking all 7 stateless EF test inputs in CI.

Revert TryGetTransactions to the inline serial loop with a remarks block explaining
why. TxsDecoder's parallel path stays available for host-side callers that need it
(InclusionListDecoder, etc.) and ExecutionPayloadV3's IL decode still uses it,
which is safe because SSZ stateless input has no InclusionListTransactions field
(so that branch is skipped via the null check).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ctions

The Zisk stateless guest builds with --no-pthread and a stripped BCL that lacks
System.Threading.Tasks.Parallel. The earlier dedup of ExecutionPayload.TryGetTransactions
onto TxsDecoder pulled Parallel.For into the stateless code path, breaking all 7
EF stateless test inputs at type-load time.

Wrap TxsDecoder's parallel branch + DecodeParallel helper + Parallel using in
#if !ZK_EVM so the stateless DLL only links the serial path. ZK_EVM is the
project-wide preprocessor symbol set when EnableZkEvm=true (see Directory.Build.props),
which the Zisk guest Makefile passes.

Then re-dedup TryGetTransactions to delegate to TxsDecoder.DecodeTxs — safe in both
flavors: server gets parallel for ≥16-tx blocks, stateless gets serial. Verified by
inspecting the stateless-flavor Nethermind.Serialization.Rlp.dll (no Parallel
references) vs the server-flavor (DecodeParallel, ParallelLoopState, etc. present).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ExecutionPayloadV3.TryGetBlock decodes IL transactions via TxsDecoder.DecodeTxs
with skipErrors:true (decode only, no sender recovery). The recovery happens later
in RecoverSignatures.RecoverData(Block), which was passing skipErrors=false for IL.

An adversarial engine_newPayloadV6 carrying a syntactically-valid IL tx with an
invalid signature would surface as a JSON-RPC exception instead of cleanly leaving
SenderAddress=null and letting the IL validator treat it as not-appendable. Pass
skipErrors:true for the IL recovery only — block.Transactions stays default
(bad sig in a block tx still invalidates the block, as expected).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The earlier wiring had three #if !ZK_EVM blocks (using, threshold + dispatch,
DecodeParallel method). Restructure so:

- DecodeTxs becomes a 4-line dispatcher: TryDecodeParallel ?? DecodeSequential
- DecodeSequential is named & unconditional
- A single #if/#else block at the bottom holds either the real TryDecodeParallel
  (with embedded threshold check + Parallel.For body) or a stub returning false
- using System.Threading.Tasks dropped; Parallel.For fully-qualified at its one
  call site, removing the need for a conditional using directive

Verified the stateless-flavor Nethermind.Serialization.Rlp.dll has zero
System.Threading.Tasks references (only the stub method name 'TryDecodeParallel'
appears as a string); server-flavor has DecodeParallel, ParallelLoopState,
System.Threading.Tasks.Parallel, ParallelLoopResult as expected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Marchhill
Copy link
Copy Markdown
Contributor Author

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jun 5, 2026

Claude finished @Marchhill's task in 3m 54s —— View job


EIP-7805 (FOCIL) PR Review — Round 11

Tasks:

  • Load repo rules and context
  • Read new commits since Round 10
  • Verify Round 10 Low finding is resolved (skipErrors: true for IL recovery)
  • Review new commits for regressions or new issues
  • Write summary

Progress since Round 10

The Round 10 Low finding has been resolved cleanly (commit f737c3d4):

Round 10 finding Status
skipErrors: false for IL-tx recovery in the block preprocessor (Low) ✅ Fixed — RecoverData(block.InclusionListTransactions, spec, skipErrors: true) with catch narrowed to InvalidDataException | ArgumentException | CryptographicException, logged at Trace

New commits reviewed

Commit Summary Issues
1df99cf0 Intermediate fix: revert TryGetTransactions to serial-inline after ZK_EVM stateless test failures ✅ No issues (superseded by f79494c5)
f79494c5 Gate TxsDecoder parallel branch behind #if !ZK_EVM; re-dedup TryGetTransactions onto TxsDecoder.DecodeTxs ✅ No issues — ZK stub returns false, causing DecodeTxs to fall back to DecodeSequential; stateless flavor retains no System.Threading.Tasks reference
1a871be7 Collapse to a single #if/#else by extracting DecodeSequential as a named, unconditional method ✅ Clean structural refactor — ParallelDecodeThreshold const and TryDecodeParallel live inside the same #if !ZK_EVM block, keeping the ZK stub minimal
f737c3d4 RecoverData: pass skipErrors: true for IL txs in the block preprocessor ✅ Resolves Round 10 Low — adversarial IL tx with valid RLP but bad signature now leaves SenderAddress = null rather than surfacing a JSON-RPC error

The TryDecodeParallel implementation is correct on both error paths: !skipErrors + error → stores lowest-index error via lock(errorGate), calls state.Stop(), returns the error string (null slots never observed); skipErrors + errors → exceptions caught per-worker, null slots compacted in a final sequential pass before returning.


Verdict: mergeable. All Critical, High, and Medium findings across all 11 review rounds are resolved. No new issues found in the latest commits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bogota eip new feature taiko related to the taiko alethia rollup

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants