You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Rpc.mapBlock asserted the block hash, so one header that did not hash to its own hash crashed the process. Providers that spread calls over several backends can return such headers after a network upgrade. After Avalanche's Helicon upgrade, some C-Chain responses omit the six new header fields (targetExponent, minPriceExponent, settledHeight, settledGasUnix, settledGasNumerator, settledExcess) but keep the correct hash. Fetching the same block again returns the full header.
Change
mapBlock: on a hash mismatch, log a warning (block number, returned and calculated hash) and return the block flagged _isInvalid with _errorMessage: 'failed to verify block hash'. The other checks are skipped for that block.
getBlockBatch: logs, receipts and traces are fetched only for unflagged blocks. A flagged block's data would be thrown away, and the logs-bloom and receipts-root checks read the same bad header, so they could throw before the caller saw the flag.
The existing handling for flagged blocks then fetches it again. getBlocks retries up to 5 times, then throws failed to verify block hash with the block numbers. PollStream cuts the batch at the flagged block and asks for it on the next poll.
Finalizer: a flagged block no longer counts as proof of finality. It is probed again later, like a missing one.
A header that never verifies (for example, a hash formula that lacks a new fork's fields) still stops ingestion. getBlocks gives up after 5 attempts. At the chain head, PollStream asks for the block again after each headPollInterval with no limit, but once the finalized head is more than strideSize blocks past it, ingest moves to the getBlocks path, which gives up as above. Only a finalized head that stops moving keeps the head path waiting.
Tests
test/rpc.test.ts: the tampered-hash test now expects a flagged block instead of an exception. New Avalanche tests: the Helicon fixture verifies, and the same block without the six fields is flagged.
get-blocks.test.ts: a flagged block is fetched again and replaced; after 5 failures the error names the hash failure.
finalizer.test.ts (new): a flagged block does not set the finalized head.
ingest.test.ts: a head block whose hash never verifies ends ingestion with failed to verify block hash once the finalized head moves past it.
test/rpc.test.ts: a block whose header fails the hash check (a zeroed logsBloom) comes back flagged, with no receipts fetched, while verifyLogsBloom is on. Before this change the logs-bloom check threw.
vitest --run in evm/evm-rpc: 279 passed, 29 skipped. tsc clean.
Also ran getBlocks and Rpc over a fake client that served a real mainnet response without the Helicon fields, then the full one: one warning, then the verified block.
A block whose header does not hash to its `hash` crashed the process.
Some providers spread calls over several backends, and after a network
upgrade not every backend returns the new header fields: after
Avalanche's Helicon upgrade, some C-Chain responses omit the six new
fields while keeping the correct `hash`. One such response was enough
to stop ingestion.
mapBlock now logs the mismatch and flags the block `_isInvalid`, so the
existing paths fetch it again: getBlocks up to 5 times before failing
with 'failed to verify block hash', PollStream on its next poll. The
finalizer treats a flagged block as unknown and probes it again later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F8ruhLh1ZL58YedAa4SpaB
…estion
The head path waits for a flagged block without limit. Once the
finalized head is more than `strideSize` blocks past it, ingest moves to
the catch-up path, which gives up after its retries, so such a block
still stops the process instead of stalling it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F8ruhLh1ZL58YedAa4SpaB
This return does not short-circuit getBlockBatch: when the caller requests logs, receipts, or traces, the method still passes the flagged block to addRequestedData afterward. Those routines can validate or mutate the block again (for example, addLogs can throw during logs-bloom verification), so the hash-invalid response may never reach getBlocks as a retryable block. Skip _isInvalid blocks while adding requested data, while retaining them in the returned array for retry.
getBlockBatch passed a hash-flagged block on to addRequestedData. Its
data would be thrown away when the block is fetched again, and the logs
and receipts checks read the same bad header, so a failed logs-bloom or
receipts-root check could throw before the caller saw the flag. Only
unflagged blocks get logs, receipts and traces now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F8ruhLh1ZL58YedAa4SpaB
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🔵 Needs a closer look
Log requests must exclude invalid blocks in the middle of a range.
Review effort: Lite Findings: None
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
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.
Why
Rpc.mapBlockasserted the block hash, so one header that did not hash to its ownhashcrashed the process. Providers that spread calls over several backends can return such headers after a network upgrade. After Avalanche's Helicon upgrade, some C-Chain responses omit the six new header fields (targetExponent,minPriceExponent,settledHeight,settledGasUnix,settledGasNumerator,settledExcess) but keep the correcthash. Fetching the same block again returns the full header.Change
mapBlock: on a hash mismatch, log a warning (block number, returned and calculated hash) and return the block flagged_isInvalidwith_errorMessage: 'failed to verify block hash'. The other checks are skipped for that block.getBlockBatch: logs, receipts and traces are fetched only for unflagged blocks. A flagged block's data would be thrown away, and the logs-bloom and receipts-root checks read the same bad header, so they could throw before the caller saw the flag.getBlocksretries up to 5 times, then throwsfailed to verify block hashwith the block numbers.PollStreamcuts the batch at the flagged block and asks for it on the next poll.Finalizer: a flagged block no longer counts as proof of finality. It is probed again later, like a missing one.A header that never verifies (for example, a hash formula that lacks a new fork's fields) still stops ingestion.
getBlocksgives up after 5 attempts. At the chain head,PollStreamasks for the block again after eachheadPollIntervalwith no limit, but once the finalized head is more thanstrideSizeblocks past it,ingestmoves to thegetBlockspath, which gives up as above. Only a finalized head that stops moving keeps the head path waiting.Tests
test/rpc.test.ts: the tampered-hash test now expects a flagged block instead of an exception. New Avalanche tests: the Helicon fixture verifies, and the same block without the six fields is flagged.get-blocks.test.ts: a flagged block is fetched again and replaced; after 5 failures the error names the hash failure.finalizer.test.ts(new): a flagged block does not set the finalized head.ingest.test.ts: a head block whose hash never verifies ends ingestion withfailed to verify block hashonce the finalized head moves past it.test/rpc.test.ts: a block whose header fails the hash check (a zeroedlogsBloom) comes back flagged, with no receipts fetched, whileverifyLogsBloomis on. Before this change the logs-bloom check threw.vitest --runinevm/evm-rpc: 279 passed, 29 skipped.tscclean.getBlocksandRpcover a fake client that served a real mainnet response without the Helicon fields, then the full one: one warning, then the verified block.🤖 Generated with Claude Code