Skip to content

Conversation

@luiz-lvj
Copy link
Collaborator

@luiz-lvj luiz-lvj commented Jan 28, 2026

This PR updates the BaseBuffer contract to have a mapping of blockNumber to blockHash. This layout storage is done to allow for storage proofs on the buffer contract.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed block-hash lookup configuration so missing hashes now revert and stale reads are prevented.
  • Chores

    • Redesigned on-chain block-hash storage to use a compact circular buffer with explicit eviction and cleanup.
    • Consolidated some dependency paths and removed an embedded submodule to simplify project layout.
  • Tests

    • Updated test snapshots and mocks to align with the storage and API adjustments.

✏️ Tip: You can customize this high-level summary in your review settings.

@luiz-lvj luiz-lvj requested a review from frangio January 28, 2026 18:29
@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

Refactors BaseBuffer to store block hashes in a mapping plus a circular buffer and updates multiple ChildToParentProver contracts and tests to use the new buffer layout and a blockHashMappingSlot value of 1 with adjusted import paths.

Changes

Cohort / File(s) Summary
Block hash storage restructuring
src/contracts/block-hash-pusher/BaseBuffer.sol
Replace per-slot BufferSlot array with _blockHashes mapping and a circular _blockNumberBuffer; parentChainBlockHash reads from mapping; _receiveHashes writes mapping and buffer and clears evicted mapping entries; maintain newestBlockNumber and emit BlockHashesPushed.
Prover slot & import updates
src/contracts/provers/.../ChildToParentProver.sol
src/contracts/provers/linea/ChildToParentProver.sol, src/contracts/provers/scroll/ChildToParentProver.sol, src/contracts/provers/zksync/ChildToParentProver.sol, src/contracts/provers/arbitrum/ChildToParentProver.sol
Change IBuffer import paths to local relative ../../block-hash-pusher/interfaces/IBuffer.sol and set blockHashMappingSlot constant from 51 to 1 where applicable.
Tests & mocks import / API adjustments
test/*, test/mocks/BufferMock.sol, test/Receiver*.t.sol, test/provers/*/ChildToParentProver.t.sol
Update IBuffer import paths to local sources; BufferMock.sol changes public types/signatures (e.g., newestBlockNumber from uint64uint256, aliasedPusher()pusher()), and removes some previously public view accessors.
Repository submodule / remapping cleanup
.gitmodules, lib/block-hash-pusher, remappings.txt
Remove lib/block-hash-pusher submodule entry and corresponding remapping block-hash-pusher/=lib/block-hash-pusher/.
Snapshots
snapshots/verifyBroadcastMessage.json
Update snapshot numeric values for multiple broadcast entries (data-only changes).

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • frangio
  • pepebndc

Poem

🐰 I hopped through bytes and slotly song,

Cleared stale hashes, set mappings strong,
From fifty-one I leapt to one,
Buffers spin and work gets done,
A tiny hop — the refactor's fun! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Change storage layout to allow mapping' directly describes the main change: replacing a per-slot BufferSlot struct with a mapping from block numbers to hashes in BaseBuffer.sol.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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


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

