Skip to content
Merged
5 changes: 5 additions & 0 deletions contracts/contracts/l1/rollup/IL1MessageQueue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ interface IL1MessageQueue {
/// @notice The start index of all pending inclusion messages.
function pendingQueueIndex() external view returns (uint256);

/// @notice Return the enqueue timestamp of the first unfinalized message.
/// @dev Used for checking if L1 messages are being processed within acceptable time.
/// @return timestamp The block.timestamp when the first unfinalized message was enqueued.
function getFirstUnfinalizedMessageEnqueueTime() external view returns (uint256 timestamp);

/// @notice Return the index of next appended message.
/// @dev Also the total number of appended messages.
function nextCrossDomainMessageIndex() external view returns (uint256);
Expand Down
8 changes: 8 additions & 0 deletions contracts/contracts/l1/rollup/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ interface IRollup {
/// @notice error zero address
error ErrZeroAddress();

/// @notice error invalid timing for permissionless batch submission
error InvalidTiming();

/**********
* Events *
**********/
Expand All @@ -86,6 +89,11 @@ interface IRollup {
/// @param batchHash The hash of the batch
event RevertBatch(uint256 indexed batchIndex, bytes32 indexed batchHash);

/// @notice Emitted when a range of batches is reverted in commitBatchWithProof.
/// @param startBatchIndex The starting batch index (inclusive).
/// @param count The number of batches reverted.
event RevertBatchRange(uint256 indexed startBatchIndex, uint256 count);

/// @notice Emitted when a batch is finalized.
/// @param batchIndex The index of the batch.
/// @param batchHash The hash of the batch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ contract L1MessageQueueWithGasPriceOracle is OwnableUpgradeable, IL1MessageQueue
/// @inheritdoc IL1MessageQueueWithGasPriceOracle
address public whitelistChecker;

mapping(uint256 => uint256) public messageEnqueueTime;

/**********************
* Function Modifiers *
**********************/
Expand Down Expand Up @@ -260,6 +262,14 @@ contract L1MessageQueueWithGasPriceOracle is OwnableUpgradeable, IL1MessageQueue
return hash;
}

function getFirstUnfinalizedMessageEnqueueTime() external view returns (uint256 timestamp) {
return messageEnqueueTime[pendingQueueIndex];
}

function getMessageEnqueueTimestamp(uint256 index) external view returns (uint256 timestamp) {
return messageEnqueueTime[index];
}

/*****************************
* Public Mutating Functions *
*****************************/
Expand Down Expand Up @@ -363,6 +373,7 @@ contract L1MessageQueueWithGasPriceOracle is OwnableUpgradeable, IL1MessageQueue
uint256 _queueIndex = messageQueue.length;
bytes32 _hash = computeTransactionHash(_sender, _queueIndex, _value, _target, _gasLimit, _data);
messageQueue.push(_hash);
messageEnqueueTime[_queueIndex] = block.timestamp;

// emit event
emit QueueTransaction(_sender, _target, _value, uint64(_queueIndex), _gasLimit, _data);
Expand Down
109 changes: 106 additions & 3 deletions contracts/contracts/l1/rollup/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
/// @notice committedStateRoots
mapping(uint256 batchIndex => bytes32 stateRoot) public committedStateRoots;

uint256 public rollupDelayPeird;

Comment thread
Kukoomomo marked this conversation as resolved.
Outdated
Comment thread
coderabbitai[bot] marked this conversation as resolved.
/**********************
* Function Modifiers *
**********************/
Expand Down Expand Up @@ -220,6 +222,13 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
BatchDataInput calldata batchDataInput,
BatchSignatureInput calldata batchSignatureInput
) external payable override onlyActiveStaker nonReqRevert whenNotPaused {
_commitBatchWithBatchData(batchDataInput, batchSignatureInput);
}
Comment thread
Kukoomomo marked this conversation as resolved.

function _commitBatchWithBatchData(
BatchDataInput calldata batchDataInput,
BatchSignatureInput calldata batchSignatureInput
) internal {
require(batchDataInput.version == 0 || batchDataInput.version == 1, "invalid version");
require(batchDataInput.prevStateRoot != bytes32(0), "previous state root is zero");
require(batchDataInput.postStateRoot != bytes32(0), "new state root is zero");
Expand Down Expand Up @@ -259,7 +268,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
}
bytes32 _blobVersionedHash = (blobhash(0) == bytes32(0)) ? ZERO_VERSIONED_HASH : blobhash(0);

{
{
uint256 _headerLength = BatchHeaderCodecV0.BATCH_HEADER_LENGTH;
if (batchDataInput.version == 1) {
_headerLength = BatchHeaderCodecV1.BATCH_HEADER_LENGTH;
Expand Down Expand Up @@ -318,6 +327,52 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
emit CommitBatch(_batchIndex, committedBatches[_batchIndex]);
}

function commitBatchWithProof(
BatchDataInput calldata batchDataInput,
BatchSignatureInput calldata batchSignatureInput,
bytes calldata _batchHeader,
bytes calldata _batchProof
) external payable nonReqRevert whenNotPaused {
require(!inChallenge, "already in challenge");
(uint256 _parentBatchPtr, ) = _loadBatchHeader(batchDataInput.parentBatchHeader);
uint256 _parentBatchIndex = BatchHeaderCodecV0.getBatchIndex(_parentBatchPtr);
require(_parentBatchIndex == lastFinalizedBatchIndex, "incorrect batch index");

// check delay timing - allow if EITHER batch submission OR L1 message processing is stalled
// This enables permissionless batch submission when sequencers are offline or censoring
if (batchDataStore[lastCommittedBatchIndex].originTimestamp + rollupDelayPeird >= block.timestamp &&
IL1MessageQueue(messageQueue).getFirstUnfinalizedMessageEnqueueTime() + rollupDelayPeird >= block.timestamp
) {
revert InvalidTiming();
}
// revert batch from the parent batch to the last committed batch
uint256 revertCount = lastCommittedBatchIndex - _parentBatchIndex;
if (revertCount > 0) {
uint256 startBatchIndex = _parentBatchIndex + 1;
for (uint256 i = startBatchIndex; i <= lastCommittedBatchIndex; i++) {
committedBatches[i] = bytes32(0);
}
emit RevertBatchRange(startBatchIndex, revertCount);
}
lastCommittedBatchIndex = _parentBatchIndex;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

_commitBatchWithBatchData(batchDataInput, batchSignatureInput);

// get batch data from batch header
(uint256 memPtr, bytes32 _batchHash) = _loadBatchHeader(_batchHeader);
// check batch hash
uint256 _batchIndex = BatchHeaderCodecV0.getBatchIndex(memPtr);
require(committedBatches[_batchIndex] == _batchHash, "incorrect batch hash");
Comment thread
Kukoomomo marked this conversation as resolved.

// verify consistency between batchDataInput and batchHeader
_verifyBatchConsistency(batchDataInput, memPtr);
Comment thread
Kukoomomo marked this conversation as resolved.
Outdated

// verify proof
_verifyProof(memPtr, _batchProof);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// finalize batch
finalizeBatch(_batchHeader);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}

/// @inheritdoc IRollup
/// @dev If the owner wants to revert a sequence of batches by sending multiple transactions,
/// make sure to revert recent batches first.
Expand Down Expand Up @@ -477,7 +532,10 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
*****************************/

/// @dev proveState proves a batch by submitting a proof.
function proveState(bytes calldata _batchHeader, bytes calldata _batchProof) external nonReqRevert whenNotPaused onlyActiveStaker{
function proveState(
bytes calldata _batchHeader,
bytes calldata _batchProof
) external nonReqRevert whenNotPaused onlyActiveStaker {
// get batch data from batch header
(uint256 memPtr, bytes32 _batchHash) = _loadBatchHeader(_batchHeader);
// check batch hash
Expand Down Expand Up @@ -614,6 +672,51 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
}
}

/// @dev Internal function to verify consistency between BatchDataInput and batch header.
/// @param batchDataInput The batch data input from the caller.
/// @param memPtr The memory pointer to the loaded batch header.
function _verifyBatchConsistency(
BatchDataInput calldata batchDataInput,
uint256 memPtr
) private pure {
// verify version
require(
batchDataInput.version == BatchHeaderCodecV0.getVersion(memPtr),
"batch version mismatch"
);

// verify number of L1 messages
require(
batchDataInput.numL1Messages == BatchHeaderCodecV0.getL1MessagePopped(memPtr),
"l1 message count mismatch"
);

// verify previous state root
require(
batchDataInput.prevStateRoot == BatchHeaderCodecV0.getPrevStateHash(memPtr),
"prev state root mismatch"
);

// verify post state root
require(
batchDataInput.postStateRoot == BatchHeaderCodecV0.getPostStateHash(memPtr),
"post state root mismatch"
);

// verify withdrawal root
require(
batchDataInput.withdrawalRoot == BatchHeaderCodecV0.getWithdrawRootHash(memPtr),
"withdrawal root mismatch"
);

// verify parent batch hash
(, bytes32 _parentBatchHash) = _loadBatchHeader(batchDataInput.parentBatchHeader);
require(
_parentBatchHash == BatchHeaderCodecV0.getParentBatchHash(memPtr),
"parent batch hash mismatch"
);
}

/// @dev Internal function to verify the zk proof.
function _verifyProof(uint256 memPtr, bytes calldata _batchProof) private view {
// Check validity of proof
Expand Down Expand Up @@ -727,7 +830,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable {
if (_version == 0) {
(_memPtr, _length) = BatchHeaderCodecV0.loadAndValidate(_batchHeader);
} else if (_version == 1) {
(_memPtr, _length) = BatchHeaderCodecV1.loadAndValidate(_batchHeader);
(_memPtr, _length) = BatchHeaderCodecV1.loadAndValidate(_batchHeader);
} else {
revert("Unsupported batch version");
}
Expand Down
Loading
Loading