Skip to content

[r3.5] db/state: CP route receipt domain reads through overlay DomainReader - #22951

Merged
lupin012 merged 3 commits into
release/3.5from
lupin012/backport_receipt_overlay_domain_reader_35
Aug 4, 2026
Merged

[r3.5] db/state: CP route receipt domain reads through overlay DomainReader#22951
lupin012 merged 3 commits into
release/3.5from
lupin012/backport_receipt_overlay_domain_reader_35

Conversation

@lupin012

@lupin012 lupin012 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Cherry-pick of #22511 and #22893 to release/3.5.

release/3.5 serves wrong logIndex values for a block whose commit is in flight. Generator.GetReceipt admits the block through the block overlay (CheckBlockExecuted(g.filters.WithOverlay(tx), …)) but reads its metadata from the committed tx: on a history miss DomainRoTx.GetAsOf falls back to GetLatest, so ReceiptAsOf answers with the previous writing block's final in-block log count, applied to every transaction of the affected block. The value is then cached in the RPC layer, which is why a restart clears it.

Both fixes have been on main since mid-July and neither was ever backported. Reported on #22106 (v3.5.1–v3.5.4, Ethereum mainnet archive), with a detailed root-cause analysis in this comment.

PR Author On main In 3.6 In 3.5
#22511 @Sahil-4555 2026-07-17 yes no
#22893 @mh0lt 2026-07-31 no no

#22893 is a direct follow-up: the code #22511 added gated the DomainReader result on err == nil && ok, so a reader error fell through to the committed tx and reopened the same window. Both are needed, in this order.

Conflict resolution

db/kv/membatchwithdb/memory_mutation.goOverlayTemporalReadView on 3.5 still has AggForkablesTx and Unmarked, removed on main by #22287. Kept the 3.5 methods; they are orthogonal to the fix.

Tests

Neither upstream PR shipped a test for the RPC-visible behaviour, so this adds three, each verified red before its fix and green after:

Note for release

No resync or DB rewind is needed. The wrong values were never persisted — they were computed at RPC time and held in the generator's in-memory cache, which the upgrade's own restart clears. Worth stating, because the early theory on #22106 was a corrupted ReceiptDomain and operators were advised to rewind or resync.

Not verified

No test crosses the whole eth_getLogs path, and nobody has confirmed the fix on a live 3.5 node. The reporter on #22106 has a reproduction that hits 8 times out of 8 — a build of this branch tested against it would settle it.