@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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/contracts/block-hash-pusher/BaseBuffer.sol`:
- Around line 49-65: The loop currently writes block numbers into _buffer even
when the corresponding block hash in blockHashes is zero, creating an
irreversible tombstone because future writes for that block are skipped; update
the write path in BaseBuffer (the loop that iterates over blockHashes, uses
variables blockNumber, bufferIndex, existingBlockNumber, and writes to _buffer
and _blockHashes) to reject zero hashes by adding a guard such as
require(blockHashes[i] != bytes32(0), "zero block hash not allowed") (or
otherwise revert) before mutating _buffer/_blockHashes so zero entries cannot be
recorded and corrupt future writes.

Comment on lines 49 to 65
// write the hashes to both the mapping and circular buffer
for (uint256 i = 0; i < blockHashesLength; i++) {
uint256 blockNumber = firstBlockNumber + i;
uint256 bufferIndex = blockNumber % _BUFFER_SIZE;
uint256 existingBlockNumber = _buffer[bufferIndex];

BufferSlot storage bufferSlot = _buffer[bufferIndex];
if (blockNumber <= bufferSlot.blockNumber) {
// noop
if (blockNumber <= existingBlockNumber) {
continue;
}

bufferSlot.blockNumber = blockNumber;
bufferSlot.blockHash = blockHashes[i];
if (existingBlockNumber != 0) {
_blockHashes[existingBlockNumber] = 0;
}

_blockHashes[blockNumber] = blockHashes[i];
_buffer[bufferIndex] = blockNumber;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard against zero block hashes to avoid irreversible tombstones.

If a zero hash is pushed, _buffer records the block number but the mapping is left empty. Later pushes for the same block number will be skipped, making it impossible to correct that entry. Consider rejecting zero hashes at write time.

🔧 Proposed fix
for (uint256 i = 0; i < blockHashesLength; i++) {
    uint256 blockNumber = firstBlockNumber + i;
+   require(blockHashes[i] != bytes32(0), "ZERO_BLOCK_HASH");
    uint256 bufferIndex = blockNumber % _BUFFER_SIZE;
    uint256 existingBlockNumber = _buffer[bufferIndex];
🤖 Prompt for AI Agents
In `@src/contracts/block-hash-pusher/BaseBuffer.sol` around lines 49 - 65, The
loop currently writes block numbers into _buffer even when the corresponding
block hash in blockHashes is zero, creating an irreversible tombstone because
future writes for that block are skipped; update the write path in BaseBuffer
(the loop that iterates over blockHashes, uses variables blockNumber,
bufferIndex, existingBlockNumber, and writes to _buffer and _blockHashes) to
reject zero hashes by adding a guard such as require(blockHashes[i] !=
bytes32(0), "zero block hash not allowed") (or otherwise revert) before mutating
_buffer/_blockHashes so zero entries cannot be recorded and corrupt future
writes.

@frangio
Copy link
Collaborator

frangio commented Jan 28, 2026

Reverts 62185dd

mapping(uint256 blockNumber => bytes32 blockHash) private _blockHashes;

BufferSlot[_BUFFER_SIZE] private _buffer;
uint256[_BUFFER_SIZE] private _buffer;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's call this _blockNumberBuffer as it used to be.

Copy link
Collaborator

@frangio frangio left a comment

Choose a reason for hiding this comment

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

Ok after above comment is addressed.

@luiz-lvj luiz-lvj requested a review from frangio January 28, 2026 20:17
@luiz-lvj luiz-lvj requested a review from frangio January 28, 2026 20:33
Copy link

@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.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/mocks/BufferMock.sol (1)

9-16: Remove the lingering uint64 cast to avoid truncation.
Line 16 still narrows the value even though newestBlockNumber is now uint256. This can silently truncate large block numbers.

✅ Proposed fix
-        newestBlockNumber = uint64(firstBlockNumber + blockHashes.length - 1);
+        newestBlockNumber = firstBlockNumber + blockHashes.length - 1;
🧹 Nitpick comments (2)
src/contracts/provers/linea/ChildToParentProver.sol (1)

45-47: Update the stale URL comment to reference the local BaseBuffer.

The comment on line 46 still references https://github.com/OffchainLabs/block-hash-pusher/... but this prover now uses a local BaseBuffer with a different storage layout. Consider updating the comment to point to the local BaseBuffer.sol path for accuracy.

📝 Suggested comment update
         // calculate the slot based on the provided block number
-        // see: https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12/contracts/Buffer.sol#L32
+        // see: src/contracts/block-hash-pusher/BaseBuffer.sol
         uint256 slot = uint256(SlotDerivation.deriveMapping(bytes32(blockHashMappingSlot), targetBlockNumber));
src/contracts/provers/scroll/ChildToParentProver.sol (1)

16-18: Verify the mapping slot aligns with BaseBuffer’s new layout.
Storage proofs will fail if Line 18 drifts from the actual mapping slot; please confirm it’s truly 1 in the updated BaseBuffer layout. If feasible, consider sourcing the slot from a shared constant to avoid future drift.

@luiz-lvj luiz-lvj merged commit afffaea into main Jan 28, 2026
3 checks passed
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.

3 participants