Skip to content

rpc/jsonrpc: two review follow-ups from #23322 - #23759

Merged
AskAlexSharov merged 5 commits into
mainfrom
lupin012/logs_gate_and_prune_gating_fixtures
Sep 4, 2026
Merged

rpc/jsonrpc: two review follow-ups from #23322#23759
AskAlexSharov merged 5 commits into
mainfrom
lupin012/logs_gate_and_prune_gating_fixtures

Conversation

@lupin012

@lupin012 lupin012 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #23322. #23690 closed seven of its deferred review notes; this closes the
two P3 ones it left, one per commit.

Note Site Change
r3880873445 eth_api.go a log query no longer inherits the post-state requirement of a full receipt
r3880873497 prune_gating_test.go the fixtures persist the receipts their retention promises

Logs

checkLogsAvailable reused the full-receipt gate, which refuses a pre-Byzantium block
below the history cutoff: a full receipt carries a post state the cache does not store
and only a re-execution can recompute. A log query never reads that field — getLogsV3
asks GetReceipt for a receipt without a post state, which is exactly why the
generator serves the cache. So with receipts kept beyond history, an unfiltered
pre-Byzantium query was refused although every byte it reads was retained.

checkReceiptsAvailable keeps the post-state leg; the rest moves unchanged into
checkReceiptSourceAvailable. checkLogsAvailable composes the blocks leg with that
one and keeps the history leg behind usesLogIndex, since an indexed filter still
searches LogAddrIdx/LogTopicIdx. Filtered queries are unaffected, and
overlay_getLogs gates on checkBlockHistoryAvailable and is untouched.

eth_capabilities is unchanged on purpose: caps.Logs is already
stricterRetention(receipts, history), so the post-state clamp was a no-op for it.
TestCapabilitiesAgreeWithTheLogsGatePreByzantium pins that the advertised boundary
does not move — the gate now reaches further back than advertised, never short of it.

Fixtures

They force-wrote PersistReceipts after InsertChain and never enabled RCacheDomain,
so a disabled domain dropped everything execution wrote: the keep-all cells passed by
re-executing against unpruned history. The domain is now enabled before the chain runs,
and requirePersistedReceipts asserts the receipts really are on disk.

Tests

Both commits are TDD; the fixture one first failed with "[]" should have 1 item(s), but has 0. New: TestLogsGateSkipsThePostStateLegPreByzantium (one block, three answers),
TestCapabilitiesAgreeWithTheLogsGatePreByzantium,
TestReceiptEndpointsCloseWhenTheCacheIsNotServed. go test -count=1 ./rpc/jsonrpc/...
green, -race green on the gate tests, make lint clean.

Not in this PR

The fixture note also asked for an endpoint test with history physically unavailable.
A negative control confirms the gap is real: the "endpoint answers" leg passes even with
zero persisted receipts. Real pruning (WithStepSize + WithPruneMode) is the right
route, but it reverses this fixture's premise of inserting without physical pruning, so
it wants its own PR.

The fixtures force-wrote PersistReceipts after InsertChain and never enabled
RCacheDomain, so a disabled domain dropped everything execution wrote. The cells
asserting that a keep-all retention serves an old block passed by re-executing
against physically unpruned history, which the retention promises nothing about,
and a regression in the cache path went unnoticed.

The domain is now enabled before the chain runs, and requirePersistedReceipts
asserts the receipts really are on disk. TestReceiptEndpointsCloseWhenTheCacheIsNotServed
adds the endpoint counterpart of the gate-level test: the retention opens the
block only while the cache is served.

Still open from the same review note: an endpoint test with history physically
unavailable, so a re-execution cannot answer at all. It needs step-sized
aggregation and a prune mode applied at insert time, which reverses this
fixture's premise of inserting without physical pruning.
checkLogsAvailable reused the full-receipt gate, which refuses a pre-Byzantium
block below the history cutoff because the post state a full receipt carries is
not stored and has to be recomputed by re-executing. A log query never reads
that field: getLogsV3 asks GetReceipt for a receipt without a post state, and
the generator serves the persistent cache exactly because of it. So with
receipts kept beyond history an unfiltered pre-Byzantium query was refused
although every byte it reads was retained.

checkReceiptsAvailable keeps the post-state leg and the rest moves unchanged
into checkReceiptSourceAvailable, which gates on where the receipts come from
whatever fields the caller reads. checkLogsAvailable composes the blocks leg
with that one, and keeps the history leg behind usesLogIndex: an indexed filter
still searches LogAddrIdx and LogTopicIdx, retired at the history cutoff.

