@@ -104,6 +104,10 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
104104 /// @dev After this period, anyone can submit batches if sequencers are offline or censoring.
105105 uint256 public rollupDelayPeriod;
106106
107+ /// @notice Store blob versioned hash per batch index. Preserved across revertBatch so recommit can reuse.
108+ /// @dev Placed after rollupDelayPeriod for upgrade-safe storage layout (forward compatibility).
109+ mapping (uint256 batchIndex = > bytes32 blobVersionedHash ) public batchBlobVersionedHashes;
110+
107111 /**********************
108112 * Function Modifiers *
109113 **********************/
@@ -116,13 +120,13 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
116120
117121 /// @notice Only challenger allowed.
118122 modifier onlyChallenger () {
119- require (isChallenger[_msgSender ()], "caller challenger allowed " );
123+ require (isChallenger[_msgSender ()], "only challenger allowed " );
120124 _;
121125 }
122126
123127 /// @notice Modifier to ensure that there is no pending revert request.
124128 modifier nonReqRevert () {
125- require (revertReqIndex == 0 , "need revert " );
129+ require (revertReqIndex == 0 , "pending revert request " );
126130 _;
127131 }
128132
@@ -217,6 +221,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
217221
218222 committedBatches[_batchIndex] = _batchHash;
219223 batchDataStore[_batchIndex] = BatchData (block .timestamp , block .timestamp , 0 , 0 );
224+ batchBlobVersionedHashes[_batchIndex] = BatchHeaderCodecV0.getBlobVersionedHash (memPtr);
220225
221226 committedStateRoots[_batchIndex] = _postStateRoot;
222227 finalizedStateRoots[_batchIndex] = _postStateRoot;
@@ -232,20 +237,49 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
232237 BatchDataInput calldata batchDataInput ,
233238 BatchSignatureInput calldata batchSignatureInput
234239 ) external payable override onlyActiveStaker nonReqRevert whenNotPaused {
235- // check l1msg delay - sequencer must process L1 messages when delayed
240+ // check if the next batch has a stored blob hash
241+ (uint256 _batchPtr , ) = _loadBatchHeader (batchDataInput.parentBatchHeader);
242+ uint256 _nextBatchIndex = BatchHeaderCodecV0.getBatchIndex (_batchPtr) + 1 ;
243+ require (batchBlobVersionedHashes[_nextBatchIndex] == bytes32 (0 ), "commitBatch requires no stored blob hash " );
244+ if (
245+ IL1MessageQueue (messageQueue).getFirstUnfinalizedMessageEnqueueTime () + rollupDelayPeriod < block .timestamp
246+ ) {
247+ require (batchDataInput.numL1Messages > 0 , "l1msg delay " );
248+ }
249+ uint256 submitterBitmap = IL1Staking (l1StakingContract).getStakerBitmap (_msgSender ());
250+ bytes32 _blobVersionedHash = (blobhash (0 ) == bytes32 (0 )) ? ZERO_VERSIONED_HASH : blobhash (0 );
251+ _commitBatchWithBatchData (batchDataInput, batchSignatureInput, submitterBitmap, _blobVersionedHash);
252+ }
253+
254+ /// @inheritdoc IRollup
255+ /// @notice Commit batch state when blob hash is already stored (recommit after revert without blob).
256+ function commitState (
257+ BatchDataInput calldata batchDataInput ,
258+ BatchSignatureInput calldata batchSignatureInput
259+ ) external override onlyActiveStaker nonReqRevert whenNotPaused {
260+ require (blobhash (0 ) == bytes32 (0 ), "commitState must not carry blob " );
261+ (uint256 _batchPtr , ) = _loadBatchHeader (batchDataInput.parentBatchHeader);
262+ uint256 _nextBatchIndex = BatchHeaderCodecV0.getBatchIndex (_batchPtr) + 1 ;
263+ require (batchBlobVersionedHashes[_nextBatchIndex] != bytes32 (0 ), "no stored blob hash for this batch " );
236264 if (
237265 IL1MessageQueue (messageQueue).getFirstUnfinalizedMessageEnqueueTime () + rollupDelayPeriod < block .timestamp
238266 ) {
239267 require (batchDataInput.numL1Messages > 0 , "l1msg delay " );
240268 }
241269 uint256 submitterBitmap = IL1Staking (l1StakingContract).getStakerBitmap (_msgSender ());
242- _commitBatchWithBatchData (batchDataInput, batchSignatureInput, submitterBitmap);
270+ _commitBatchWithBatchData (
271+ batchDataInput,
272+ batchSignatureInput,
273+ submitterBitmap,
274+ batchBlobVersionedHashes[_nextBatchIndex]
275+ );
243276 }
244277
245278 function _commitBatchWithBatchData (
246279 BatchDataInput calldata batchDataInput ,
247280 BatchSignatureInput calldata batchSignatureInput ,
248- uint256 submitterBitmap
281+ uint256 submitterBitmap ,
282+ bytes32 blobVersionedHash
249283 ) internal {
250284 require (batchDataInput.version == 0 || batchDataInput.version == 1 , "invalid version " );
251285 require (batchDataInput.prevStateRoot != bytes32 (0 ), "previous state root is zero " );
@@ -284,7 +318,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
284318 assembly {
285319 _batchIndex := add (_batchIndex, 1 ) // increase batch index
286320 }
287- bytes32 _blobVersionedHash = ( blobhash ( 0 ) == bytes32 ( 0 )) ? ZERO_VERSIONED_HASH : blobhash ( 0 ) ;
321+ bytes32 _blobVersionedHash = blobVersionedHash ;
288322
289323 {
290324 uint256 _headerLength = BatchHeaderCodecV0.BATCH_HEADER_LENGTH;
@@ -314,6 +348,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
314348 }
315349 committedBatches[_batchIndex] = BatchHeaderCodecV0.computeBatchHash (_batchPtr, _headerLength);
316350 committedStateRoots[_batchIndex] = batchDataInput.postStateRoot;
351+ batchBlobVersionedHashes[_batchIndex] = _blobVersionedHash;
317352 uint256 proveRemainingTime = 0 ;
318353 if (inChallenge) {
319354 // Make the batch finalize time longer than the time required for the current challenge
@@ -368,8 +403,17 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
368403 require (batchDataInput.numL1Messages > 0 , "l1msg delay " );
369404 }
370405 require (rollupDelay || l1MsgQueueDelayed, "invalid timing " );
371-
372- _commitBatchWithBatchData (batchDataInput, batchSignatureInput,0 );
406+ // check if the next batch has a stored blob hash
407+ (uint256 _batchPtr , ) = _loadBatchHeader (batchDataInput.parentBatchHeader);
408+ uint256 _nextBatchIndex = BatchHeaderCodecV0.getBatchIndex (_batchPtr) + 1 ;
409+ bytes32 _blobVersionedHash = bytes32 (0 );
410+ if (batchBlobVersionedHashes[_nextBatchIndex] != bytes32 (0 )) {
411+ require (blobhash (0 ) == bytes32 (0 ), "must not carry blob when using stored blob hash " );
412+ _blobVersionedHash = batchBlobVersionedHashes[_nextBatchIndex];
413+ } else {
414+ _blobVersionedHash = (blobhash (0 ) == bytes32 (0 )) ? ZERO_VERSIONED_HASH : blobhash (0 );
415+ }
416+ _commitBatchWithBatchData (batchDataInput, batchSignatureInput, 0 , _blobVersionedHash);
373417
374418 // get batch data from batch header
375419 (uint256 memPtr , bytes32 _batchHash ) = _loadBatchHeader (_batchHeader);
@@ -617,6 +661,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
617661
618662 delete batchDataStore[_batchIndex - 1 ];
619663 delete committedStateRoots[_batchIndex - 1 ];
664+ delete batchBlobVersionedHashes[_batchIndex - 1 ];
620665 delete challenges[_batchIndex - 1 ];
621666
622667 emit FinalizeBatch (
0 commit comments