fix: cherry-pick: validate legacy checkpoint block against L1 in CheckValidBlock (#1638) (#1639)#1645
Conversation
…1638) (#1639) - Fixes a sync failure loop in `l1infotreesync` after upgrading from the legacy syncer (e.g. 0.8.x) to the multidownloader-based implementation (#1638). - Root cause: on upgrade the processor reports a last-processed **checkpoint block** (an "empty" block the legacy syncer persisted to track progress) that the legacy syncer downloaded, but the multidownloader's own freshly-created storage never contained it. `EVMMultidownloader.CheckValidBlock` then failed with `not found in storage or blocks_reorged`, leaving the syncer stuck with no self-recovery (manual DB deletion was the only workaround). - Fix: when a block is neither in `blocks` nor in `blocks_reorged` and is **at or below the finalized block**, `CheckValidBlock` now validates it against L1 by fetching the canonical header at that height and comparing hashes: - **match** → the block is canonical, it just was never downloaded by this multidownloader (legacy checkpoint after upgrade, or pruned block) → **valid**, the syncer resumes from `lastBlock+1`. - **differ** → the block is on an orphaned branch of a finalized height → **error** (severe inconsistency requiring manual intervention). - Blocks **above** the finalized block are not stable and keep the previous error behaviour. - None. - None. - 🤖 **Automatic**: `TestEVMMultidownloader_CheckValidBlock_LegacyCheckpointAfterUpgrade` (real empty storage) covering the three paths: canonical hash matches → valid; canonical hash differs → error; block above finalized → error. Existing `CheckValidBlock` mock tests updated for the new finalized lookup. - 🖱️ **Manual**: N/A. - Closes #1638 - N/A - The fix lives in the multidownloader layer (`CheckValidBlock`), where the error surfaces. The validation is gated on `blockNumber <= finalized` so the extra RPC call only happens for stable blocks, and only while the missing block is being resolved (transient, during the legacy→multidownloader transition; once the first blocks are downloaded and stored, the processor advances into the known range and this branch no longer fires). - On a canonical hash mismatch of a finalized block, the multidownloader cannot synthesize a reorg it never observed, so it surfaces a descriptive error rather than fabricating reorg data. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
Claude finished @joanestebanr's task in 1m 17s —— View job Code Review Complete🚀 What's NewThis PR cherry-picks a critical fix from 🐛 Bug Fixes
📋 Config UpdatesNone
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e5db97601
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // inconsistency and let the caller retry. | ||
| func (dh *EVMMultidownloader) checkValidBlockAgainstL1(ctx context.Context, blockNumber uint64, | ||
| blockHash common.Hash) (bool, uint64, error) { | ||
| finalizedBlockNumber, err := dh.GetFinalizedBlockNumber(ctx) |
There was a problem hiding this comment.
Query a real finalized height before trusting missing blocks
I checked the syncer configs and BlockFinality is user-configurable with LatestBlock, SafeBlock, and PendingBlock in addition to FinalizedBlock, but GetFinalizedBlockNumber here returns dh.cfg.BlockFinality rather than an immutable finalized height. In those configurations, a missing unfinalized block that matches the current canonical header is accepted as valid even though the multidownloader has no local block or reorged record for that height; if that block is later reorged, the resumed sync has skipped the only height it could use to detect/repair the processor state. Please gate this fallback on a truly finalized tag (or query aggkittypes.FinalizedBlock directly) before treating the absent block as valid.
Useful? React with 👍 / 👎.
|



🔄 Changes Summary
Cherry-pick of #1639 (merged into
release/0.10) ontodevelop.l1infotreesyncafter upgrading from the legacy syncer (e.g. 0.8.x) to the multidownloader-based implementation (bug: updating aggkit sometimes produce a stuck situation 'error downloading blocks' #1638).EVMMultidownloader.CheckValidBlockthen failed withnot found in storage or blocks_reorged, leaving the syncer stuck with no self-recovery (manual DB deletion was the only workaround).blocksnor inblocks_reorgedand is at or below the finalized block,CheckValidBlocknow validates it against L1 by fetching the canonical header at that height and comparing hashes:lastBlock+1.📋 Config Updates
✅ Testing
TestEVMMultidownloader_CheckValidBlock_LegacyCheckpointAfterUpgrade(real empty storage) covering the three paths; existingCheckValidBlockmock tests updated for the new finalized lookup. Build andmultidownloaderpackage tests verified locally ondevelop.🐞 Issues
🔗 Related PRs
release/0.10).📝 Notes
CheckValidBlock), where the error surfaces. The validation is gated onblockNumber <= finalized, so the extra RPC call only happens for stable blocks and only while the missing block is being resolved (transient, during the legacy→multidownloader transition).🤖 Generated with Claude Code