perf(node): serve redacted blocks from the sealed header - #1391
Open
mattsse wants to merge 2 commits into
Open
Conversation
`eth_getBlockByNumber`/`eth_getBlockByHash` on the redacted RPC went through `EthBlocks::rpc_block(.., false)`, which loads the recovered block — a block cache hit, or a body read with sender recovery and per-transaction hashing on a miss — then collects every transaction hash and RLP-encodes the whole block to fill `size`. `redact_block` immediately threw all of that away: the transaction list became an empty `BlockTransactions::Hashes`, `redact_header` zeroed `size`, and withdrawals collapsed to an empty default. Header-only requests for old blocks also evicted live traffic from the block cache. The response is now built from `sealed_header_by_id`, converted with the same `RpcConvert` the full path used (size 0, since it is zeroed anyway) and paired with an empty uncle list, an empty transaction list, and a withdrawals field derived from the header's withdrawals root — a block carries withdrawals exactly when the header has that root, so the serialized JSON is unchanged. `pending` is already normalized to `latest` before it reaches this path. `eth_coinbase` used `rpc_block_header`, which loads the recovered block the same way, and now reads the beneficiary from `latest_header`.
mattsse
requested review from
0xKitsune,
0xrusowsky,
adityapk00,
klkvr and
legion2002
as code owners
September 3, 2026 21:06
mattsse
commented
Sep 3, 2026
Comment on lines
749
to
757
| let Some(header) = self | ||
| .eth | ||
| .api | ||
| .provider() | ||
| .sealed_header_by_id(id) | ||
| .map_err(internal)? | ||
| else { | ||
| return Ok(raw_null()); | ||
| }; |
Contributor
Author
There was a problem hiding this comment.
it is likely that the block is cache so we should use the get_maybe_block and only on cache miss load the header
The header-only path read the sealed header straight from the provider on every request. The requested block is usually cached, since clients poll `latest`, so resolve the block id to a hash and read the header through `EthStateCache::get_header`: it serves the cached header or the header of the cached full block, and only on a miss loads it from the provider on the cache's blocking task and keeps it for the next request. An unknown hash still returns null.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
eth_getBlockByNumberandeth_getBlockByHashon the redacted RPC loaded the recovered block, collected every transaction hash and RLP-encoded the whole block forsize, then discarded all of it:redact_blockemptied the transaction list andredact_headerzeroedsize. Header-only requests for old blocks also evicted live traffic from the block cache.The response is now built from the header alone: the block id is resolved to a hash and the header is read through the eth state cache, which serves it from the cached header or the cached full block and only loads it from the provider on a miss, then it is converted with the same
RpcConvertas before with size 0, and paired with empty uncles, an empty transaction list and a withdrawals field derived from the header's withdrawals root. A block has withdrawals exactly when its header has that root, so the serialized JSON is unchanged for every block, includinglatest, hash lookups and unknown blocks. The one behaviour change is that a header whose body was pruned now returns the redacted header instead of null, which cannot occur on zone nodes since they retain bodies.eth_coinbasetook the same full-block path and now reads the beneficiary fromlatest_header.