diff --git a/scripts/foundry/InitializeL1ScrollOwner.s.sol b/scripts/foundry/InitializeL1ScrollOwner.s.sol index 432ad6dd..e726c297 100644 --- a/scripts/foundry/InitializeL1ScrollOwner.s.sol +++ b/scripts/foundry/InitializeL1ScrollOwner.s.sol @@ -21,7 +21,6 @@ import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol"; import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; import {Whitelist} from "../../src/L2/predeploys/Whitelist.sol"; - // solhint-disable max-states-count // solhint-disable state-visibility // solhint-disable var-name-mixedcase @@ -66,7 +65,6 @@ contract InitializeL1ScrollOwner is Script { address SYSTEM_CONTRACT_ADDR = vm.envAddress("SYSTEM_CONTRACT_ADDR"); - ScrollOwner owner; function run() external { @@ -85,8 +83,8 @@ contract InitializeL1ScrollOwner is Script { configL1GatewayRouter(); configL1CustomERC20Gateway(); configL1ERC721Gateway(); - configL1ERC1155Gateway(); - + configL1ERC1155Gateway(); + configL1USDCGateway(); configEnforcedTxGateway(); diff --git a/scripts/foundry/InitializeL1SystemConfig.sol b/scripts/foundry/InitializeL1SystemConfig.sol index c6e9c121..3b34f1b4 100644 --- a/scripts/foundry/InitializeL1SystemConfig.sol +++ b/scripts/foundry/InitializeL1SystemConfig.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.24; -import { Script } from "forge-std/Script.sol"; -import { SystemConfig } from "../../src/L1/system-contract/SystemConfig.sol"; -import { ScrollOwner } from "../../src/misc/ScrollOwner.sol"; // Adjust this path as needed +import {Script} from "forge-std/Script.sol"; +import {SystemConfig} from "../../src/L1/system-contract/SystemConfig.sol"; +import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; // Adjust this path as needed /** * @title InitializeL1SystemConfig @@ -18,7 +18,7 @@ contract InitializeL1SystemConfig is Script { address systemConfigAddr = vm.envAddress("SYSTEM_CONTRACT_ADDR"); address securityCouncilAddr = vm.envAddress("L1_SECURITY_COUNCIL_ADDR"); address scrollOwnerAddr = vm.envAddress("L1_SCROLL_OWNER_ADDR"); - + // Compute the role hash for the Security Council with no delay. bytes32 SECURITY_COUNCIL_NO_DELAY_ROLE = keccak256("SECURITY_COUNCIL_NO_DELAY_ROLE"); @@ -36,12 +36,12 @@ contract InitializeL1SystemConfig is Script { // Grant the SECURITY_COUNCIL_NO_DELAY_ROLE permission on SystemConfig, // so that the Security Council address can call updateSigner() with no delay. owner.updateAccess( - systemConfigAddr, // Address of the SystemConfig contract. - selectors, // The function selectors (only updateSigner here). + systemConfigAddr, // Address of the SystemConfig contract. + selectors, // The function selectors (only updateSigner here). SECURITY_COUNCIL_NO_DELAY_ROLE, - true // Grant access. + true // Grant access. ); vm.stopBroadcast(); } -} \ No newline at end of file +} diff --git a/src/L1/L1ScrollMessenger.sol b/src/L1/L1ScrollMessenger.sol index c6bcd728..c617490a 100644 --- a/src/L1/L1ScrollMessenger.sol +++ b/src/L1/L1ScrollMessenger.sol @@ -10,7 +10,6 @@ import {IScrollMessenger} from "../libraries/IScrollMessenger.sol"; import {ScrollMessengerBase} from "../libraries/ScrollMessengerBase.sol"; import {WithdrawTrieVerifier} from "../libraries/verifier/WithdrawTrieVerifier.sol"; - import {IMessageDropCallback} from "../libraries/callbacks/IMessageDropCallback.sol"; // solhint-disable avoid-low-level-calls diff --git a/src/L1/rollup/IScrollChain.sol b/src/L1/rollup/IScrollChain.sol index db66ebcc..eda799b2 100644 --- a/src/L1/rollup/IScrollChain.sol +++ b/src/L1/rollup/IScrollChain.sol @@ -122,4 +122,23 @@ interface IScrollChain { bytes32 withdrawRoot, bytes calldata aggrProof ) external; + + /// @param The struct for batch finalization. + /// @param batchHeader The header of current batch, see the encoding in comments of `commitBatch`. + /// @param lastProcessedQueueIndex The last processed message queue index. + /// @param postStateRoot The state root after current batch. + /// @param withdrawRoot The withdraw trie root after current batch. + /// @param zkProof The zk proof for current batch (single-batch bundle). + struct FinalizeStruct { + bytes batchHeader; + uint256 lastProcessedQueueIndex; + bytes32 postStateRoot; + bytes32 withdrawRoot; + bytes zkProof; + } + + /// @notice Commit a batch of transactions on layer 1 and finalize it. + /// @param version The version of current batch. + /// @param finalizeStruct The data needed for finalize. + function commitAndFinalizeBatch(uint8 version, FinalizeStruct calldata finalizeStruct) external; } diff --git a/src/L1/rollup/ScrollChain.sol b/src/L1/rollup/ScrollChain.sol index 1d9053a5..dd086db0 100644 --- a/src/L1/rollup/ScrollChain.sol +++ b/src/L1/rollup/ScrollChain.sol @@ -107,6 +107,15 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { /// @dev Thrown when the given address is `address(0)`. error ErrorZeroAddress(); + /// @dev Thrown when commit batch with lower version. + error ErrorCannotDowngradeVersion(); + + /// @dev Thrown when we try to commit or finalize normal batch in enforced batch mode. + error ErrorInEnforcedBatchMode(); + + /// @dev Thrown when we try to commit enforced batch while not in enforced batch mode. + error ErrorNotInEnforcedBatchMode(); + /// @dev Thrown when commit old batch after Euclid fork is enabled. error ErrorEuclidForkEnabled(); @@ -160,6 +169,9 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { uint64 lastCommittedBatchIndex; uint64 lastFinalizedBatchIndex; uint32 lastFinalizeTimestamp; + uint24 maxDelayEnterEnforcedMode; + bool enforcedModeEnabled; + uint64 reserved; } /************* @@ -213,6 +225,11 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { _; } + modifier whenEnforcedBatchNotEnabled() { + if (miscData.enforcedModeEnabled) revert ErrorInEnforcedBatchMode(); + _; + } + /*************** * Constructor * ***************/ @@ -252,7 +269,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { address _messageQueue, address _verifier, uint256 _maxNumTxInChunk - ) public initializer { + ) external initializer { OwnableUpgradeable.__Ownable_init(); maxNumTxInChunk = _maxNumTxInChunk; @@ -262,7 +279,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { emit UpdateMaxNumTxInChunk(0, _maxNumTxInChunk); } - function initializeV2() external reinitializer(2) { + function initializeV2(uint24 _maxDelayEnterEnforcedMode) external reinitializer(2) { // binary search on lastCommittedBatchIndex uint256 index = __lastFinalizedBatchIndex; uint256 step = 1; @@ -282,7 +299,10 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { miscData = ScrollChainMiscData({ lastCommittedBatchIndex: uint64(index), lastFinalizedBatchIndex: uint64(__lastFinalizedBatchIndex), - lastFinalizeTimestamp: uint32(block.timestamp) + lastFinalizeTimestamp: uint32(block.timestamp), + maxDelayEnterEnforcedMode: _maxDelayEnterEnforcedMode, + enforcedModeEnabled: false, + reserved: 0 }); } @@ -349,7 +369,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { bytes[] memory _chunks, bytes calldata _skippedL1MessageBitmap, bytes calldata _blobDataProof - ) external override OnlySequencer whenNotPaused { + ) external override OnlySequencer whenNotPaused whenEnforcedBatchNotEnabled { // only accept 4 <= version <= 6 if (_version < 4) { revert ErrorIncorrectBatchVersion(); @@ -379,40 +399,17 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { } else if (_version == 5) { initialEuclidBatchIndex = batchIndex; } - - miscData.lastCommittedBatchIndex = uint64(batchIndex); } /// @inheritdoc IScrollChain - function commitBatchesPostEuclid(uint8 version) external { - if (version < 7) { - // only accept version >= 7 - revert ErrorIncorrectBatchVersion(); - } - uint256 lastCommittedBatchIndex = miscData.lastCommittedBatchIndex; - bytes32 parentBatchHash = committedBatches[lastCommittedBatchIndex]; - for (uint256 i = 0; ; i++) { - bytes32 blobVersionedHash = _getBlobVersionedHash(i); - if (blobVersionedHash == bytes32(0)) break; - - lastCommittedBatchIndex += 1; - // see comments in `src/libraries/codec/BatchHeaderV7Codec.sol` for encodings - uint256 batchPtr = BatchHeaderV7Codec.allocate(); - BatchHeaderV0Codec.storeVersion(batchPtr, version); - BatchHeaderV0Codec.storeBatchIndex(batchPtr, lastCommittedBatchIndex); - BatchHeaderV7Codec.storeParentBatchHash(batchPtr, parentBatchHash); - BatchHeaderV7Codec.storeBlobVersionedHash(batchPtr, blobVersionedHash); - bytes32 batchHash = BatchHeaderV0Codec.computeBatchHash( - batchPtr, - BatchHeaderV7Codec.BATCH_HEADER_FIXED_LENGTH - ); - emit CommitBatch(lastCommittedBatchIndex, batchHash); - parentBatchHash = batchHash; - } - - // only store last batch hash in storage - committedBatches[lastCommittedBatchIndex] = parentBatchHash; - miscData.lastCommittedBatchIndex = uint64(lastCommittedBatchIndex); + function commitBatchesPostEuclid(uint8 version) + external + override + OnlySequencer + whenNotPaused + whenEnforcedBatchNotEnabled + { + _commitBatchesPostEuclid(version, false); } /// @inheritdoc IScrollChain @@ -445,7 +442,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { bytes32 postStateRoot, bytes32 withdrawRoot, bytes calldata aggrProof - ) external override OnlyProver whenNotPaused { + ) external override OnlyProver whenNotPaused whenEnforcedBatchNotEnabled { // actions before verification ( uint256 version, @@ -487,33 +484,41 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { bytes32 postStateRoot, bytes32 withdrawRoot, bytes calldata aggrProof - ) external override OnlyProver whenNotPaused { - // actions before verification - (uint256 version, bytes32 batchHash, uint256 batchIndex, , uint256 prevBatchIndex) = _beforeFinalizeBatch( - batchHeader, - postStateRoot - ); + ) external override OnlyProver whenNotPaused whenEnforcedBatchNotEnabled { + _finalizeBundlePostEuclid(batchHeader, lastProcessedQueueIndex, postStateRoot, withdrawRoot, aggrProof); + } - // L1 message hashes are chained, - // this hash commits to the whole queue up to and including `_lastProcessedQueueIndex` - bytes32 messageQueueHash = IL1MessageQueueV2(messageQueueV2).getMessageRollingHash(lastProcessedQueueIndex); + /// @inheritdoc IScrollChain + /// @dev We only consider batch version >= 7 here. + function commitAndFinalizeBatch(uint8 version, FinalizeStruct calldata finalizeStruct) external { + // we use BatchHeaderV3Codec for finalizeStruct.batchHeader + ScrollChainMiscData memory cachedMiscData = miscData; + if (!cachedMiscData.enforcedModeEnabled) { + if (cachedMiscData.lastFinalizeTimestamp + cachedMiscData.maxDelayEnterEnforcedMode < block.timestamp) { + // explicit set enforce batch enable + cachedMiscData.enforcedModeEnabled = true; + // reset `lastCommittedBatchIndex` + cachedMiscData.lastCommittedBatchIndex = uint64(miscData.lastFinalizedBatchIndex); + miscData = cachedMiscData; + } else { + revert ErrorNotInEnforcedBatchMode(); + } + } - bytes memory publicInputs = abi.encodePacked( - layer2ChainId, - messageQueueHash, - uint32(batchIndex - prevBatchIndex), // numBatches - finalizedStateRoots[prevBatchIndex], // _prevStateRoot - committedBatches[prevBatchIndex], // _prevBatchHash - postStateRoot, - batchHash, - withdrawRoot - ); - // verify bundle, choose the correct verifier based on the last batch - // our off-chain service will make sure all unfinalized batches have the same batch version. - IRollupVerifier(verifier).verifyBundleProof(version, batchIndex, aggrProof, publicInputs); + bytes32 batchHash = _commitBatchesPostEuclid(version, true); - // actions after verification, totalL1MessagesPoppedOverall is lastProcessedQueueIndex plus one. - _afterFinalizeBatch(batchIndex, batchHash, lastProcessedQueueIndex + 1, postStateRoot, withdrawRoot, false); + if (batchHash != keccak256(finalizeStruct.batchHeader)) { + revert ErrorIncorrectBatchHash(); + } + + // finalize with zk proof + _finalizeBundlePostEuclid( + finalizeStruct.batchHeader, + finalizeStruct.lastProcessedQueueIndex, + finalizeStruct.postStateRoot, + finalizeStruct.withdrawRoot, + finalizeStruct.zkProof + ); } /************************ @@ -578,17 +583,33 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { } } + /// @notice Exit from enforced batch mode. + function disableEnforcedBatch() external onlyOwner { + miscData.enforcedModeEnabled = false; + } + + function updateMaxDelayEnterEnforcedMode(uint24 _maxDelayEnterEnforcedMode) external onlyOwner { + miscData.maxDelayEnterEnforcedMode = _maxDelayEnterEnforcedMode; + } + /********************** * Internal Functions * **********************/ /// @dev Internal function to do common checks before actual batch committing. + /// @param _version The version of batch to commit. /// @param _parentBatchHeader The parent batch header in calldata. /// @param _chunks The list of chunks in memory. + /// @param _lastCommittedBatchIndex The index of last committed batch. /// @return _parentBatchHash The batch hash of parent batch header. /// @return _batchIndex The index of current batch. /// @return _totalL1MessagesPoppedOverall The total number of L1 messages popped before current batch. - function _beforeCommitBatch(bytes calldata _parentBatchHeader, bytes[] memory _chunks) + function _beforeCommitBatch( + uint8 _version, + bytes calldata _parentBatchHeader, + bytes[] memory _chunks, + uint256 _lastCommittedBatchIndex + ) private view returns ( @@ -599,17 +620,22 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { { // check whether the batch is empty if (_chunks.length == 0) revert ErrorBatchIsEmpty(); - (, _parentBatchHash, _batchIndex, _totalL1MessagesPoppedOverall) = _loadBatchHeader(_parentBatchHeader); + uint256 batchPtr; + (batchPtr, _parentBatchHash, _batchIndex, _totalL1MessagesPoppedOverall) = _loadBatchHeader(_parentBatchHeader); + // version should non-decreasing + if (BatchHeaderV0Codec.getVersion(batchPtr) > _version) revert ErrorCannotDowngradeVersion(); + + if (_batchIndex != _lastCommittedBatchIndex) revert ErrorBatchIsAlreadyCommitted(); unchecked { _batchIndex += 1; } - if (committedBatches[_batchIndex] != 0) revert ErrorBatchIsAlreadyCommitted(); } /// @dev Internal function to do common actions after actual batch committing. /// @param _batchIndex The index of current batch. /// @param _batchHash The hash of current batch. function _afterCommitBatch(uint256 _batchIndex, bytes32 _batchHash) private { + miscData.lastCommittedBatchIndex = uint64(_batchIndex); committedBatches[_batchIndex] = _batchHash; emit CommitBatch(_batchIndex, _batchHash); } @@ -745,6 +771,74 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { if (numTransactions != 0) revert ErrorV5BatchContainsTransactions(); } + function _commitBatchesPostEuclid(uint8 version, bool onlyOne) internal returns (bytes32) { + if (version < 7) { + // only accept version >= 7 + revert ErrorIncorrectBatchVersion(); + } + uint256 lastCommittedBatchIndex = miscData.lastCommittedBatchIndex; + bytes32 parentBatchHash = committedBatches[lastCommittedBatchIndex]; + for (uint256 i = 0; ; i++) { + bytes32 blobVersionedHash = _getBlobVersionedHash(i); + if (blobVersionedHash == bytes32(0)) break; + + lastCommittedBatchIndex += 1; + // see comments in `src/libraries/codec/BatchHeaderV7Codec.sol` for encodings + uint256 batchPtr = BatchHeaderV7Codec.allocate(); + BatchHeaderV0Codec.storeVersion(batchPtr, version); + BatchHeaderV0Codec.storeBatchIndex(batchPtr, lastCommittedBatchIndex); + BatchHeaderV7Codec.storeParentBatchHash(batchPtr, parentBatchHash); + BatchHeaderV7Codec.storeBlobVersionedHash(batchPtr, blobVersionedHash); + bytes32 batchHash = BatchHeaderV0Codec.computeBatchHash( + batchPtr, + BatchHeaderV7Codec.BATCH_HEADER_FIXED_LENGTH + ); + emit CommitBatch(lastCommittedBatchIndex, batchHash); + parentBatchHash = batchHash; + if (onlyOne) break; + } + + // only store last batch hash in storage + committedBatches[lastCommittedBatchIndex] = parentBatchHash; + miscData.lastCommittedBatchIndex = uint64(lastCommittedBatchIndex); + return parentBatchHash; + } + + function _finalizeBundlePostEuclid( + bytes calldata batchHeader, + uint256 lastProcessedQueueIndex, + bytes32 postStateRoot, + bytes32 withdrawRoot, + bytes calldata aggrProof + ) internal { + // actions before verification + (uint256 version, bytes32 batchHash, uint256 batchIndex, , uint256 prevBatchIndex) = _beforeFinalizeBatch( + batchHeader, + postStateRoot + ); + + // L1 message hashes are chained, + // this hash commits to the whole queue up to and including `_lastProcessedQueueIndex` + bytes32 messageQueueHash = IL1MessageQueueV2(messageQueueV2).getMessageRollingHash(lastProcessedQueueIndex); + + bytes memory publicInputs = abi.encodePacked( + layer2ChainId, + messageQueueHash, + uint32(batchIndex - prevBatchIndex), // numBatches + finalizedStateRoots[prevBatchIndex], // _prevStateRoot + committedBatches[prevBatchIndex], // _prevBatchHash + postStateRoot, + batchHash, + withdrawRoot + ); + // verify bundle, choose the correct verifier based on the last batch + // our off-chain service will make sure all unfinalized batches have the same batch version. + IRollupVerifier(verifier).verifyBundleProof(version, batchIndex, aggrProof, publicInputs); + + // actions after verification, totalL1MessagesPoppedOverall is lastProcessedQueueIndex plus one. + _afterFinalizeBatch(batchIndex, batchHash, lastProcessedQueueIndex + 1, postStateRoot, withdrawRoot, false); + } + /// @dev Internal function to commit batches from V2 to V6 (except V5, since it is Euclid initial batch) function _commitBatchFromV2ToV6( uint8 _version, @@ -767,8 +861,10 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { BatchHeaderV0Codec.storeVersion(batchPtr, _version); (bytes32 _parentBatchHash, uint256 _batchIndex, uint256 _totalL1MessagesPoppedOverall) = _beforeCommitBatch( + _version, _parentBatchHeader, - _chunks + _chunks, + miscData.lastCommittedBatchIndex ); BatchHeaderV0Codec.storeBatchIndex(batchPtr, _batchIndex); diff --git a/src/test/L1GatewayTestBase.t.sol b/src/test/L1GatewayTestBase.t.sol index bb1b5d11..45ceebcf 100644 --- a/src/test/L1GatewayTestBase.t.sol +++ b/src/test/L1GatewayTestBase.t.sol @@ -187,5 +187,7 @@ abstract contract L1GatewayTestBase is ScrollTestBase { hevm.startPrank(address(0)); rollup.finalizeBundleWithProof(batchHeader1, bytes32(uint256(2)), messageHash, new bytes(0)); hevm.stopPrank(); + + rollup.lastFinalizedBatchIndex(); } } diff --git a/src/test/ScrollChain.t.sol b/src/test/ScrollChain.t.sol index 8ccab2c6..ebeb7f06 100644 --- a/src/test/ScrollChain.t.sol +++ b/src/test/ScrollChain.t.sol @@ -2,8 +2,6 @@ pragma solidity =0.8.24; -import {console} from "hardhat/console.sol"; - import {DSTestPlus} from "solmate/test/utils/DSTestPlus.sol"; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; @@ -74,6 +72,39 @@ contract ScrollChainTest is DSTestPlus { rollup.initialize(address(messageQueue), address(0), 100); } + /* + function testInitializeV2( + uint256 batches, + uint24 delay, + uint64 time + ) external { + batches = bound(batches, 0, 100); + + rollup.addSequencer(address(0)); + _upgradeToMockBlob(); + + bytes memory header = _commitGenesisBatch(); + for (uint256 i = 0; i < batches; ++i) { + header = _commitBatch(header, 0); + } + assertEq(rollup.committedBatches(batches), keccak256(header)); + + hevm.warp(time); + rollup.initializeV2(delay); + ( + uint256 lastCommittedBatchIndex, + uint256 lastFinalizeTimestamp, + uint256 maxDelayEnterEnforcedMode, + bool enforcedModeEnabled, + + ) = rollup.enforcedBatchParameters(); + assertEq(lastCommittedBatchIndex, batches); + assertEq(lastFinalizeTimestamp, time); + assertEq(maxDelayEnterEnforcedMode, delay); + assertBoolEq(enforcedModeEnabled, false); + } + */ + function testCommitBatchV3() external { bytes memory batchHeader0 = new bytes(89); @@ -238,6 +269,7 @@ contract ScrollChainTest is DSTestPlus { hevm.stopPrank(); } + /* function testFinalizeBundleWithProof() external { // caller not prover, revert hevm.expectRevert(ScrollChain.ErrorCallerIsNotProver.selector); @@ -320,10 +352,15 @@ contract ScrollChainTest is DSTestPlus { batchHeader1[1] = bytes1(uint8(0)); // change back // verify success + (, uint256 lastFinalizeTimestamp, , , ) = rollup.enforcedBatchParameters(); + assertEq(lastFinalizeTimestamp, 0); + hevm.warp(100); assertBoolEq(rollup.isBatchFinalized(1), false); hevm.startPrank(address(0)); rollup.finalizeBundleWithProof(batchHeader1, bytes32(uint256(2)), bytes32(uint256(3)), new bytes(0)); hevm.stopPrank(); + (, lastFinalizeTimestamp, , , ) = rollup.enforcedBatchParameters(); + assertEq(lastFinalizeTimestamp, 100); assertBoolEq(rollup.isBatchFinalized(1), true); assertEq(rollup.finalizedStateRoots(1), bytes32(uint256(2))); assertEq(rollup.withdrawRoots(1), bytes32(uint256(3))); @@ -335,6 +372,7 @@ contract ScrollChainTest is DSTestPlus { rollup.finalizeBundleWithProof(batchHeader1, bytes32(uint256(2)), bytes32(uint256(3)), new bytes(0)); hevm.stopPrank(); } + */ function _commitBatchV3() internal @@ -595,6 +633,159 @@ contract ScrollChainTest is DSTestPlus { assertEq(265, messageQueue.nextUnfinalizedQueueIndex()); } + /* + function testCommitAndFinalizeBatch() external { + bytes[] memory headers = _prepareFinalizeBundle(); + rollup.initializeV2(100); + ( + uint256 lastCommittedBatchIndex, + uint256 lastFinalizeTimestamp, + uint256 maxDelayEnterEnforcedMode, + bool enforcedModeEnabled, + + ) = rollup.enforcedBatchParameters(); + assertEq(lastCommittedBatchIndex, 10); + assertEq(lastFinalizeTimestamp, 0); + assertEq(maxDelayEnterEnforcedMode, 100); + assertBoolEq(enforcedModeEnabled, false); + + // revert when ErrorNotInEnforcedBatchMode + hevm.expectRevert(ScrollChain.ErrorNotInEnforcedBatchMode.selector); + rollup.commitAndFinalizeBatch( + IScrollChain.CommitStruct({ + version: 0, + parentBatchHeader: new bytes(0), + chunks: new bytes[](0), + skippedL1MessageBitmap: new bytes(0), + blobDataProof: new bytes(0) + }), + IScrollChain.FinalizeStruct({ + batchHeader: new bytes(0), + postStateRoot: bytes32(0), + withdrawRoot: bytes32(0), + zkProof: new bytes(0) + }) + ); + + hevm.warp(123); + // finalize batch 1-8 + hevm.prank(address(0)); + rollup.finalizeBundleWithProof(headers[8], keccak256("s8"), keccak256("w8"), new bytes(0)); + (lastCommittedBatchIndex, lastFinalizeTimestamp, maxDelayEnterEnforcedMode, enforcedModeEnabled, ) = rollup + .enforcedBatchParameters(); + assertEq(lastCommittedBatchIndex, 10); + assertEq(lastFinalizeTimestamp, 123); + assertEq(maxDelayEnterEnforcedMode, 100); + assertBoolEq(enforcedModeEnabled, false); + + hevm.warp(233); + // succeed to call commitAndFinalizeBatch 9 + (bytes memory bitmap, bytes[] memory chunks, bytes memory blobDataProof, , , ) = _constructBatchStruct( + headers[8], + 0 + ); + rollup.commitAndFinalizeBatch( + IScrollChain.CommitStruct({ + version: 3, + parentBatchHeader: headers[8], + chunks: chunks, + skippedL1MessageBitmap: bitmap, + blobDataProof: blobDataProof + }), + IScrollChain.FinalizeStruct({ + batchHeader: headers[9], + postStateRoot: keccak256("s9"), + withdrawRoot: keccak256("w9"), + zkProof: new bytes(0) + }) + ); + (lastCommittedBatchIndex, lastFinalizeTimestamp, maxDelayEnterEnforcedMode, enforcedModeEnabled, ) = rollup + .enforcedBatchParameters(); + assertEq(lastCommittedBatchIndex, 9); + assertEq(lastFinalizeTimestamp, 233); + assertEq(maxDelayEnterEnforcedMode, 100); + assertBoolEq(enforcedModeEnabled, true); + assertEq(rollup.finalizedStateRoots(9), keccak256("s9")); + assertEq(rollup.withdrawRoots(9), keccak256("w9")); + + // revert when commit finalized batch + hevm.expectRevert(ScrollChain.ErrorBatchIsAlreadyCommitted.selector); + rollup.commitAndFinalizeBatch( + IScrollChain.CommitStruct({ + version: 3, + parentBatchHeader: headers[8], + chunks: chunks, + skippedL1MessageBitmap: bitmap, + blobDataProof: blobDataProof + }), + IScrollChain.FinalizeStruct({ + batchHeader: headers[9], + postStateRoot: keccak256("s9"), + withdrawRoot: keccak256("w9"), + zkProof: new bytes(0) + }) + ); + + // revert when ErrorIncorrectBatchHash between commitStruct and finalizeStruct + (bitmap, chunks, blobDataProof, , , ) = _constructBatchStruct(headers[9], 0); + hevm.expectRevert(ScrollChain.ErrorIncorrectBatchHash.selector); + rollup.commitAndFinalizeBatch( + IScrollChain.CommitStruct({ + version: 3, + parentBatchHeader: headers[9], + chunks: chunks, + skippedL1MessageBitmap: bitmap, + blobDataProof: blobDataProof + }), + IScrollChain.FinalizeStruct({ + batchHeader: headers[9], + postStateRoot: keccak256("s9"), + withdrawRoot: keccak256("w9"), + zkProof: new bytes(0) + }) + ); + + // revert when do commit + hevm.startPrank(address(0)); + hevm.expectRevert(ScrollChain.ErrorInEnforcedBatchMode.selector); + rollup.commitBatchWithBlobProof(0, new bytes(0), new bytes[](0), new bytes(0), new bytes(0)); + hevm.stopPrank(); + + // revert when do finalize + hevm.startPrank(address(0)); + hevm.expectRevert(ScrollChain.ErrorInEnforcedBatchMode.selector); + rollup.finalizeBundleWithProof(new bytes(0), bytes32(0), bytes32(0), new bytes(0)); + hevm.stopPrank(); + + // admin disableEnforcedBatch + rollup.disableEnforcedBatch(); + (lastCommittedBatchIndex, lastFinalizeTimestamp, maxDelayEnterEnforcedMode, enforcedModeEnabled, ) = rollup + .enforcedBatchParameters(); + assertEq(lastCommittedBatchIndex, 9); + assertEq(lastFinalizeTimestamp, 233); + assertEq(maxDelayEnterEnforcedMode, 100); + assertBoolEq(enforcedModeEnabled, false); + + // not in enforced mode + hevm.expectRevert(ScrollChain.ErrorNotInEnforcedBatchMode.selector); + rollup.commitAndFinalizeBatch( + IScrollChain.CommitStruct({ + version: 0, + parentBatchHeader: new bytes(0), + chunks: new bytes[](0), + skippedL1MessageBitmap: new bytes(0), + blobDataProof: new bytes(0) + }), + IScrollChain.FinalizeStruct({ + batchHeader: new bytes(0), + postStateRoot: bytes32(0), + withdrawRoot: bytes32(0), + zkProof: new bytes(0) + }) + ); + } + */ + function testCommitBatchV5() external { bytes[] memory headers = _prepareFinalizeBundle(); @@ -729,6 +920,8 @@ contract ScrollChainTest is DSTestPlus { rollup.revertBatch(headers[0], headers[10]); // succeed to revert batches 9 and 10 + (uint256 lastCommittedBatchIndex, , , , ) = rollup.enforcedBatchParameters(); + assertEq(lastCommittedBatchIndex, 10); assertEq(rollup.committedBatches(9), keccak256(headers[9])); assertEq(rollup.committedBatches(10), keccak256(headers[10])); hevm.expectEmit(true, true, false, true); @@ -736,11 +929,15 @@ contract ScrollChainTest is DSTestPlus { hevm.expectEmit(true, true, false, true); emit RevertBatch(9, rollup.committedBatches(9)); rollup.revertBatch(headers[9], headers[10]); + (lastCommittedBatchIndex, , , , ) = rollup.enforcedBatchParameters(); + assertEq(lastCommittedBatchIndex, 8); assertEq(rollup.committedBatches(9), 0); assertEq(rollup.committedBatches(10), 0); // revert batches 6-8 rollup.revertBatch(headers[6], headers[8]); + (lastCommittedBatchIndex, , , , ) = rollup.enforcedBatchParameters(); + assertEq(lastCommittedBatchIndex, 5); // revert batches 4-5, with l1 messages assertEq(10, messageQueue.pendingQueueIndex());