Skip to content

perf(engine): decode payload transactions once - #1

Open
gr8h wants to merge 3 commits into
mainfrom
feat/decode-once-payload-issue-20431
Open

perf(engine): decode payload transactions once#1
gr8h wants to merge 3 commits into
mainfrom
feat/decode-once-payload-issue-20431

Conversation

@gr8h

@gr8h gr8h commented Aug 14, 2026

Copy link
Copy Markdown
Owner

Decodes execution payload transactions a single time and streams them to payload-to-block conversion, instead of decoding once for execution and again for the block body.

  • convert_payload_to_block_with_tx_stream: opt-in streaming conversion on PayloadValidator; default falls back to full decode.
  • Streaming path assembles the body from decoded transactions; disconnect falls back to re-decoding raw bytes.
  • Block-hash check runs before waiting on the stream, so hash mismatches report latestValidHash: null per the engine API even when a transaction is malformed.
  • Header tx-root is never reused for the pre-execution root check (would compare a value to itself); re-encoding check retained, with a test locking the invariant.
  • Tests: decode-error reporting, hash-mismatch precedence, abort-does-not-hang with block equivalence against the non-streaming path.

Refs upstream issue paradigmxyz#20431.

https://claude.ai/code/session_01QBN5jDc2WdmkaqM1r2GqTL

gr8h added 3 commits August 13, 2026 14:10
Ref paradigmxyz#20431 (item 3)

Payload transactions were RLP-decoded twice per newPayload: once in the
conversion thread and again in the execution-side tx iterator. The iterator is
now the single decoder: it streams each decoded transaction over a bounded
channel (capacity = tx count, sends never block) into payload conversion, which
assembles the block body from the stream and returns the transactions root
computed from the raw payload bytes, skipping the per-tx re-encode in the
pre-execution root check.

Opt-in via two defaulted PayloadValidator methods; non-Ethereum implementations
keep the existing double-decode behavior unchanged. If the stream disconnects
before all transactions arrive (malformed tx, aborted execution, early return),
conversion falls back to decoding the retained raw bytes, reproducing the
existing errors. The block hash is now validated before transaction decoding
(engine-API step 1); hive engine-api showed zero delta for this reordering.

Deviations from the handoff spec (.scratch/decode-once/spec.md): the sender is
also dropped in ensure_ok!/early-return paths because LazyHandle::try_into_inner
blocks (spec assumed scope-drop suffices); the tx-root-mismatch test is
uncraftable since V1 payloads carry no root field (root is derived from the raw
bytes), so root correctness is locked by a seam test instead.

Claude-Session: https://claude.ai/code/session_01BaDayhJk8crEcAQFxmvQF4
Encode the drop-sender-before-join invariant in a PendingValidatedBlock
wrapper instead of three manual drops, define the body tx sender alias via
the canonical PayloadTxStreamSender, and deduplicate the fork-field checks
between the streaming and non-streaming payload validators.

Claude-Session: https://claude.ai/code/session_01H8bWdbHjv1fuUGgyR3TZcT
The header-derived transactions root always matches the raw bytes it was computed from, so reusing it in the pre-execution check would compare a value to itself and stop rejecting non-canonical transaction RLP. Return a plain SealedBlock, keep the re-encoding check, and lock the invariant in with tests. Also dedupes the body_tx send sites into a helper.

Claude-Session: https://claude.ai/code/session_01QBN5jDc2WdmkaqM1r2GqTL
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.

1 participant