Sahil-4555 and others added 3 commits August 2, 2026 22:04
…mainReader (#22511)

as per issue #22504 where the node would sometimes serve corrupted
logIndex values for newly-executed blocks. This was because
`rawtemporaldb.ReceiptAsOf` was reading receipt metadata from the
database (stale), rather than the in-memory uncommitted state. Users
find this to be an incorrect log ordering in JSON-RPC receipts
(`eth_getTransactionReceipt`), breaking downstream consumers (indexers,
explorers, bridges) that depend on deterministic log indices.

Receipt generation pulls uncommitted data for newly executed blocks by
calling `rawtemporaldb.ReceiptAsOf`. This translates into `GetAsOf` and
`HistorySeek` calls on the `ReceiptDomain`.

The generator wraps the transaction in a block overlay, but
`MemoryMutation.GetAsOf` and `MemoryMutation.HistorySeek` were going
straight through to the underlying database transaction (`m.db.GetAsOf`
and `m.db.HistorySeek`). Since they completely bypassed the uncommitted
in-memory overlay, queries for new blocks would fall back to the
database history and read stale values (such as LogIndexAfterTxKey) from
the previous block.

To address this, we have:

1. Added a `DomainReader` interface to `MemoryMutation` to push
read-views back into the uncommitted memory overlay.
2. Intercept `GetAsOf` and `HistorySeek` calls in `MemoryMutation` and
`OverlayTemporalReadView` for all domains. We first query the
`DomainReader` (implemented by `SharedDomains`) to fetch the uncommitted
memory state. If a value is found and there is no error, we use it; if
not found or if the query errors (e.g. `inMemHistoryReads` is disabled
for that domain), we silently fall back to the database transaction.
3. Gave `TemporalMemBatch` the ability to skip the `inMemHistoryReads`
guard for `ReceiptDomain` in particular. This ensures that lookups on
receipt indexes always work from the overlay even when full history
tracking is disabled.
4. In receipt generator methods, wrapped the transaction in
`WithTemporalOverlay` to ensure all reads go through the uncommitted
memory overlay.

We added a regression test `TestBlockOverlay_DomainReadsRegression`. The
test writes receipt domain data to `SharedDomains` and checks:
- The production path (`NewReadView` returns `*MemoryMutation`)
correctly intercepts `GetAsOf` and `HistorySeek` for returning the
uncommitted data.
- The secondary path (`BlockOverlayTemporalTx` returning
`*OverlayTemporalReadView`) also correctly resolves the in-memory
receipt values.

Closes: #22504
(cherry picked from commit 114c90a)
…llowing them (#22893)

Independent correctness fix split out of #21414 (per the request to
break it up). Stands alone on `main`.

`MemoryMutation` and `OverlayTemporalReadView` `GetAsOf`/`HistorySeek`
gated the `DomainReader` result with `err == nil && ok`, so a reader
error fell through to the committed tx and was silently hidden. This
propagates the error and keeps the `ok`-based committed fallback (a
tombstone is `ok=true`, so it is not resurrected).

No dependency on the background-commit work; part of the split recorded
on #21414.

(cherry picked from commit 8324709)
Covers the two cherry-picks below it, each verified red before its fix and
green after:

- TestReceiptAsOf_InFlightBlockLogIndex — ReceiptAsOf through the overlay read
  view must return the in-flight block's LogIndexAfterTx. Without #22511 it
  returns the last committed block's value (0x7 instead of 0x3), which is the
  wrong first log index applied to every transaction of that block. Uses the
  production key and accessor, unlike the storage-level test #22511 shipped.

- TestDomainReadErrorsPropagate — a DomainReader error must reach the caller
  instead of falling through to the committed tx. Without #22893 the error is
  swallowed and the read silently answers with stale data. Covers both
  MemoryMutation and OverlayTemporalReadView.

- TestGetReceiptLogIndexThroughOverlay — pins the production wiring: the
  overlay is seeded with a log index the committed tx does not hold, so only a
  read routed through Filters.WithTemporalOverlay can produce it. Drop that
  call from GetReceipt and this test fails; the other two stay green.
@lupin012 lupin012 changed the title [r3.5] db/state: route receipt domain reads through overlay DomainReader [r3.5] db/state: CP route receipt domain reads through overlay DomainReader Aug 2, 2026
@lupin012
lupin012 marked this pull request as ready for review August 3, 2026 06:08
pull Bot pushed a commit to Dustin4444/erigon that referenced this pull request Aug 3, 2026
…erlay (erigontech#22961)

Port of the tests added in the release-branch backports erigontech#22951
(`release/3.5`) and erigontech#22960 (`release/3.6`).

All three pass on `main` — both erigontech#22511 and erigontech#22893 are here — so they
land as regression coverage. Each was written against the branch where
its fix was missing and verified red there first:

| Test | Verified red on | Without |
|---|---|---|
| `TestReceiptAsOf_InFlightBlockLogIndex` | `release/3.5` | erigontech#22511 —
returns `0x7` instead of `0x3` |
| `TestDomainReadErrorsPropagate` | `release/3.6` | erigontech#22893 — error
swallowed, `but got nil` |
| `TestGetReceiptLogIndexThroughOverlay` | `main` | the
`WithTemporalOverlay` call in `GetReceipt` |

The third one is the reason this is worth having on `main` regardless of
the backports: `Filters.WithOverlay` / `WithTemporalOverlay` had **no
test coverage anywhere in the tree**. Remove `tx =
g.filters.WithTemporalOverlay(tx)` from `GetReceipt` and the bug
reported on erigontech#22106 returns in production while CI stays green —
verified, that test now fails.

The other two also add coverage the upstream fixes did not ship:
erigontech#22511's own test works at storage level with a synthetic key, so
nothing exercised `LogIndexAfterTxKey` through
`rawtemporaldb.ReceiptAsOf`; erigontech#22893 shipped with no test at all.

One adaptation from the release branches: `AppendReceipt` is named
`AppendReceiptMetadata` here.
@lupin012
lupin012 added this pull request to the merge queue Aug 4, 2026
Merged via the queue into release/3.5 with commit 3229459 Aug 4, 2026
165 checks passed
@lupin012
lupin012 deleted the lupin012/backport_receipt_overlay_domain_reader_35 branch August 4, 2026 19:35
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.

4 participants