Skip to content

refactor(l1): simplify sync functionality - #1380

Open
0xKitsune wants to merge 3 commits into
kit/l1-subscriber-sync-blocksfrom
kit/refactor-l1-sync
Open

refactor(l1): simplify sync functionality#1380
0xKitsune wants to merge 3 commits into
kit/l1-subscriber-sync-blocksfrom
kit/refactor-l1-sync

Conversation

@0xKitsune

@0xKitsune 0xKitsune commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

This PR simplifies finalized L1 synchronization by keeping one next-block cursor across RPC reconnects. It removes queue and tracker cursor reconstruction and keeps the subscription loop directly in run, so partial backfills resume from the first unfinished block.

@0xKitsune

Copy link
Copy Markdown
Collaborator Author

cyclops audit fast

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

cc @0xKitsune

Cyclops audit event published. View workflow run

Config: config: default, iterations: 1, hours: default

@tempoxyz-bot tempoxyz-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

👁️ Cyclops Review

This PR keeps a single in-memory L1 sync cursor across reconnects and advances it after each finalized block is fully applied. The cursor-preservation path looks safer for partial retries, but one startup error path now bypasses retry handling, and there is one body-only defense-in-depth note below.

Body-only finding

🛡️ [DEFENSE-IN-DEPTH] Validate fetched header height before applying queue side effects

crates/l1/src/subscriber.rs:602 requests block block_number, but the returned header's own number() is not checked before receipts are fetched by the returned hash and before later side effects use header.number(). A hostile or misbehaving RPC endpoint can return a real finalized header for a later height when the queue is empty; the queue can admit that later height before the tracker rejects it for checkpoint discontinuity, leaving ingestion stuck on retryable out-of-order errors. This appears pre-existing and bounded to liveness because downstream consumers re-check checkpoint continuity.

Recommended Fix: Fail closed immediately after get_header_by_number(block_number.into()) unless header.number() == block_number. This also makes the progress logging arithmetic robust against headers above the intended to range.

Reviewer Callouts
  • L1 cursor invariant: The refactor removes the previous queue/tracker high-water recomputation and relies on a local next_block. Consider adding a debug assertion near crates/l1/src/subscriber.rs:714 tying next_block to the queue/tracker tips so future changes cannot silently break the invariant.
  • Backfill progress arithmetic: remaining = to - block_number at crates/l1/src/subscriber.rs:727 is safe only if returned headers cannot exceed the requested range. If height validation is not added, switch this to to.saturating_sub(block_number).

/// Deterministic failures while applying a receipt-verified finalized block are fatal.
pub async fn run(self) {
pub async fn run(self) -> Result<(), L1SubscriberError> {
let mut next_block = self.resolve_start_block()?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ [ISSUE] Startup checkpoint read errors now bypass retry handling

run() now calls self.resolve_start_block()? before entering the reconnect/retry loop. Errors from provider.latest() or tempo_num_hash() are classified as retryable Other errors elsewhere, but here they are returned directly to the caller, which panics the critical task. A transient state-provider/storage read failure at subscriber startup can therefore crash the node instead of being retried as it was before this refactor.

Recommended Fix:
Initialize next_block through the same retry policy used for connection/sync errors, or add a small pre-loop retry around resolve_start_block() that retries provider/storage errors while still treating configuration errors such as an unanchored genesis as fatal.

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.

2 participants