[don't merge]fix(rollup): reject empty block span in commitBatch (#996)#997
[don't merge]fix(rollup): reject empty block span in commitBatch (#996)#997curryxbo wants to merge 2 commits into
Conversation
|
Warning Review limit reached
More reviews will be available in 14 minutes and 14 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds a monotonicity check in ChangesEmpty Block Span Validation for V1 Batch Headers
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
_commitBatch accepted an unconstrained lastBlockNumber, so a single active staker could commit a batch with lastBlockNumber <= parent.lastBlockNumber (blockCount == 0). The field is not signature-bound (the _getBLSMsgHash BLS path is still a bytes32(0) stub), so this needs no collusion. Derivation nodes in layer1 verify mode then panic (pre-#995) or stall permanently (post-#995) on the malformed batch — a network-wide DoS whose root cause is on-chain. Enforce a non-empty, monotonic block span in _commitBatchWithBatchData (shared by commitBatch / commitState / commitBatchWithProof): when the parent header carries a lastBlockNumber (V1+), require lastBlockNumber > parentLastBlockNumber. This mirrors the node's blockCount = lastBlockNumber - parentLastBlockNumber derivation. The V0->V1 transition (parent version 0) carries no lastBlockNumber and is a historical one-time event, so it is not guarded here. Tests: empty span (== parent, the off-by-one the original underflow guard missed) reverts with "empty block span"; a strictly increasing span still commits (the only happy-path coverage for V1 parents — the existing suite is all V0). Full Rollup suite passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1fd49aa to
3fa7bae
Compare
What
Closes #996. Fixes the on-chain root cause of the batch-parse DoS mitigated node-side in #994 / #995.
Rollup.sol::_commitBatchaccepted an unconstrainedlastBlockNumberfrom calldata — there was no check that the committed batch'slastBlockNumberis strictly greater than the parent's (i.e. thatblockCount >= 1). Because the BLS signature path is still a stub (_getBLSMsgHashreturnsbytes32(0)), the field is not signature-bound, so a single active staker (byzantine or buggy) can commitlastBlockNumber == parent.lastBlockNumber(empty span) on L1. Derivation nodes inlayer1verify mode then panic (pre-#995) or stall permanently (post-#995) on that batch, since it cannot be skipped without diverging from the L1-canonical chain.Fix
In
_commitBatchWithBatchData(the shared path forcommitBatch/commitState/commitBatchWithProof), after loading the parent header and before computing the data hash:This mirrors the node's
blockCount = lastBlockNumber - parentLastBlockNumberderivation. The check only applies when the parent header carries alastBlockNumber(V1+). The V0→V1 transition (parent version 0) carries nolastBlockNumberand is a historical one-time event, so it is not guarded here — matching the node'sparentVersion == 0branch.Tests
New tests in
RollupCommitBatchWithProofTest:test_commitBatchWithProof_reverts_on_empty_block_span—lastBlockNumber == parent(the off-by-one the original underflow guard missed) → reverts"empty block span".test_commitBatchWithProof_allows_monotonic_block_span—lastBlockNumber > parent→ still commits. This is the only happy-path coverage for a V1 parent; the existing suite is all V0, which skips the guard.Full
Rollup.t.solsuite (39 tests across 5 contracts) passes, including the existingtest_commitBatches_succeeds/test_revertBatch_succeedsmulti-batch paths.Scope / follow-up (out of scope)
_getBLSMsgHashis still abytes32(0)stub — until batch fields (incl.lastBlockNumber) are bound to a real BLS signature over the sequencer set (_getValidSequencerSetis also a TODO), the data hash is authenticated only by a single submitter, not by consensus. This is the deeper systemic fix and should be tracked separately.MAX_BLOCKS_PER_BATCH) as defense-in-depth; the node side (fix(derivation): reject invalid blockCount to prevent batch-parse DoS #995) already bounds allocation by payload length, so it is not required for this DoS.Notes
🤖 Generated with Claude Code