mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-11 15:08:09 -05:00
Compare commits
1 Commits
test/code
...
feat/enfor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c9d6e1143 |
@@ -393,6 +393,62 @@ contract ScrollChain is OwnableUpgradeable, IScrollChain {
|
||||
emit UpdateMaxNumL2TxInChunk(_oldMaxNumL2TxInChunk, _maxNumL2TxInChunk);
|
||||
}
|
||||
|
||||
function forceFinalizeBatch(
|
||||
bytes calldata _batchHeader,
|
||||
bytes32 _prevStateRoot,
|
||||
bytes32 _postStateRoot,
|
||||
bytes32 _withdrawRoot
|
||||
) external onlyOwner {
|
||||
require(_prevStateRoot != bytes32(0), "previous state root is zero");
|
||||
require(_postStateRoot != bytes32(0), "new state root is zero");
|
||||
|
||||
// compute batch hash and verify
|
||||
(uint256 memPtr, bytes32 _batchHash) = _loadBatchHeader(_batchHeader);
|
||||
|
||||
uint256 _batchIndex = BatchHeaderV0Codec.batchIndex(memPtr);
|
||||
require(committedBatches[_batchIndex] == _batchHash, "incorrect batch hash");
|
||||
|
||||
// verify previous state root.
|
||||
require(finalizedStateRoots[_batchIndex - 1] == _prevStateRoot, "incorrect previous state root");
|
||||
|
||||
// avoid duplicated verification
|
||||
require(finalizedStateRoots[_batchIndex] == bytes32(0), "batch already verified");
|
||||
|
||||
// check and update lastFinalizedBatchIndex
|
||||
unchecked {
|
||||
require(lastFinalizedBatchIndex + 1 == _batchIndex, "incorrect batch index");
|
||||
lastFinalizedBatchIndex = _batchIndex;
|
||||
}
|
||||
|
||||
// record state root and withdraw root
|
||||
finalizedStateRoots[_batchIndex] = _postStateRoot;
|
||||
withdrawRoots[_batchIndex] = _withdrawRoot;
|
||||
|
||||
// Pop finalized and non-skipped message from L1MessageQueue.
|
||||
uint256 _l1MessagePopped = BatchHeaderV0Codec.l1MessagePopped(memPtr);
|
||||
if (_l1MessagePopped > 0) {
|
||||
IL1MessageQueue _queue = IL1MessageQueue(messageQueue);
|
||||
|
||||
unchecked {
|
||||
uint256 _startIndex = BatchHeaderV0Codec.totalL1MessagePopped(memPtr) - _l1MessagePopped;
|
||||
|
||||
for (uint256 i = 0; i < _l1MessagePopped; i += 256) {
|
||||
uint256 _count = 256;
|
||||
if (_l1MessagePopped - i < _count) {
|
||||
_count = _l1MessagePopped - i;
|
||||
}
|
||||
uint256 _skippedBitmap = BatchHeaderV0Codec.skippedBitmap(memPtr, i / 256);
|
||||
|
||||
_queue.popCrossDomainMessage(_startIndex, _count, _skippedBitmap);
|
||||
|
||||
_startIndex += 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* Internal Functions *
|
||||
**********************/
|
||||
|
||||
@@ -446,6 +446,75 @@ contract ScrollChainTest is DSTestPlus {
|
||||
}
|
||||
}
|
||||
|
||||
function testForceFinalizeBatch() public {
|
||||
// caller not owner, revert
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert("Ownable: caller is not the owner");
|
||||
rollup.forceFinalizeBatch(new bytes(0), bytes32(0), bytes32(0), bytes32(0));
|
||||
hevm.stopPrank();
|
||||
|
||||
rollup.updateSequencer(address(this), true);
|
||||
|
||||
bytes memory batchHeader0 = new bytes(89);
|
||||
|
||||
// import genesis batch
|
||||
assembly {
|
||||
mstore(add(batchHeader0, add(0x20, 25)), 1)
|
||||
}
|
||||
rollup.importGenesisBatch(batchHeader0, bytes32(uint256(1)));
|
||||
bytes32 batchHash0 = rollup.committedBatches(0);
|
||||
|
||||
bytes[] memory chunks = new bytes[](1);
|
||||
bytes memory chunk0;
|
||||
|
||||
// commit one batch
|
||||
chunk0 = new bytes(1 + 60);
|
||||
chunk0[0] = bytes1(uint8(1)); // one block in this chunk
|
||||
chunks[0] = chunk0;
|
||||
rollup.commitBatch(0, batchHeader0, chunks, new bytes(0));
|
||||
assertGt(uint256(rollup.committedBatches(1)), 0);
|
||||
|
||||
bytes memory batchHeader1 = new bytes(89);
|
||||
assembly {
|
||||
mstore(add(batchHeader1, 0x20), 0) // version
|
||||
mstore(add(batchHeader1, add(0x20, 1)), shl(192, 1)) // batchIndex
|
||||
mstore(add(batchHeader1, add(0x20, 9)), 0) // l1MessagePopped
|
||||
mstore(add(batchHeader1, add(0x20, 17)), 0) // totalL1MessagePopped
|
||||
mstore(add(batchHeader1, add(0x20, 25)), 0x246394445f4fe64ed5598554d55d1682d6fb3fe04bf58eb54ef81d1189fafb51) // dataHash
|
||||
mstore(add(batchHeader1, add(0x20, 57)), batchHash0) // parentBatchHash
|
||||
}
|
||||
|
||||
// incorrect batch hash, revert
|
||||
hevm.expectRevert("incorrect batch hash");
|
||||
batchHeader1[0] = bytes1(uint8(1)); // change version to 1
|
||||
rollup.forceFinalizeBatch(batchHeader1, bytes32(uint256(1)), bytes32(uint256(2)), bytes32(0));
|
||||
batchHeader1[0] = bytes1(uint8(0)); // change back
|
||||
|
||||
// batch header length too small, revert
|
||||
hevm.expectRevert("batch header length too small");
|
||||
rollup.forceFinalizeBatch(new bytes(88), bytes32(uint256(1)), bytes32(uint256(2)), bytes32(0));
|
||||
|
||||
// wrong bitmap length, revert
|
||||
hevm.expectRevert("wrong bitmap length");
|
||||
rollup.forceFinalizeBatch(new bytes(90), bytes32(uint256(1)), bytes32(uint256(2)), bytes32(0));
|
||||
|
||||
// incorrect previous state root, revert
|
||||
hevm.expectRevert("incorrect previous state root");
|
||||
rollup.forceFinalizeBatch(batchHeader1, bytes32(uint256(2)), bytes32(uint256(2)), bytes32(0));
|
||||
|
||||
// verify success
|
||||
assertBoolEq(rollup.isBatchFinalized(1), false);
|
||||
rollup.forceFinalizeBatch(batchHeader1, bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3)));
|
||||
assertBoolEq(rollup.isBatchFinalized(1), true);
|
||||
assertEq(rollup.finalizedStateRoots(1), bytes32(uint256(2)));
|
||||
assertEq(rollup.withdrawRoots(1), bytes32(uint256(3)));
|
||||
assertEq(rollup.lastFinalizedBatchIndex(), 1);
|
||||
|
||||
// batch already verified, revert
|
||||
hevm.expectRevert("batch already verified");
|
||||
rollup.forceFinalizeBatch(batchHeader1, bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3)));
|
||||
}
|
||||
|
||||
function testRevertBatch() public {
|
||||
// caller not owner, revert
|
||||
hevm.startPrank(address(1));
|
||||
|
||||
Reference in New Issue
Block a user