#23193 fixes debug_getModifiedAccountsByNumber, which resolved latest/pending on the overlay (fork-choice) view while scanning history on the committed view. The review of that PR found the same class of bug in two more endpoints.
Common setup: during an FCU background-commit window — after PublishOverlay makes block N+1 visible to overlay-aware readers, but before the batch is committed to the DB — overlay-aware tag resolution returns N+1 while the data reads only reach block N. The window is short-lived but recurs on every FCU.
eth_getLogs: spurious errors on the latest tag
resolveLogsRange resolves its baseline latest on the committed view (nil filters), but resolves user-supplied tags on the overlay view (api.filters):
|
latest, _, _, err := rpchelper.GetBlockNumber(ctx, rpc.BlockNumberOrHashWithNumber(rpc.LatestExecutedBlockNumber), tx, api._blockReader, nil) |
|
if err != nil { |
|
return 0, 0, err |
|
} |
|
|
|
begin = latest |
|
if crit.FromBlock != nil { |
|
fromBlock := crit.FromBlock.Int64() |
|
if fromBlock > 0 { |
|
begin = uint64(fromBlock) |
|
} else { |
|
blockNum := rpc.BlockNumber(fromBlock) |
|
begin, _, _, err = rpchelper.GetBlockNumber(ctx, rpc.BlockNumberOrHashWithNumber(blockNum), tx, api._blockReader, api.filters) |
|
if err != nil { |
|
return 0, 0, err |
|
} |
|
} |
|
|
|
if checkFuture && begin > latest { |
|
return 0, 0, &rpc.CustomError{Message: ErrBlockRangeIntoFuture, Code: rpc.ErrCodeInvalidParams} |
|
} |
|
} |
|
end = latest |
|
if crit.ToBlock != nil { |
|
toBlock := crit.ToBlock.Int64() |
|
if toBlock > 0 { |
|
end = uint64(toBlock) |
|
} else { |
|
blockNum := rpc.BlockNumber(toBlock) |
|
end, _, _, err = rpchelper.GetBlockNumber(ctx, rpc.BlockNumberOrHashWithNumber(blockNum), tx, api._blockReader, api.filters) |
|
if err != nil { |
|
return 0, 0, err |
|
} |
|
} |
|
|
|
if checkFuture && end > latest { |
|
return 0, 0, &rpc.CustomError{Message: ErrBlockRangeIntoFuture, Code: rpc.ErrCodeInvalidParams} |
|
} |
|
} |
With overlay head N+1 and committed execution progress N:
eth_getLogs({"fromBlock":"latest"}) resolves begin = N+1 (api.filters, line 170) but latest = N (nil filters, line 158), so the checkFuture guard returns ErrBlockRangeIntoFuture.
- Even without that guard,
GetLogs rejects begin > latestExecuted with "node is still syncing" (eth_receipts.go line 247).
So a plain latest logs poll — the most common eth_getLogs shape — fails transiently during every commit window.
Suggested fix, same as #23193: pass nil filters at lines 170 and 187 so user tags resolve on the same committed view that the guards and the log scan use. Trade-off (same as accepted in #23193): pending then resolves to the latest executed block instead of the txpool's pending block.
trace_filter: silently omits the overlay-head block
TraceAPIImpl.Filter resolves fromBlock/toBlock tags on the overlay view, then scans txnum indexes on the committed tx without any executed-progress guard:
|
if req.FromBlock == nil { |
|
fromBlock = 0 |
|
} else { |
|
fromBlock, _, _, err = rpchelper.GetBlockNumber(ctx, *req.FromBlock, dbtx, api._blockReader, api.filters) |
|
if err != nil { |
|
if errors.As(err, &rpc.BlockNotFoundErr{}) { |
|
stream.WriteEmptyArray() |
|
return nil // waiting for spec: not error for historical reasons |
|
} |
|
return err |
|
} |
|
} |
|
|
|
if req.ToBlock == nil { |
|
headNumber, err := api._blockReader.HeaderNumber(ctx, dbtx, rawdb.ReadHeadHeaderHash(dbtx)) |
|
if err != nil { |
|
return err |
|
} |
|
toBlock = *headNumber |
|
} else { |
|
toBlock, _, _, err = rpchelper.GetBlockNumber(ctx, *req.ToBlock, dbtx, api._blockReader, api.filters) |
|
if err != nil { |
|
if errors.As(err, &rpc.BlockNotFoundErr{}) { |
|
stream.WriteEmptyArray() |
|
return nil // waiting for spec: not error for historical reasons |
|
} |
|
return err |
|
} |
|
} |
|
if fromBlock > toBlock { |
|
return errors.New("invalid parameters: fromBlock cannot be greater than toBlock") |
|
} |
TxNums.Min/Max silently clamp a missing block to the last available txnum (see db/kv/rawdbv3/txnum.go). With overlay head N+1:
trace_filter({"toBlock":"latest"}) resolves toBlock = N+1, clamps to block N's last txnum, and returns traces only up to block N. The response is a bare array that does not echo the resolved range, so an omitted head block looks identical to an empty one. An incremental indexer that advances its cursor to the requested head permanently skips block N+1's traces.
{"fromBlock":"latest"} combined with an older numeric toBlock errors with "fromBlock cannot be greater than toBlock" instead.
trace_block/trace_transaction guard against this via rpchelper.CheckBlockExecuted in callBlock/callTransaction; Filter alone lacks the guard.
Suggested fix: resolve the tags with nil filters like #23193, and optionally add an executed-progress guard so an explicit out-of-range block number errors instead of silently clamping.
Related
debug_getModifiedAccountsByHash is missing the startNum > latestBlock guard its ByNumber twin has (only the two-param path checks endNum), so it returns a silent empty result for a not-yet-executed block where ByNumber errors. Worth aligning while touching this area.
#23193 fixes
debug_getModifiedAccountsByNumber, which resolvedlatest/pendingon the overlay (fork-choice) view while scanning history on the committed view. The review of that PR found the same class of bug in two more endpoints.Common setup: during an FCU background-commit window — after
PublishOverlaymakes block N+1 visible to overlay-aware readers, but before the batch is committed to the DB — overlay-aware tag resolution returns N+1 while the data reads only reach block N. The window is short-lived but recurs on every FCU.eth_getLogs: spurious errors on thelatesttagresolveLogsRangeresolves its baselinelateston the committed view (nilfilters), but resolves user-supplied tags on the overlay view (api.filters):erigon/rpc/jsonrpc/eth_receipts.go
Lines 158 to 196 in 70cb10d
With overlay head N+1 and committed execution progress N:
eth_getLogs({"fromBlock":"latest"})resolvesbegin= N+1 (api.filters, line 170) butlatest= N (nilfilters, line 158), so thecheckFutureguard returnsErrBlockRangeIntoFuture.GetLogsrejectsbegin > latestExecutedwith "node is still syncing" (eth_receipts.go line 247).So a plain
latestlogs poll — the most commoneth_getLogsshape — fails transiently during every commit window.Suggested fix, same as #23193: pass
nilfilters at lines 170 and 187 so user tags resolve on the same committed view that the guards and the log scan use. Trade-off (same as accepted in #23193):pendingthen resolves to the latest executed block instead of the txpool's pending block.trace_filter: silently omits the overlay-head blockTraceAPIImpl.FilterresolvesfromBlock/toBlocktags on the overlay view, then scans txnum indexes on the committed tx without any executed-progress guard:erigon/rpc/jsonrpc/trace_filtering.go
Lines 325 to 356 in 70cb10d
TxNums.Min/Maxsilently clamp a missing block to the last available txnum (seedb/kv/rawdbv3/txnum.go). With overlay head N+1:trace_filter({"toBlock":"latest"})resolvestoBlock= N+1, clamps to block N's last txnum, and returns traces only up to block N. The response is a bare array that does not echo the resolved range, so an omitted head block looks identical to an empty one. An incremental indexer that advances its cursor to the requested head permanently skips block N+1's traces.{"fromBlock":"latest"}combined with an older numerictoBlockerrors with "fromBlock cannot be greater than toBlock" instead.trace_block/trace_transactionguard against this viarpchelper.CheckBlockExecutedincallBlock/callTransaction;Filteralone lacks the guard.Suggested fix: resolve the tags with
nilfilters like #23193, and optionally add an executed-progress guard so an explicit out-of-range block number errors instead of silently clamping.Related
debug_getModifiedAccountsByHashis missing thestartNum > latestBlockguard its ByNumber twin has (only the two-param path checksendNum), so it returns a silent empty result for a not-yet-executed block where ByNumber errors. Worth aligning while touching this area.