[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 1 commit into
Conversation
📝 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 docstrings
🧪 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