rpc/jsonrpc, rpc/gasprice: gate block-data and replay endpoints on the data they read - #23760
Conversation
…e data they read Follow-up to #23322, same defect class as #21965: an endpoint that gates on state history refuses data that was never pruned away. 1. The endpoints that need a block body and nothing else -- debug_getRawBlock, debug_getRawTransaction and erigon_getBlockByTimestamp -- gated on Mode.History, so under --prune.mode=blocks they refused bodies the node still holds. They now gate on Mode.Blocks. 2. eth_feeHistory had no gate at all. The base-fee and gas-used series come from headers, which no retention shape takes away, but reward percentiles are computed from each block's transactions and the gas their receipts report. The oracle now checks the oldest block of the resolved range through a new OracleBackend method when percentiles are requested, so a range reaching below retention answers with the prune error instead of a truncated result. 3. The endpoints that replay a block read its transactions on top of the state history preceding it, and gated on history alone: the trace and debug tracing entry points (trace_block, trace_transaction, trace_filter, trace_replayTransaction, trace_replayBlockTransactions, debug_traceBlockBy*, debug_traceTransaction), the Otterscan searches, and the Otterscan tracer helper. They now gate on both boundaries. debug_traceCall, trace_call, trace_callMany and trace_rawTransaction execute against the state a block leaves behind and stay on history. The endpoint table gains rows for the header, block-data, fee-history, trace and Otterscan-search endpoints, plus a prune-mode row where the blocks boundary is stricter than the history one (--prune.mode=archive --prune.distance.blocks=N). That row is what makes the blocks leg observable; it also showed the two by-address log rows were declared as history-only when the query reads bodies as well.
…ata_endpoints # Conflicts: # rpc/jsonrpc/check_prune_gates_test.go
There was a problem hiding this comment.
🟡 Changes recommended
Timestamp and Otterscan edge paths still apply the blocks gate to the wrong effective block.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Updates RPC pruning gates to match the block, receipt, or state data each endpoint reads.
Changes:
- Gates block-data and replay endpoints on correct retention boundaries.
- Adds receipt availability checks for fee-history rewards.
- Expands prune-mode endpoint coverage.
File summaries
| File | Description |
|---|---|
rpc/jsonrpc/tracing.go |
Gates debug replay endpoints on blocks and history. |
rpc/jsonrpc/trace_filtering.go |
Updates trace endpoint gates. |
rpc/jsonrpc/trace_adhoc.go |
Updates replay endpoint gates. |
rpc/jsonrpc/prune_gating_test.go |
Expands endpoint and prune-mode coverage. |
rpc/jsonrpc/otterscan_api.go |
Adds block availability to Otterscan gates. |
rpc/jsonrpc/eth_system.go |
Exposes receipt availability to the gas oracle. |
rpc/jsonrpc/erigon_block.go |
Changes timestamp lookup to the blocks gate. |
rpc/jsonrpc/debug_api.go |
Corrects raw block and transaction gates. |
rpc/jsonrpc/check_prune_gates_test.go |
Tests fee-history range gating. |
rpc/gasprice/gasprice.go |
Extends the oracle backend interface. |
rpc/gasprice/gasprice_test.go |
Updates the oracle mock. |
rpc/gasprice/feehistory.go |
Gates reward percentile data. |
rpc/gasprice/feehistory_truncate_test.go |
Updates the truncation test backend. |
Review details
- Files reviewed: 13/13 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…ata_endpoints # Conflicts: # rpc/gasprice/feehistory_truncate_test.go # rpc/gasprice/gasprice.go # rpc/gasprice/gasprice_test.go
erigon_getBlockByTimestamp resolved the block number on three separate paths, each with its own buildBlockResponse, and the blocks gate sat on the last one only. A timestamp at or before the genesis one took the early return and answered with null instead of PrunedError when the retention window prunes genesis. blockNumByTimestamp now resolves the number for all three cases, leaving one gate and one response path. The Otterscan searches passed the paginating blockNum straight to the gate, where 0 is the sentinel for the newest (or oldest) page rather than a request for genesis, so the first page of ots_searchTransactionsBefore failed on archive history with a blocks window. The sentinel is excluded from the entry gate and buildSearchResults gates every block the scan reaches, once per block and before reading its body, so a sparse page cannot cross the cutoff unnoticed.
|
Complexity-only pass: the production diff is mostly a swap between gate helpers that already exist on
net: -23 lines possible. One thing I looked at and would not cut: |
…nel comment Review follow-up. The two SearchTransactionsBefore tests differed only in page size and paid for setupPruneGating twice; one test with two calls keeps both legs and one setup. The blockNum == 0 rationale now lives at SearchTransactionsBefore alone, with a pointer from SearchTransactionsAfter.
There was a problem hiding this comment.
🟡 Changes recommended
The fee-history gate can admit pruned receipt metadata and return incorrect reward percentiles.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 14/14 changed files
- Comments generated: 1
- Review effort level: Balanced
| } | ||
|
|
||
| func (b *GasPriceOracleBackend) CheckBlockReceiptsAvailable(ctx context.Context, blockNumber uint64) error { | ||
| return b.baseApi.checkBlockReceiptsAvailable(ctx, b.tx, blockNumber) |
The reward percentiles read the gas used through GetReceiptsGasUsed, which resolves it from ReceiptDomain (retired with state history) and never from RCacheDomain, so gating on the receipt-cache retention let a range below the history cutoff through and resolved its gas used from pruned history. Gate on blocks plus history instead, and rename the backend method so it names the data the oracle actually reads.
Follow-up to #23322, same defect class as #21965: an endpoint that gates on state history refuses data that was never pruned away.
debug_getRawBlock,debug_getRawTransactionanderigon_getBlockByTimestampread the body and nothing else, so they now gate onMode.Blocksinstead ofMode.History.eth_feeHistoryhad no gate. Headers carry the base-fee and gas-used series, so only the reward-percentile path is gated, on the oldest block of the resolved range, via a newOracleBackend.CheckBlockReceiptsAvailable.trace_block,trace_transaction,trace_filter,trace_replayTransaction,trace_replayBlockTransactions,debug_traceBlockByNumber,debug_traceBlockByHash,debug_traceTransaction, the Otterscan searches and the Otterscan tracer helper. They now gate on both boundaries. The*_callendpoints execute against the state a block leaves behind and stay on history.In the named presets history is always the stricter boundary, which is why the missing blocks leg stayed invisible. It shows up with
--prune.mode=archive --prune.distance.blocks=N.Tests: the prune-gating table gains rows for the header, block-data, fee-history, trace and Otterscan-search endpoints, plus an
archive_blocks_windowmode row — without a mode where blocks is stricter than history, the blocks leg cannot be observed.