eth_capabilities is unchanged. caps.Logs is already the stricter form describing
an indexed query, so the post-state clamp was a no-op for it: the advertised
boundary stays at the history cutoff and the gate now reaches further back than
advertised, never short of it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The implementation matches generator behavior and includes focused regression coverage.

Pull request overview

Separates log availability from full-receipt post-state requirements and strengthens receipt-cache fixtures.

Changes:

  • Adds a receipt-source gate for log queries.
  • Enables and verifies persisted receipt fixtures before execution.
  • Adds pre-Byzantium and cache-disabled regression tests.
File summaries
File Description
rpc/jsonrpc/eth_api.go Separates receipt-source and full-receipt gating.
rpc/jsonrpc/prune_gating_test.go Persists and verifies fixture receipts.
rpc/jsonrpc/check_prune_gates_test.go Covers cache and pre-Byzantium boundaries.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@lupin012
lupin012 marked this pull request as ready for review September 3, 2026 17:17
@lupin012
lupin012 requested a review from yperbasis as a code owner September 3, 2026 17:18
@AskAlexSharov
AskAlexSharov added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit 613a394 Sep 4, 2026
138 checks passed
@AskAlexSharov
AskAlexSharov deleted the lupin012/logs_gate_and_prune_gating_fixtures branch September 4, 2026 03:20
pull Bot pushed a commit to Dustin4444/erigon that referenced this pull request Sep 6, 2026
…#23322 (erigontech#23812)

Follow-up to erigontech#23322. erigontech#23690 closed seven of its deferred review notes
and erigontech#23759 two more; this closes the three that are about
`RemoteBlockReader.FrozenBlocks` and the mechanism behind it.

| Note | Site | Change |
| --- | --- | --- |
|
[r3885705116](erigontech#23322 (comment))
| `block_reader.go` | a slow backend no longer costs every caller a full
timeout |
|
[r3885704185](erigontech#23322 (comment))
| `block_reader.go` | the zero handed out before the backend answers is
no longer read as "no snapshots" |
|
[r3885706881](erigontech#23322 (comment))
| `block_reader.go`, `eth_api.go` | one shared TTL-cache instead of two
hand-rolled ones |

## Change

- The refresh runs on a goroutine of its own; a caller that has a value
to serve returns without waiting for the next one. A failed attempt is
stamped like a successful one, so an unreachable backend costs one
attempt per TTL instead of one per caller and per request.
- `FrozenBlocksObserved() (uint64, bool)` reports the count together
with whether the backend ever answered. `receipts.PostStateCalculated`
and the `eth_simulateV1` commitment path both read it; those are the two
sentinel readers a remote reader can reach.
- `common/concurrent.CachedValue[T]` holds what the getter and
`holdsPreMergeBlockData` each kept by hand: one TTL, one dedup, one rule
for what a failed pass leaves behind. Each site keeps its own waiting
policy — `Produce` runs on the caller's goroutine, `Go` refreshes behind
it.

## For review

**`singleflight` was tried and dropped.** `DoChan` cannot join the pass
in flight without possibly starting one of its own, and it runs the
producer on a goroutine the caller does not own. That is unsafe for the
pre-merge probe, which reads through the caller's `kv.Tx`: a caller
released by `ctx.Done()` rolls that transaction back under a live
reader. `Produce` runs the pass on the caller's goroutine instead.
`DoChan` also turns a producer panic into `go panic(e)`, which ends the
process rather than the request.

**An unobserved count now reads as "snapshots exist".** For a
pre-Byzantium block on a remote rpcdaemon whose backend has not answered
yet, `PostStateCalculated` returns false, so the stored receipt is
served — possibly without its `root` field — where before the block was
re-executed and the gate could answer `PrunedError` for receipts that
are on disk. That is the direction the note asks for, and the window is
one TTL after a failed round trip.

**Panics.** `Produce` publishes the failed pass and re-panics, so the
RPC server logs it as before; `Go` contains and logs it with the stack,
since its goroutine has no caller to recover it.

## Tests

`common/concurrent/cached_value_test.go` (11) pins the contract,
including that a caller running the pass finishes it before returning
while one that only waits is released by its own context.
`TestRemoteBlockReaderFrozenBlocks*` cover the getter,
`rpc/jsonrpc/receipts/post_state_calculated_test.go` the sentinel. The
`eth_simulateV1` leg has no dedicated test — the branch sits inside the
commitment path.

`make lint` clean; `./common/concurrent/...`,
`./db/snapshotsync/freezeblocks/...`, `./rpc/jsonrpc/...`,
`./execution/blockreplay/...`, `./cmd/rpcdaemon/...` green, with `-race`
on the concurrency tests.
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.

3 participants