Skip to content

fix(derivation): advance L1 message cursor for skipped blocks#942

Open
curryxbo wants to merge 2 commits intomainfrom
fix/derivation-l1-cursor-skipped-blocks
Open

fix(derivation): advance L1 message cursor for skipped blocks#942
curryxbo wants to merge 2 commits intomainfrom
fix/derivation-l1-cursor-skipped-blocks

Conversation

@curryxbo
Copy link
Copy Markdown
Contributor

@curryxbo curryxbo commented Apr 24, 2026

Summary

  • In handleL1Message, when a block is skipped because block.Number <= l2Height (snapshot-started nodes), the L1 message cursor totalL1MessagePopped was not advanced. Subsequent blocks in the same batch then read L1 messages from the wrong offset, which could fail the length check or prepend incorrect L1 txs to SafeL2Data.
  • Fix: always advance totalL1MessagePopped by block.l1MsgNum, even on the skip path, so per-batch offsets stay aligned with the batch encoding (consistent with BatchInfo.ParseBatch's accumulation of l1MsgNum).
  • Drive-by: error message in the length-mismatch branch now prints len(l1Messages) instead of the whole slice.

Why this matters

parentTotalL1MessagePopped reflects the queue position at the end of the parent batch. Within the current batch, each block — in order — consumes l1MsgNum messages regardless of whether this node re-executes it. Skipping the cursor bump breaks that invariant and silently misaligns L1 reads for later blocks.

Test plan

  • Unit/integration tests covering a batch where the first block(s) are <= l2Height and carry non-zero l1MsgNum, and a later block has l1MsgNum > 0; assert correct L1 txs are prepended and no length-mismatch error is returned.
  • Manual verification on a snapshot-started node syncing across a batch boundary that includes L1 messages.

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes
    • Fixed L1 message tracking for nodes that start from snapshots so the message cursor advances correctly for blocks at or below the expected height, preventing missed message accounting.
    • Improved L1 message validation errors to report accurate message counts, making diagnostics clearer when discrepancies occur.

When a node starts from a snapshot, blocks in the current batch whose
number is <= the local L2 height are skipped to avoid re-injecting L1
messages. However, the L1 message cursor (totalL1MessagePopped) was not
advanced for those skipped blocks, causing subsequent blocks in the same
batch to read L1 messages from the wrong offset. This could fail the
length check, or worse, prepend incorrect L1 transactions to a block's
SafeL2Data.

Always advance the cursor by block.l1MsgNum, even when the block is
skipped, so that the per-batch L1 message offsets stay aligned with the
batch encoding.

Also fix the error message to print the number of L1 messages received
instead of the entire slice.

Made-with: Cursor
@curryxbo curryxbo requested a review from a team as a code owner April 24, 2026 09:45
@curryxbo curryxbo requested review from r3aker86 and removed request for a team April 24, 2026 09:45
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9380e72d-9d6e-463a-9a63-20c111205756

📥 Commits

Reviewing files that changed from the base of the PR and between a3c5f29 and f8d9645.

📒 Files selected for processing (1)
  • node/derivation/derivation.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • node/derivation/derivation.go

📝 Walkthrough

Walkthrough

The handleL1Message function in the derivation module is updated to advance the L1 message cursor for snapshot-started nodes by accumulating block.l1MsgNum for blocks with block.Number <= l2Height. Error reporting for L1 message count validation now uses len(l1Messages).

Changes

Cohort / File(s) Summary
L1 Message Cursor Handling
node/derivation/derivation.go
Advance L1 message cursor for snapshot-started nodes by adding block.l1MsgNum for blocks where block.Number <= l2Height; improve error message to report len(l1Messages) instead of printing the slice.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I nibbled bugs and straightened the line,
Counts now true, cursors hop in time,
Snapshot starts no longer stall the play,
Derivation hums through night and day! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(derivation): advance L1 message cursor for skipped blocks' directly and precisely describes the main bug fix: advancing the L1 message cursor for blocks that are skipped during processing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/derivation-l1-cursor-skipped-blocks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
node/derivation/derivation.go (1)

566-596: Consider adding the unit test called out in the PR test plan.

The PR description explicitly plans a unit/integration test for a batch where initial block(s) are <= l2Height with non-zero l1MsgNum followed by a later block with l1MsgNum > 0. I don't see a corresponding test file in this diff. Given this is a subtle cursor-alignment bug that only manifests on snapshot-started nodes, regression coverage would be valuable so a future refactor doesn't silently reintroduce the off-by-N on the skip path.

Want me to sketch a table-driven unit test for handleL1Message that fakes d.syncer.ReadL1MessagesInRange and asserts both the skip-advance and mixed skip/non-skip cases?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@node/derivation/derivation.go` around lines 566 - 596, Add a unit test for
Derivation.handleL1Message that covers the snapshot-skip path: create a
BatchInfo with initial block(s) having Number <= l2Height but l1MsgNum > 0 and a
later block with Number > l2Height and l1MsgNum > 0, mock
d.syncer.ReadL1MessagesInRange (or d.getL1Message) to return predictable L1
messages, call handleL1Message and assert the L1 cursor advancement
(totalL1MessagePopped behavior) and that rollupData.blockContexts for the
non-skipped block has the encoded L1 transactions prepended to
SafeL2Data.Transactions; include an additional table-driven case where no
skipping occurs to verify normal behavior. Ensure the test references
handleL1Message and the mocked getL1Message/ReadL1MessagesInRange so future
refactors catch the cursor-alignment regression.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@node/derivation/derivation.go`:
- Around line 566-596: Add a unit test for Derivation.handleL1Message that
covers the snapshot-skip path: create a BatchInfo with initial block(s) having
Number <= l2Height but l1MsgNum > 0 and a later block with Number > l2Height and
l1MsgNum > 0, mock d.syncer.ReadL1MessagesInRange (or d.getL1Message) to return
predictable L1 messages, call handleL1Message and assert the L1 cursor
advancement (totalL1MessagePopped behavior) and that rollupData.blockContexts
for the non-skipped block has the encoded L1 transactions prepended to
SafeL2Data.Transactions; include an additional table-driven case where no
skipping occurs to verify normal behavior. Ensure the test references
handleL1Message and the mocked getL1Message/ReadL1MessagesInRange so future
refactors catch the cursor-alignment regression.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9db0b0da-8232-4e70-8132-bc8b5311a576

📥 Commits

Reviewing files that changed from the base of the PR and between 78c30ab and a3c5f29.

📒 Files selected for processing (1)
  • node/derivation/derivation.go

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.

1 participant