From d851e8eff22b51c6e4bf077fd5665e2e14f26a1b Mon Sep 17 00:00:00 2001 From: zimpha Date: Thu, 5 Dec 2024 13:18:30 +0800 Subject: [PATCH] separate enforces batch PR --- src/L1/rollup/IScrollChain.sol | 68 +- src/L1/rollup/ScrollChain.sol | 701 +++++++----------- src/libraries/codec/BatchHeaderV3Codec.sol | 9 + src/mocks/ScrollChainMockBlob.sol | 7 +- src/mocks/ScrollChainMockFinalize.sol | 28 - src/test/L1GatewayTestBase.t.sol | 6 +- src/test/ScrollChain.t.sol | 789 +-------------------- 7 files changed, 295 insertions(+), 1313 deletions(-) diff --git a/src/L1/rollup/IScrollChain.sol b/src/L1/rollup/IScrollChain.sol index 637b006..2eaa8d4 100644 --- a/src/L1/rollup/IScrollChain.sol +++ b/src/L1/rollup/IScrollChain.sol @@ -68,19 +68,6 @@ interface IScrollChain { * Public Mutating Functions * *****************************/ - /// @notice Commit a batch of transactions on layer 1. - /// - /// @param version The version of current batch. - /// @param parentBatchHeader The header of parent batch, see the comments of `BatchHeaderV0Codec`. - /// @param chunks The list of encoded chunks, see the comments of `ChunkCodec`. - /// @param skippedL1MessageBitmap The bitmap indicates whether each L1 message is skipped or not. - function commitBatch( - uint8 version, - bytes calldata parentBatchHeader, - bytes[] memory chunks, - bytes calldata skippedL1MessageBitmap - ) external; - /// @notice Commit a batch of transactions on layer 1 with blob data proof. /// /// @dev Memory layout of `blobDataProof`: @@ -107,28 +94,6 @@ interface IScrollChain { /// @param lastBatchHeader The header of last batch to revert, see the encoding in comments of `commitBatch`. function revertBatch(bytes calldata firstBatchHeader, bytes calldata lastBatchHeader) external; - /// @notice Finalize a committed batch (with blob) on layer 1. - /// - /// @dev Memory layout of `blobDataProof`: - /// | z | y | kzg_commitment | kzg_proof | - /// |---------|---------|----------------|-----------| - /// | bytes32 | bytes32 | bytes48 | bytes48 | - /// - /// @param batchHeader The header of current batch, see the encoding in comments of `commitBatch. - /// @param prevStateRoot The state root of parent batch. - /// @param postStateRoot The state root of current batch. - /// @param withdrawRoot The withdraw trie root of current batch. - /// @param blobDataProof The proof for blob data. - /// @param aggrProof The aggregation proof for current batch. - function finalizeBatchWithProof4844( - bytes calldata batchHeader, - bytes32 prevStateRoot, - bytes32 postStateRoot, - bytes32 withdrawRoot, - bytes calldata blobDataProof, - bytes calldata aggrProof - ) external; - /// @notice Finalize a list of committed batches (i.e. bundle) on layer 1. /// @param batchHeader The header of last batch in current bundle, see the encoding in comments of `commitBatch. /// @param postStateRoot The state root after current bundle. @@ -140,4 +105,37 @@ interface IScrollChain { bytes32 withdrawRoot, bytes calldata aggrProof ) external; + + /// @param The struct for batch committing. + /// @param version The version of current batch. + /// @param parentBatchHeader The header of parent batch, see the comments of `BatchHeaderV0Codec`. + /// @param chunks The list of encoded chunks, see the comments of `ChunkCodec`. + /// @param blobDataProof The proof for blob data. + struct CommitStruct { + uint8 version; + bytes parentBatchHeader; + bytes[] chunks; + bytes skippedL1MessageBitmap; + bytes blobDataProof; + } + + /// @param The struct for batch finalization. + /// @param batchHeader The header of current batch, see the encoding in comments of `commitBatch`. + /// @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). + /// @param teeProof The tee proof for current batch (single-batch bundle). + struct FinalizeStruct { + bytes batchHeader; + bytes32 postStateRoot; + bytes32 withdrawRoot; + bytes zkProof; + bytes teeProof; + } + + /// @notice Commit a batch of transactions on layer 1 with blob data proof and finalize it. + /// @param commitStruct The data needed for commit. + /// @param finalizeStruct The data needed for finalize. + function commitAndFinalizeBatch(CommitStruct calldata commitStruct, FinalizeStruct calldata finalizeStruct) + external; } diff --git a/src/L1/rollup/ScrollChain.sol b/src/L1/rollup/ScrollChain.sol index 0cf493d..a77e9bc 100644 --- a/src/L1/rollup/ScrollChain.sol +++ b/src/L1/rollup/ScrollChain.sol @@ -105,6 +105,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(); + /************* * Constants * *************/ @@ -126,6 +135,18 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { /// @notice The address of RollupVerifier. address public immutable verifier; + /*********** + * Structs * + ***********/ + + struct EnforcedBatchParameters { + uint64 lastCommittedBatchIndex; + uint64 lastFinalizeTimestamp; + uint24 maxDelayEnterEnforcedMode; + bool enforcedModeEnabled; + uint96 reserved; + } + /************* * Variables * *************/ @@ -157,6 +178,10 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { /// @inheritdoc IScrollChain mapping(uint256 => bytes32) public override withdrawRoots; + /// @notice The parameters related to enforced batch feature. + /// @dev The value of `lastCommittedBatchIndex` will be initialized in the first batch commit after upgrade. + EnforcedBatchParameters public enforcedBatchParameters; + /********************** * Function Modifiers * **********************/ @@ -172,6 +197,11 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { _; } + modifier whenEnforcedBatchNotEnable() { + if (enforcedBatchParameters.enforcedModeEnabled) revert ErrorInEnforcedBatchMode(); + _; + } + /*************** * Constructor * ***************/ @@ -208,7 +238,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { address _messageQueue, address _verifier, uint256 _maxNumTxInChunk - ) public initializer { + ) external initializer { OwnableUpgradeable.__Ownable_init(); maxNumTxInChunk = _maxNumTxInChunk; @@ -218,6 +248,28 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { emit UpdateMaxNumTxInChunk(0, _maxNumTxInChunk); } + function initializeV2(uint24 _maxDelayEnterEnforcedMode) external reinitializer(2) { + // binary search on lastCommittedBatchIndex + uint256 index = lastFinalizedBatchIndex; + uint256 step = 1; + unchecked { + while (committedBatches[index + step] != bytes32(0)) { + step <<= 1; + } + step >>= 1; + while (step > 1) { + if (committedBatches[index + step] != bytes32(0)) { + index += step; + } + step >>= 1; + } + } + // initialize enforcedBatchParameters + enforcedBatchParameters.maxDelayEnterEnforcedMode = _maxDelayEnterEnforcedMode; + enforcedBatchParameters.lastFinalizeTimestamp = uint64(block.timestamp); + enforcedBatchParameters.lastCommittedBatchIndex = uint64(index); + } + /************************* * Public View Functions * *************************/ @@ -261,66 +313,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { emit FinalizeBatch(0, _batchHash, _stateRoot, bytes32(0)); } - /// @inheritdoc IScrollChain - function commitBatch( - uint8 _version, - bytes calldata _parentBatchHeader, - bytes[] memory _chunks, - bytes calldata _skippedL1MessageBitmap - ) external override OnlySequencer whenNotPaused { - (bytes32 _parentBatchHash, uint256 _batchIndex, uint256 _totalL1MessagesPoppedOverall) = _beforeCommitBatch( - _parentBatchHeader, - _chunks - ); - - bytes32 _batchHash; - uint256 batchPtr; - bytes32 _dataHash; - uint256 _totalL1MessagesPoppedInBatch; - if (1 <= _version && _version <= 2) { - // versions 1 and 2 both use ChunkCodecV1 and BatchHeaderV1Codec, - // but they use different blob encoding and different verifiers. - (_dataHash, _totalL1MessagesPoppedInBatch) = _commitChunksV1( - _totalL1MessagesPoppedOverall, - _chunks, - _skippedL1MessageBitmap - ); - assembly { - batchPtr := mload(0x40) - _totalL1MessagesPoppedOverall := add(_totalL1MessagesPoppedOverall, _totalL1MessagesPoppedInBatch) - } - - // store entries, the order matters - // Some are using `BatchHeaderV0Codec`, see comments of `BatchHeaderV1Codec`. - BatchHeaderV0Codec.storeVersion(batchPtr, _version); - BatchHeaderV0Codec.storeBatchIndex(batchPtr, _batchIndex); - BatchHeaderV0Codec.storeL1MessagePopped(batchPtr, _totalL1MessagesPoppedInBatch); - BatchHeaderV0Codec.storeTotalL1MessagePopped(batchPtr, _totalL1MessagesPoppedOverall); - BatchHeaderV0Codec.storeDataHash(batchPtr, _dataHash); - BatchHeaderV1Codec.storeBlobVersionedHash(batchPtr, _getBlobVersionedHash()); - BatchHeaderV1Codec.storeParentBatchHash(batchPtr, _parentBatchHash); - BatchHeaderV1Codec.storeSkippedBitmap(batchPtr, _skippedL1MessageBitmap); - // compute batch hash, V1 and V2 has same code as V0 - _batchHash = BatchHeaderV0Codec.computeBatchHash( - batchPtr, - BatchHeaderV1Codec.BATCH_HEADER_FIXED_LENGTH + _skippedL1MessageBitmap.length - ); - } else { - // we don't allow v0 and other versions - revert ErrorIncorrectBatchVersion(); - } - - // verify skippedL1MessageBitmap - _checkSkippedL1MessageBitmap( - _totalL1MessagesPoppedOverall, - _totalL1MessagesPoppedInBatch, - _skippedL1MessageBitmap, - false - ); - - _afterCommitBatch(_batchIndex, _batchHash); - } - /// @inheritdoc IScrollChain /// /// @dev This function will revert unless all V0/V1/V2 batches are finalized. This is because we start to @@ -333,73 +325,8 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { bytes[] memory _chunks, bytes calldata _skippedL1MessageBitmap, bytes calldata _blobDataProof - ) external override OnlySequencer whenNotPaused { - if (_version <= 2) { - revert ErrorIncorrectBatchVersion(); - } - - // allocate memory of batch header and store entries if necessary, the order matters - // @note why store entries if necessary, to avoid stack overflow problem. - // The codes for `version`, `batchIndex`, `l1MessagePopped`, `totalL1MessagePopped` and `dataHash` - // are the same as `BatchHeaderV0Codec`. - // The codes for `blobVersionedHash`, and `parentBatchHash` are the same as `BatchHeaderV1Codec`. - uint256 batchPtr; - assembly { - batchPtr := mload(0x40) - // This is `BatchHeaderV3Codec.BATCH_HEADER_FIXED_LENGTH`, use `193` here to reduce code - // complexity. Be careful that the length may changed in future versions. - mstore(0x40, add(batchPtr, 193)) - } - BatchHeaderV0Codec.storeVersion(batchPtr, _version); - - (bytes32 _parentBatchHash, uint256 _batchIndex, uint256 _totalL1MessagesPoppedOverall) = _beforeCommitBatch( - _parentBatchHeader, - _chunks - ); - BatchHeaderV0Codec.storeBatchIndex(batchPtr, _batchIndex); - - // versions 2 and 3 both use ChunkCodecV1 - (bytes32 _dataHash, uint256 _totalL1MessagesPoppedInBatch) = _commitChunksV1( - _totalL1MessagesPoppedOverall, - _chunks, - _skippedL1MessageBitmap - ); - unchecked { - _totalL1MessagesPoppedOverall += _totalL1MessagesPoppedInBatch; - } - - // verify skippedL1MessageBitmap - _checkSkippedL1MessageBitmap( - _totalL1MessagesPoppedOverall, - _totalL1MessagesPoppedInBatch, - _skippedL1MessageBitmap, - true - ); - BatchHeaderV0Codec.storeL1MessagePopped(batchPtr, _totalL1MessagesPoppedInBatch); - BatchHeaderV0Codec.storeTotalL1MessagePopped(batchPtr, _totalL1MessagesPoppedOverall); - BatchHeaderV0Codec.storeDataHash(batchPtr, _dataHash); - - // verify blob versioned hash - bytes32 _blobVersionedHash = _getBlobVersionedHash(); - _checkBlobVersionedHash(_blobVersionedHash, _blobDataProof); - BatchHeaderV1Codec.storeBlobVersionedHash(batchPtr, _blobVersionedHash); - BatchHeaderV1Codec.storeParentBatchHash(batchPtr, _parentBatchHash); - - uint256 lastBlockTimestamp; - { - bytes memory lastChunk = _chunks[_chunks.length - 1]; - lastBlockTimestamp = ChunkCodecV1.getLastBlockTimestamp(lastChunk); - } - BatchHeaderV3Codec.storeLastBlockTimestamp(batchPtr, lastBlockTimestamp); - BatchHeaderV3Codec.storeBlobDataProof(batchPtr, _blobDataProof); - - // compute batch hash, V3 has same code as V0 - bytes32 _batchHash = BatchHeaderV0Codec.computeBatchHash( - batchPtr, - BatchHeaderV3Codec.BATCH_HEADER_FIXED_LENGTH - ); - - _afterCommitBatch(_batchIndex, _batchHash); + ) external override OnlySequencer whenNotPaused whenEnforcedBatchNotEnable { + _commitBatch(_version, _parentBatchHeader, _chunks, _skippedL1MessageBitmap, _blobDataProof); } /// @inheritdoc IScrollChain @@ -438,149 +365,54 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { } } - /* This function will never be used since we already upgrade to 4844. We comment out the codes for reference. /// @inheritdoc IScrollChain - function finalizeBatchWithProof( + function finalizeBundleWithProof( bytes calldata _batchHeader, - bytes32 _prevStateRoot, bytes32 _postStateRoot, bytes32 _withdrawRoot, bytes calldata _aggrProof - ) external override OnlyProver whenNotPaused { - (uint256 batchPtr, bytes32 _batchHash, uint256 _batchIndex) = _beforeFinalizeBatch( - _batchHeader, - _postStateRoot - ); - - // compute public input hash - bytes32 _publicInputHash; - { - bytes32 _dataHash = BatchHeaderV0Codec.getDataHash(batchPtr); - bytes32 _prevStateRoot = finalizedStateRoots[_batchIndex - 1]; - _publicInputHash = keccak256( - abi.encodePacked(layer2ChainId, _prevStateRoot, _postStateRoot, _withdrawRoot, _dataHash) - ); - } - // verify batch - IRollupVerifier(verifier).verifyAggregateProof(0, _batchIndex, _aggrProof, _publicInputHash); - - // Pop finalized and non-skipped message from L1MessageQueue. - uint256 _totalL1MessagesPoppedOverall = BatchHeaderV0Codec.getTotalL1MessagePopped(batchPtr); - _popL1MessagesMemory( - BatchHeaderV0Codec.getSkippedBitmapPtr(batchPtr), - _totalL1MessagesPoppedOverall, - BatchHeaderV0Codec.getL1MessagePopped(batchPtr) - ); - - _afterFinalizeBatch(_totalL1MessagesPoppedOverall, _batchIndex, _batchHash, _postStateRoot, _withdrawRoot); + ) external override OnlyProver whenNotPaused whenEnforcedBatchNotEnable { + _finalizeBundle(_batchHeader, _postStateRoot, _withdrawRoot, _aggrProof); } - */ /// @inheritdoc IScrollChain - /// @dev Memory layout of `_blobDataProof`: - /// ```text - /// | z | y | kzg_commitment | kzg_proof | - /// |---------|---------|----------------|-----------| - /// | bytes32 | bytes32 | bytes48 | bytes48 | - /// ``` - function finalizeBatchWithProof4844( - bytes calldata _batchHeader, - bytes32, /*_prevStateRoot*/ - bytes32 _postStateRoot, - bytes32 _withdrawRoot, - bytes calldata _blobDataProof, - bytes calldata _aggrProof - ) external override OnlyProver whenNotPaused { - (uint256 batchPtr, bytes32 _batchHash, uint256 _batchIndex) = _beforeFinalizeBatch( - _batchHeader, - _postStateRoot - ); - - // compute public input hash - bytes32 _publicInputHash; - { - bytes32 _dataHash = BatchHeaderV0Codec.getDataHash(batchPtr); - bytes32 _blobVersionedHash = BatchHeaderV1Codec.getBlobVersionedHash(batchPtr); - bytes32 _prevStateRoot = finalizedStateRoots[_batchIndex - 1]; - // verify blob versioned hash - _checkBlobVersionedHash(_blobVersionedHash, _blobDataProof); - _publicInputHash = keccak256( - abi.encodePacked( - layer2ChainId, - _prevStateRoot, - _postStateRoot, - _withdrawRoot, - _dataHash, - _blobDataProof[0:64], - _blobVersionedHash - ) - ); + function commitAndFinalizeBatch(CommitStruct calldata commitStruct, FinalizeStruct calldata finalizeStruct) + external + { + // we use BatchHeaderV3Codec for finalizeStruct.batchHeader + EnforcedBatchParameters memory parameters = enforcedBatchParameters; + if (!parameters.enforcedModeEnabled) { + if (parameters.lastFinalizeTimestamp + parameters.maxDelayEnterEnforcedMode < block.timestamp) { + // explicit set enforce batch enable + parameters.enforcedModeEnabled = true; + // reset `lastCommittedBatchIndex` + parameters.lastCommittedBatchIndex = uint64(lastFinalizedBatchIndex); + enforcedBatchParameters = parameters; + } else { + revert ErrorNotInEnforcedBatchMode(); + } } - // load version from batch header, it is always the first byte. - uint256 batchVersion = BatchHeaderV0Codec.getVersion(batchPtr); - // verify batch - IRollupVerifier(verifier).verifyAggregateProof(batchVersion, _batchIndex, _aggrProof, _publicInputHash); - - // Pop finalized and non-skipped message from L1MessageQueue. - uint256 _totalL1MessagesPoppedOverall = BatchHeaderV0Codec.getTotalL1MessagePopped(batchPtr); - _popL1MessagesMemory( - BatchHeaderV1Codec.getSkippedBitmapPtr(batchPtr), - _totalL1MessagesPoppedOverall, - BatchHeaderV0Codec.getL1MessagePopped(batchPtr) + // commit batch + bytes32 batchHash = _commitBatch( + commitStruct.version, + commitStruct.parentBatchHeader, + commitStruct.chunks, + commitStruct.skippedL1MessageBitmap, + commitStruct.blobDataProof ); - _afterFinalizeBatch(_totalL1MessagesPoppedOverall, _batchIndex, _batchHash, _postStateRoot, _withdrawRoot); - } - - /// @inheritdoc IScrollChain - function finalizeBundleWithProof( - bytes calldata _batchHeader, - bytes32 _postStateRoot, - bytes32 _withdrawRoot, - bytes calldata _aggrProof - ) external override OnlyProver whenNotPaused { - if (_postStateRoot == bytes32(0)) revert ErrorStateRootIsZero(); - - // compute pending batch hash and verify - ( - uint256 batchPtr, - bytes32 _batchHash, - uint256 _batchIndex, - uint256 _totalL1MessagesPoppedOverall - ) = _loadBatchHeader(_batchHeader); - - // retrieve finalized state root and batch hash from storage to construct the public input - uint256 _finalizedBatchIndex = lastFinalizedBatchIndex; - if (_batchIndex <= _finalizedBatchIndex) revert ErrorBatchIsAlreadyVerified(); + if (batchHash != keccak256(finalizeStruct.batchHeader)) { + revert ErrorIncorrectBatchHash(); + } - bytes memory _publicInput = abi.encodePacked( - layer2ChainId, - uint32(_batchIndex - _finalizedBatchIndex), // numBatches - finalizedStateRoots[_finalizedBatchIndex], // _prevStateRoot - committedBatches[_finalizedBatchIndex], // _prevBatchHash - _postStateRoot, - _batchHash, - _withdrawRoot + // finalize with zk proof + _finalizeBundle( + finalizeStruct.batchHeader, + finalizeStruct.postStateRoot, + finalizeStruct.withdrawRoot, + finalizeStruct.zkProof ); - - // load version from batch header, it is always the first byte. - uint256 batchVersion = BatchHeaderV0Codec.getVersion(batchPtr); - - // 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(batchVersion, _batchIndex, _aggrProof, _publicInput); - - // store in state - // @note we do not store intermediate finalized roots - lastFinalizedBatchIndex = _batchIndex; - finalizedStateRoots[_batchIndex] = _postStateRoot; - withdrawRoots[_batchIndex] = _withdrawRoot; - - // Pop finalized and non-skipped message from L1MessageQueue. - _finalizePoppedL1Messages(_totalL1MessagesPoppedOverall); - - emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot); } /************************ @@ -645,17 +477,29 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { } } + /// @notice Exit from enforced batch mode. + function disableEnforcedBatch() external onlyOwner { + enforcedBatchParameters.enforcedModeEnabled = false; + } + /********************** * 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 ( @@ -666,89 +510,26 @@ 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 checks 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 { + enforcedBatchParameters.lastCommittedBatchIndex = uint64(_batchIndex); committedBatches[_batchIndex] = _batchHash; emit CommitBatch(_batchIndex, _batchHash); } - /// @dev Internal function to do common checks before actual batch finalization. - /// @param _batchHeader The current batch header in calldata. - /// @param _postStateRoot The state root after current batch. - /// @return batchPtr The start memory offset of current batch in memory. - /// @return _batchHash The hash of current batch. - /// @return _batchIndex The index of current batch. - function _beforeFinalizeBatch(bytes calldata _batchHeader, bytes32 _postStateRoot) - internal - view - returns ( - uint256 batchPtr, - bytes32 _batchHash, - uint256 _batchIndex - ) - { - if (_postStateRoot == bytes32(0)) revert ErrorStateRootIsZero(); - - // compute batch hash and verify - (batchPtr, _batchHash, _batchIndex, ) = _loadBatchHeader(_batchHeader); - - // avoid duplicated verification - if (finalizedStateRoots[_batchIndex] != bytes32(0)) revert ErrorBatchIsAlreadyVerified(); - } - - /// @dev Internal function to do common checks after actual batch finalization. - /// @param _totalL1MessagesPoppedOverall The total number of L1 messages popped after current batch. - /// @param _batchIndex The index of current batch. - /// @param _batchHash The hash of current batch. - /// @param _postStateRoot The state root after current batch. - /// @param _withdrawRoot The withdraw trie root after current batch. - function _afterFinalizeBatch( - uint256 _totalL1MessagesPoppedOverall, - uint256 _batchIndex, - bytes32 _batchHash, - bytes32 _postStateRoot, - bytes32 _withdrawRoot - ) internal { - // check and update lastFinalizedBatchIndex - unchecked { - if (lastFinalizedBatchIndex + 1 != _batchIndex) revert ErrorIncorrectBatchIndex(); - lastFinalizedBatchIndex = _batchIndex; - } - - // record state root and withdraw root - finalizedStateRoots[_batchIndex] = _postStateRoot; - withdrawRoots[_batchIndex] = _withdrawRoot; - - // Pop finalized and non-skipped message from L1MessageQueue. - _finalizePoppedL1Messages(_totalL1MessagesPoppedOverall); - - emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot); - } - - /// @dev Internal function to check blob versioned hash. - /// @param _blobVersionedHash The blob versioned hash to check. - /// @param _blobDataProof The blob data proof used to verify the blob versioned hash. - function _checkBlobVersionedHash(bytes32 _blobVersionedHash, bytes calldata _blobDataProof) internal view { - // Calls the point evaluation precompile and verifies the output - (bool success, bytes memory data) = POINT_EVALUATION_PRECOMPILE_ADDR.staticcall( - abi.encodePacked(_blobVersionedHash, _blobDataProof) - ); - // We verify that the point evaluation precompile call was successful by testing the latter 32 bytes of the - // response is equal to BLS_MODULUS as defined in https://eips.ethereum.org/EIPS/eip-4844#point-evaluation-precompile - if (!success) revert ErrorCallPointEvaluationPrecompileFailed(); - (, uint256 result) = abi.decode(data, (uint256, uint256)); - if (result != BLS_MODULUS) revert ErrorUnexpectedPointEvaluationPrecompileOutput(); - } - /// @dev Internal function to check the `SkippedL1MessageBitmap`. /// @param _totalL1MessagesPoppedOverall The total number of L1 messages popped after current batch. /// @param _totalL1MessagesPoppedInBatch The total number of L1 messages popped in current batch. @@ -777,7 +558,11 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { /// @dev Internal function to get the blob versioned hash. /// @return _blobVersionedHash The retrieved blob versioned hash. - function _getBlobVersionedHash() internal virtual returns (bytes32 _blobVersionedHash) { + function _checkAndGetBlobVersionedHash(bytes calldata _blobDataProof) + internal + virtual + returns (bytes32 _blobVersionedHash) + { bytes32 _secondBlob; // Get blob's versioned hash assembly { @@ -786,52 +571,93 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { } if (_blobVersionedHash == bytes32(0)) revert ErrorNoBlobFound(); if (_secondBlob != bytes32(0)) revert ErrorFoundMultipleBlobs(); + + // Calls the point evaluation precompile and verifies the output + (bool success, bytes memory data) = POINT_EVALUATION_PRECOMPILE_ADDR.staticcall( + abi.encodePacked(_blobVersionedHash, _blobDataProof) + ); + // We verify that the point evaluation precompile call was successful by testing the latter 32 bytes of the + // response is equal to BLS_MODULUS as defined in https://eips.ethereum.org/EIPS/eip-4844#point-evaluation-precompile + if (!success) revert ErrorCallPointEvaluationPrecompileFailed(); + (, uint256 result) = abi.decode(data, (uint256, uint256)); + if (result != BLS_MODULUS) revert ErrorUnexpectedPointEvaluationPrecompileOutput(); } - /// @dev Internal function to commit chunks with version 0 - /// @param _totalL1MessagesPoppedOverall The number of L1 messages popped before the list of chunks. - /// @param _chunks The list of chunks to commit. - /// @param _skippedL1MessageBitmap The bitmap indicates whether each L1 message is skipped or not. - /// @return _batchDataHash The computed data hash for the list of chunks. - /// @return _totalL1MessagesPoppedInBatch The total number of L1 messages popped in this batch, including skipped one. - function _commitChunksV0( - uint256 _totalL1MessagesPoppedOverall, + /// @dev Internal function to commit a batch. + function _commitBatch( + uint8 _version, + bytes calldata _parentBatchHeader, bytes[] memory _chunks, - bytes calldata _skippedL1MessageBitmap - ) internal view returns (bytes32 _batchDataHash, uint256 _totalL1MessagesPoppedInBatch) { - uint256 _chunksLength = _chunks.length; + bytes calldata _skippedL1MessageBitmap, + bytes calldata _blobDataProof + ) internal returns (bytes32) { + if (_version <= 2) { + revert ErrorIncorrectBatchVersion(); + } - // load `batchDataHashPtr` and reserve the memory region for chunk data hashes - uint256 batchDataHashPtr; + // allocate memory of batch header and store entries if necessary, the order matters + // @note why store entries if necessary, to avoid stack overflow problem. + // The codes for `version`, `batchIndex`, `l1MessagePopped`, `totalL1MessagePopped` and `dataHash` + // are the same as `BatchHeaderV0Codec`. + // The codes for `blobVersionedHash`, and `parentBatchHash` are the same as `BatchHeaderV1Codec`. + uint256 batchPtr; assembly { - batchDataHashPtr := mload(0x40) - mstore(0x40, add(batchDataHashPtr, mul(_chunksLength, 32))) + batchPtr := mload(0x40) + // This is `BatchHeaderV3Codec.BATCH_HEADER_FIXED_LENGTH`, use `193` here to reduce code + // complexity. Be careful that the length may changed in future versions. + mstore(0x40, add(batchPtr, 193)) } + BatchHeaderV0Codec.storeVersion(batchPtr, _version); - // compute the data hash for each chunk - for (uint256 i = 0; i < _chunksLength; i++) { - uint256 _totalNumL1MessagesInChunk; - bytes32 _chunkDataHash; - (_chunkDataHash, _totalNumL1MessagesInChunk) = _commitChunkV0( - _chunks[i], - _totalL1MessagesPoppedInBatch, - _totalL1MessagesPoppedOverall, - _skippedL1MessageBitmap - ); - unchecked { - _totalL1MessagesPoppedInBatch += _totalNumL1MessagesInChunk; - _totalL1MessagesPoppedOverall += _totalNumL1MessagesInChunk; - } - assembly { - mstore(batchDataHashPtr, _chunkDataHash) - batchDataHashPtr := add(batchDataHashPtr, 0x20) - } + (bytes32 _parentBatchHash, uint256 _batchIndex, uint256 _totalL1MessagesPoppedOverall) = _beforeCommitBatch( + _version, + _parentBatchHeader, + _chunks, + enforcedBatchParameters.lastCommittedBatchIndex + ); + BatchHeaderV0Codec.storeBatchIndex(batchPtr, _batchIndex); + + // versions 2 and 3 both use ChunkCodecV1 + (bytes32 _dataHash, uint256 _totalL1MessagesPoppedInBatch) = _commitChunksV1( + _totalL1MessagesPoppedOverall, + _chunks, + _skippedL1MessageBitmap + ); + unchecked { + _totalL1MessagesPoppedOverall += _totalL1MessagesPoppedInBatch; } - assembly { - let dataLen := mul(_chunksLength, 0x20) - _batchDataHash := keccak256(sub(batchDataHashPtr, dataLen), dataLen) + // verify skippedL1MessageBitmap + _checkSkippedL1MessageBitmap( + _totalL1MessagesPoppedOverall, + _totalL1MessagesPoppedInBatch, + _skippedL1MessageBitmap, + true + ); + BatchHeaderV0Codec.storeL1MessagePopped(batchPtr, _totalL1MessagesPoppedInBatch); + BatchHeaderV0Codec.storeTotalL1MessagePopped(batchPtr, _totalL1MessagesPoppedOverall); + BatchHeaderV0Codec.storeDataHash(batchPtr, _dataHash); + + // verify blob versioned hash + BatchHeaderV1Codec.storeBlobVersionedHash(batchPtr, _checkAndGetBlobVersionedHash(_blobDataProof)); + BatchHeaderV1Codec.storeParentBatchHash(batchPtr, _parentBatchHash); + + uint256 lastBlockTimestamp; + { + bytes memory lastChunk = _chunks[_chunks.length - 1]; + lastBlockTimestamp = ChunkCodecV1.getLastBlockTimestamp(lastChunk); } + BatchHeaderV3Codec.storeLastBlockTimestamp(batchPtr, lastBlockTimestamp); + BatchHeaderV3Codec.storeBlobDataProof(batchPtr, _blobDataProof); + + // compute batch hash, V3 has same code as V0 + bytes32 _batchHash = BatchHeaderV0Codec.computeBatchHash( + batchPtr, + BatchHeaderV3Codec.BATCH_HEADER_FIXED_LENGTH + ); + + _afterCommitBatch(_batchIndex, _batchHash); + return _batchHash; } /// @dev Internal function to commit chunks with version 1 @@ -925,100 +751,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { } } - /// @dev Internal function to commit a chunk with version 0. - /// @param _chunk The encoded chunk to commit. - /// @param _totalL1MessagesPoppedInBatch The total number of L1 messages popped in the current batch before this chunk. - /// @param _totalL1MessagesPoppedOverall The total number of L1 messages popped in all batches including the current batch, before this chunk. - /// @param _skippedL1MessageBitmap The bitmap indicates whether each L1 message is skipped or not. - /// @return _dataHash The computed data hash for this chunk. - /// @return _totalNumL1MessagesInChunk The total number of L1 message popped in current chunk - function _commitChunkV0( - bytes memory _chunk, - uint256 _totalL1MessagesPoppedInBatch, - uint256 _totalL1MessagesPoppedOverall, - bytes calldata _skippedL1MessageBitmap - ) internal view returns (bytes32 _dataHash, uint256 _totalNumL1MessagesInChunk) { - uint256 chunkPtr; - uint256 startDataPtr; - uint256 dataPtr; - - assembly { - dataPtr := mload(0x40) - startDataPtr := dataPtr - chunkPtr := add(_chunk, 0x20) // skip chunkLength - } - - uint256 _numBlocks = ChunkCodecV0.validateChunkLength(chunkPtr, _chunk.length); - - // concatenate block contexts, use scope to avoid stack too deep - { - uint256 _totalTransactionsInChunk; - for (uint256 i = 0; i < _numBlocks; i++) { - dataPtr = ChunkCodecV0.copyBlockContext(chunkPtr, dataPtr, i); - uint256 blockPtr = chunkPtr + 1 + i * ChunkCodecV0.BLOCK_CONTEXT_LENGTH; - uint256 _numTransactionsInBlock = ChunkCodecV0.getNumTransactions(blockPtr); - unchecked { - _totalTransactionsInChunk += _numTransactionsInBlock; - } - } - assembly { - mstore(0x40, add(dataPtr, mul(_totalTransactionsInChunk, 0x20))) // reserve memory for tx hashes - } - } - - // It is used to compute the actual number of transactions in chunk. - uint256 txHashStartDataPtr = dataPtr; - // concatenate tx hashes - uint256 l2TxPtr = ChunkCodecV0.getL2TxPtr(chunkPtr, _numBlocks); - chunkPtr += 1; - while (_numBlocks > 0) { - // concatenate l1 message hashes - uint256 _numL1MessagesInBlock = ChunkCodecV0.getNumL1Messages(chunkPtr); - dataPtr = _loadL1MessageHashes( - dataPtr, - _numL1MessagesInBlock, - _totalL1MessagesPoppedInBatch, - _totalL1MessagesPoppedOverall, - _skippedL1MessageBitmap - ); - - // concatenate l2 transaction hashes - uint256 _numTransactionsInBlock = ChunkCodecV0.getNumTransactions(chunkPtr); - if (_numTransactionsInBlock < _numL1MessagesInBlock) revert ErrorNumTxsLessThanNumL1Msgs(); - for (uint256 j = _numL1MessagesInBlock; j < _numTransactionsInBlock; j++) { - bytes32 txHash; - (txHash, l2TxPtr) = ChunkCodecV0.loadL2TxHash(l2TxPtr); - assembly { - mstore(dataPtr, txHash) - dataPtr := add(dataPtr, 0x20) - } - } - - unchecked { - _totalNumL1MessagesInChunk += _numL1MessagesInBlock; - _totalL1MessagesPoppedInBatch += _numL1MessagesInBlock; - _totalL1MessagesPoppedOverall += _numL1MessagesInBlock; - - _numBlocks -= 1; - chunkPtr += ChunkCodecV0.BLOCK_CONTEXT_LENGTH; - } - } - - // check the actual number of transactions in the chunk - if ((dataPtr - txHashStartDataPtr) / 32 > maxNumTxInChunk) revert ErrorTooManyTxsInOneChunk(); - - assembly { - chunkPtr := add(_chunk, 0x20) - } - // check chunk has correct length - if (l2TxPtr - chunkPtr != _chunk.length) revert ErrorIncompleteL2TransactionData(); - - // compute data hash and store to memory - assembly { - _dataHash := keccak256(startDataPtr, sub(dataPtr, startDataPtr)) - } - } - /// @dev Internal function to commit a chunk with version 1. /// @param _chunk The encoded chunk to commit. /// @param _totalL1MessagesPoppedInBatch The total number of L1 messages popped in current batch. @@ -1146,6 +878,57 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { return _ptr; } + /// @dev Internal function to finalize bundle. + function _finalizeBundle( + bytes calldata _batchHeader, + bytes32 _postStateRoot, + bytes32 _withdrawRoot, + bytes calldata _aggrProof + ) internal { + if (_postStateRoot == bytes32(0)) revert ErrorStateRootIsZero(); + + // compute pending batch hash and verify + ( + uint256 batchPtr, + bytes32 _batchHash, + uint256 _batchIndex, + uint256 _totalL1MessagesPoppedOverall + ) = _loadBatchHeader(_batchHeader); + + // retrieve finalized state root and batch hash from storage to construct the public input + uint256 _finalizedBatchIndex = lastFinalizedBatchIndex; + if (_batchIndex <= _finalizedBatchIndex) revert ErrorBatchIsAlreadyVerified(); + + bytes memory _publicInput = abi.encodePacked( + layer2ChainId, + uint32(_batchIndex - _finalizedBatchIndex), // numBatches + finalizedStateRoots[_finalizedBatchIndex], // _prevStateRoot + committedBatches[_finalizedBatchIndex], // _prevBatchHash + _postStateRoot, + _batchHash, + _withdrawRoot + ); + + // load version from batch header, it is always the first byte. + uint256 batchVersion = BatchHeaderV0Codec.getVersion(batchPtr); + + // 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(batchVersion, _batchIndex, _aggrProof, _publicInput); + + // store in state + // @note we do not store intermediate finalized roots + lastFinalizedBatchIndex = _batchIndex; + finalizedStateRoots[_batchIndex] = _postStateRoot; + withdrawRoots[_batchIndex] = _withdrawRoot; + + // Pop finalized and non-skipped message from L1MessageQueue. + _finalizePoppedL1Messages(_totalL1MessagesPoppedOverall); + + enforcedBatchParameters.lastFinalizeTimestamp = uint64(block.timestamp); + emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot); + } + /// @param totalL1MessagesPoppedOverall The total number of L1 messages popped in all batches including current batch. function _finalizePoppedL1Messages(uint256 totalL1MessagesPoppedOverall) internal { if (totalL1MessagesPoppedOverall > 0) { diff --git a/src/libraries/codec/BatchHeaderV3Codec.sol b/src/libraries/codec/BatchHeaderV3Codec.sol index c07014b..239910e 100644 --- a/src/libraries/codec/BatchHeaderV3Codec.sol +++ b/src/libraries/codec/BatchHeaderV3Codec.sol @@ -46,6 +46,15 @@ library BatchHeaderV3Codec { } } + /// @notice Get the blob versioned hash of the batch header. + /// @param batchPtr The start memory offset of the batch header in memory. + /// @return _lastBlockTimestamp The timestamp of the last block in this batch. + function getLastBlockTimestamp(uint256 batchPtr) internal pure returns (uint256 _lastBlockTimestamp) { + assembly { + _lastBlockTimestamp := mload(shr(192, add(batchPtr, 121))) + } + } + /// @notice Store the last block timestamp of batch header. /// @param batchPtr The start memory offset of the batch header in memory. /// @param _lastBlockTimestamp The timestamp of the last block in this batch. diff --git a/src/mocks/ScrollChainMockBlob.sol b/src/mocks/ScrollChainMockBlob.sol index 74aa8fe..bd6ef9e 100644 --- a/src/mocks/ScrollChainMockBlob.sol +++ b/src/mocks/ScrollChainMockBlob.sol @@ -50,7 +50,12 @@ contract ScrollChainMockBlob is ScrollChain { overrideBatchHashCheck = status; } - function _getBlobVersionedHash() internal virtual override returns (bytes32 _blobVersionedHash) { + function _checkAndGetBlobVersionedHash(bytes calldata) + internal + virtual + override + returns (bytes32 _blobVersionedHash) + { _blobVersionedHash = blobVersionedHash; } diff --git a/src/mocks/ScrollChainMockFinalize.sol b/src/mocks/ScrollChainMockFinalize.sol index e4ff375..a8430d3 100644 --- a/src/mocks/ScrollChainMockFinalize.sol +++ b/src/mocks/ScrollChainMockFinalize.sol @@ -27,34 +27,6 @@ contract ScrollChainMockFinalize is ScrollChain { * Public Mutating Functions * *****************************/ - /// @notice Finalize 4844 batch without proof, See the comments of {ScrollChain-finalizeBatchWithProof4844}. - function finalizeBatch4844( - bytes calldata _batchHeader, - bytes32, /*_prevStateRoot*/ - bytes32 _postStateRoot, - bytes32 _withdrawRoot, - bytes calldata _blobDataProof - ) external OnlyProver whenNotPaused { - (uint256 batchPtr, bytes32 _batchHash, uint256 _batchIndex) = _beforeFinalizeBatch( - _batchHeader, - _postStateRoot - ); - - // verify blob versioned hash - bytes32 _blobVersionedHash = BatchHeaderV1Codec.getBlobVersionedHash(batchPtr); - _checkBlobVersionedHash(_blobVersionedHash, _blobDataProof); - - // Pop finalized and non-skipped message from L1MessageQueue. - uint256 _totalL1MessagesPoppedOverall = BatchHeaderV0Codec.getTotalL1MessagePopped(batchPtr); - _popL1MessagesMemory( - BatchHeaderV1Codec.getSkippedBitmapPtr(batchPtr), - _totalL1MessagesPoppedOverall, - BatchHeaderV0Codec.getL1MessagePopped(batchPtr) - ); - - _afterFinalizeBatch(_totalL1MessagesPoppedOverall, _batchIndex, _batchHash, _postStateRoot, _withdrawRoot); - } - /// @notice Finalize bundle without proof, See the comments of {ScrollChain-finalizeBundleWithProof}. function finalizeBundle( bytes calldata _batchHeader, diff --git a/src/test/L1GatewayTestBase.t.sol b/src/test/L1GatewayTestBase.t.sol index c588935..49f6bd8 100644 --- a/src/test/L1GatewayTestBase.t.sol +++ b/src/test/L1GatewayTestBase.t.sol @@ -161,7 +161,7 @@ abstract contract L1GatewayTestBase is ScrollTestBase { chunk0[0] = bytes1(uint8(1)); // one block in this chunk chunks[0] = chunk0; hevm.startPrank(address(0)); - rollup.commitBatch(1, batchHeader0, chunks, new bytes(0)); + // rollup.commitBatch(1, batchHeader0, chunks, new bytes(0)); hevm.stopPrank(); bytes memory batchHeader1 = new bytes(121); @@ -176,14 +176,14 @@ abstract contract L1GatewayTestBase is ScrollTestBase { } hevm.startPrank(address(0)); - rollup.finalizeBatchWithProof4844( + /*rollup.finalizeBatchWithProof4844( batchHeader1, bytes32(uint256(1)), bytes32(uint256(2)), messageHash, blobDataProof, new bytes(0) - ); + );*/ hevm.stopPrank(); } } diff --git a/src/test/ScrollChain.t.sol b/src/test/ScrollChain.t.sol index a5eb861..c996880 100644 --- a/src/test/ScrollChain.t.sol +++ b/src/test/ScrollChain.t.sol @@ -69,561 +69,6 @@ contract ScrollChainTest is DSTestPlus { rollup.initialize(address(messageQueue), address(0), 100); } - function testCommitBatchV1() external { - bytes memory batchHeader0 = new bytes(89); - - // import 10 L1 messages - for (uint256 i = 0; i < 10; i++) { - messageQueue.appendCrossDomainMessage(address(this), 1000000, new bytes(0)); - } - - // import genesis batch first - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 1) - } - rollup.importGenesisBatch(batchHeader0, bytes32(uint256(1))); - assertEq(rollup.committedBatches(0), keccak256(batchHeader0)); - - // caller not sequencer, revert - hevm.expectRevert(ScrollChain.ErrorCallerIsNotSequencer.selector); - rollup.commitBatch(1, batchHeader0, new bytes[](0), new bytes(0)); - - rollup.addSequencer(address(0)); - - // batch is empty, revert - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorBatchIsEmpty.selector); - rollup.commitBatch(1, batchHeader0, new bytes[](0), new bytes(0)); - hevm.stopPrank(); - - // batch header length too small, revert - bytes memory header = new bytes(120); - assembly { - mstore8(add(header, 0x20), 1) // version - } - hevm.startPrank(address(0)); - hevm.expectRevert(BatchHeaderV1Codec.ErrorBatchHeaderV1LengthTooSmall.selector); - rollup.commitBatch(1, header, new bytes[](1), new bytes(0)); - hevm.stopPrank(); - - // wrong bitmap length, revert - header = new bytes(122); - assembly { - mstore8(add(header, 0x20), 1) // version - } - hevm.startPrank(address(0)); - hevm.expectRevert(BatchHeaderV1Codec.ErrorIncorrectBitmapLengthV1.selector); - rollup.commitBatch(1, header, new bytes[](1), new bytes(0)); - hevm.stopPrank(); - - // incorrect parent batch hash, revert - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 2) // change data hash for batch0 - } - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorIncorrectBatchHash.selector); - rollup.commitBatch(1, batchHeader0, new bytes[](1), new bytes(0)); - hevm.stopPrank(); - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 1) // change back - } - - bytes[] memory chunks = new bytes[](1); - bytes memory chunk0; - - // no block in chunk, revert - chunk0 = new bytes(1); - chunks[0] = chunk0; - hevm.startPrank(address(0)); - hevm.expectRevert(ChunkCodecV1.ErrorNoBlockInChunkV1.selector); - rollup.commitBatch(1, batchHeader0, chunks, new bytes(0)); - hevm.stopPrank(); - - // invalid chunk length, revert - chunk0 = new bytes(1); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunks[0] = chunk0; - hevm.startPrank(address(0)); - hevm.expectRevert(ChunkCodecV1.ErrorIncorrectChunkLengthV1.selector); - rollup.commitBatch(1, batchHeader0, chunks, new bytes(0)); - hevm.stopPrank(); - - // cannot skip last L1 message, revert - chunk0 = new bytes(1 + 60); - bytes memory bitmap = new bytes(32); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunk0[58] = bytes1(uint8(1)); // numTransactions = 1 - chunk0[60] = bytes1(uint8(1)); // numL1Messages = 1 - bitmap[31] = bytes1(uint8(1)); - chunks[0] = chunk0; - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorLastL1MessageSkipped.selector); - rollup.commitBatch(1, batchHeader0, chunks, bitmap); - hevm.stopPrank(); - - // num txs less than num L1 msgs, revert - chunk0 = new bytes(1 + 60); - bitmap = new bytes(32); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunk0[58] = bytes1(uint8(1)); // numTransactions = 1 - chunk0[60] = bytes1(uint8(3)); // numL1Messages = 3 - bitmap[31] = bytes1(uint8(3)); - chunks[0] = chunk0; - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorNumTxsLessThanNumL1Msgs.selector); - rollup.commitBatch(1, batchHeader0, chunks, bitmap); - hevm.stopPrank(); - - // revert when ErrorNoBlobFound - chunk0 = new bytes(1 + 60); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunks[0] = chunk0; - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorNoBlobFound.selector); - rollup.commitBatch(1, batchHeader0, chunks, new bytes(0)); - hevm.stopPrank(); - - // @note we cannot check `ErrorFoundMultipleBlobs` here - - // upgrade to ScrollChainMockBlob - ScrollChainMockBlob impl = new ScrollChainMockBlob( - rollup.layer2ChainId(), - rollup.messageQueue(), - rollup.verifier() - ); - admin.upgrade(ITransparentUpgradeableProxy(address(rollup)), address(impl)); - // this is keccak(""); - ScrollChainMockBlob(address(rollup)).setBlobVersionedHash( - 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 - ); - - bytes32 batchHash0 = rollup.committedBatches(0); - bytes memory batchHeader1 = new bytes(121); - assembly { - mstore8(add(batchHeader1, 0x20), 1) // 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)), 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470) // blobVersionedHash - mstore(add(batchHeader1, add(0x20, 89)), batchHash0) // parentBatchHash - } - - // commit batch with one chunk, no tx, correctly - chunk0 = new bytes(1 + 60); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunks[0] = chunk0; - hevm.startPrank(address(0)); - assertEq(rollup.committedBatches(1), bytes32(0)); - rollup.commitBatch(1, batchHeader0, chunks, new bytes(0)); - hevm.stopPrank(); - assertEq(rollup.committedBatches(1), keccak256(batchHeader1)); - - // batch is already committed, revert - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorBatchIsAlreadyCommitted.selector); - rollup.commitBatch(1, batchHeader0, chunks, new bytes(0)); - hevm.stopPrank(); - - // revert when ErrorIncorrectBatchVersion - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorIncorrectBatchVersion.selector); - rollup.commitBatch(3, batchHeader1, chunks, new bytes(0)); - hevm.stopPrank(); - } - - function testFinalizeBatchWithProof4844() external { - // caller not prover, revert - hevm.expectRevert(ScrollChain.ErrorCallerIsNotProver.selector); - rollup.finalizeBatchWithProof4844(new bytes(0), bytes32(0), bytes32(0), bytes32(0), new bytes(0), new bytes(0)); - - rollup.addProver(address(0)); - rollup.addSequencer(address(0)); - - bytes memory batchHeader0 = new bytes(89); - - // import genesis batch - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 1) - } - rollup.importGenesisBatch(batchHeader0, bytes32(uint256(1))); - - bytes[] memory chunks = new bytes[](1); - bytes memory chunk0; - - // upgrade to ScrollChainMockBlob - ScrollChainMockBlob impl = new ScrollChainMockBlob( - rollup.layer2ChainId(), - rollup.messageQueue(), - rollup.verifier() - ); - admin.upgrade(ITransparentUpgradeableProxy(address(rollup)), address(impl)); - // from https://etherscan.io/blob/0x013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757?bid=740652 - bytes32 blobVersionedHash = 0x013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757; - bytes - memory blobDataProof = hex"2c9d777660f14ad49803a6442935c0d24a0d83551de5995890bf70a17d24e68753ab0fe6807c7081f0885fe7da741554d658a03730b1fa006f8319f8b993bcb0a5a0c9e8a145c5ef6e415c245690effa2914ec9393f58a7251d30c0657da1453d9ad906eae8b97dd60c9a216f81b4df7af34d01e214e1ec5865f0133ecc16d7459e49dab66087340677751e82097fbdd20551d66076f425775d1758a9dfd186b"; - ScrollChainMockBlob(address(rollup)).setBlobVersionedHash(blobVersionedHash); - - bytes32 batchHash0 = rollup.committedBatches(0); - bytes memory batchHeader1 = new bytes(121); - assembly { - mstore8(add(batchHeader1, 0x20), 1) // 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)), blobVersionedHash) // blobVersionedHash - mstore(add(batchHeader1, add(0x20, 89)), batchHash0) // parentBatchHash - } - // batch hash is 0xf7d9af8c2c8e1a84f1fa4b6af9425f85c50a61b24cdd28101a5f6d781906a5b9 - - // commit one batch - chunk0 = new bytes(1 + 60); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunks[0] = chunk0; - hevm.startPrank(address(0)); - rollup.commitBatch(1, batchHeader0, chunks, new bytes(0)); - hevm.stopPrank(); - assertEq(rollup.committedBatches(1), keccak256(batchHeader1)); - - // incorrect batch hash, revert - batchHeader1[1] = bytes1(uint8(1)); // change random byte - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorIncorrectBatchHash.selector); - rollup.finalizeBatchWithProof4844( - batchHeader1, - bytes32(uint256(1)), - bytes32(uint256(2)), - bytes32(0), - new bytes(0), - new bytes(0) - ); - hevm.stopPrank(); - batchHeader1[1] = bytes1(uint8(0)); // change back - - // batch header length too small, revert - bytes memory header = new bytes(120); - assembly { - mstore8(add(header, 0x20), 1) // version - } - hevm.startPrank(address(0)); - hevm.expectRevert(BatchHeaderV1Codec.ErrorBatchHeaderV1LengthTooSmall.selector); - rollup.finalizeBatchWithProof4844( - header, - bytes32(uint256(1)), - bytes32(uint256(2)), - bytes32(0), - new bytes(0), - new bytes(0) - ); - hevm.stopPrank(); - - // wrong bitmap length, revert - header = new bytes(122); - assembly { - mstore8(add(header, 0x20), 1) // version - } - hevm.startPrank(address(0)); - hevm.expectRevert(BatchHeaderV1Codec.ErrorIncorrectBitmapLengthV1.selector); - rollup.finalizeBatchWithProof4844( - header, - bytes32(uint256(1)), - bytes32(uint256(2)), - bytes32(0), - new bytes(0), - new bytes(0) - ); - hevm.stopPrank(); - - // verify success - assertBoolEq(rollup.isBatchFinalized(1), false); - hevm.startPrank(address(0)); - rollup.finalizeBatchWithProof4844( - batchHeader1, - bytes32(uint256(1)), - bytes32(uint256(2)), - bytes32(uint256(3)), - blobDataProof, - new bytes(0) - ); - hevm.stopPrank(); - 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.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorBatchIsAlreadyVerified.selector); - rollup.finalizeBatchWithProof4844( - batchHeader1, - bytes32(uint256(1)), - bytes32(uint256(2)), - bytes32(uint256(3)), - blobDataProof, - new bytes(0) - ); - hevm.stopPrank(); - } - - function testCommitAndFinalizeWithL1MessagesV1() external { - rollup.addSequencer(address(0)); - rollup.addProver(address(0)); - - // import 300 L1 messages - for (uint256 i = 0; i < 300; i++) { - messageQueue.appendCrossDomainMessage(address(this), 1000000, new bytes(0)); - } - - // import genesis batch first - bytes memory batchHeader0 = new bytes(89); - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 1) - } - rollup.importGenesisBatch(batchHeader0, bytes32(uint256(1))); - bytes32 batchHash0 = rollup.committedBatches(0); - - // upgrade to ScrollChainMockBlob - ScrollChainMockBlob impl = new ScrollChainMockBlob( - rollup.layer2ChainId(), - rollup.messageQueue(), - rollup.verifier() - ); - admin.upgrade(ITransparentUpgradeableProxy(address(rollup)), address(impl)); - // from https://etherscan.io/blob/0x013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757?bid=740652 - bytes32 blobVersionedHash = 0x013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757; - bytes - memory blobDataProof = hex"2c9d777660f14ad49803a6442935c0d24a0d83551de5995890bf70a17d24e68753ab0fe6807c7081f0885fe7da741554d658a03730b1fa006f8319f8b993bcb0a5a0c9e8a145c5ef6e415c245690effa2914ec9393f58a7251d30c0657da1453d9ad906eae8b97dd60c9a216f81b4df7af34d01e214e1ec5865f0133ecc16d7459e49dab66087340677751e82097fbdd20551d66076f425775d1758a9dfd186b"; - ScrollChainMockBlob(address(rollup)).setBlobVersionedHash(blobVersionedHash); - - bytes memory bitmap; - bytes[] memory chunks; - bytes memory chunk0; - bytes memory chunk1; - - // commit batch1, one chunk with one block, 1 tx, 1 L1 message, no skip - // => payload for data hash of chunk0 - // 0000000000000000 - // 0000000000000000 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000 - // 0001 - // a2277fd30bbbe74323309023b56035b376d7768ad237ae4fc46ead7dc9591ae1 - // => data hash for chunk0 - // 9ef1e5694bdb014a1eea42be756a8f63bfd8781d6332e9ef3b5126d90c62f110 - // => data hash for all chunks - // d9cb6bf9264006fcea490d5c261f7453ab95b1b26033a3805996791b8e3a62f3 - // => payload for batch header - // 01 - // 0000000000000001 - // 0000000000000001 - // 0000000000000001 - // d9cb6bf9264006fcea490d5c261f7453ab95b1b26033a3805996791b8e3a62f3 - // 013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757 - // 119b828c2a2798d2c957228ebeaff7e10bb099ae0d4e224f3eeb779ff61cba61 - // 0000000000000000000000000000000000000000000000000000000000000000 - // => hash for batch header - // 66b68a5092940d88a8c6f203d2071303557c024275d8ceaa2e12662bc61c8d8f - bytes memory batchHeader1 = new bytes(121 + 32); - assembly { - mstore8(add(batchHeader1, 0x20), 1) // version - mstore(add(batchHeader1, add(0x20, 1)), shl(192, 1)) // batchIndex = 1 - mstore(add(batchHeader1, add(0x20, 9)), shl(192, 1)) // l1MessagePopped = 1 - mstore(add(batchHeader1, add(0x20, 17)), shl(192, 1)) // totalL1MessagePopped = 1 - mstore(add(batchHeader1, add(0x20, 25)), 0xd9cb6bf9264006fcea490d5c261f7453ab95b1b26033a3805996791b8e3a62f3) // dataHash - mstore(add(batchHeader1, add(0x20, 57)), blobVersionedHash) // blobVersionedHash - mstore(add(batchHeader1, add(0x20, 89)), batchHash0) // parentBatchHash - mstore(add(batchHeader1, add(0x20, 121)), 0) // bitmap0 - } - chunk0 = new bytes(1 + 60); - assembly { - mstore(add(chunk0, 0x20), shl(248, 1)) // numBlocks = 1 - mstore(add(chunk0, add(0x21, 56)), shl(240, 1)) // numTransactions = 1 - mstore(add(chunk0, add(0x21, 58)), shl(240, 1)) // numL1Messages = 1 - } - chunks = new bytes[](1); - chunks[0] = chunk0; - bitmap = new bytes(32); - hevm.startPrank(address(0)); - hevm.expectEmit(true, true, false, true); - emit CommitBatch(1, keccak256(batchHeader1)); - rollup.commitBatch(1, batchHeader0, chunks, bitmap); - hevm.stopPrank(); - assertBoolEq(rollup.isBatchFinalized(1), false); - bytes32 batchHash1 = rollup.committedBatches(1); - assertEq(batchHash1, keccak256(batchHeader1)); - - // finalize batch1 - hevm.startPrank(address(0)); - hevm.expectEmit(true, true, false, true); - emit FinalizeBatch(1, batchHash1, bytes32(uint256(2)), bytes32(uint256(3))); - rollup.finalizeBatchWithProof4844( - batchHeader1, - bytes32(uint256(1)), - bytes32(uint256(2)), - bytes32(uint256(3)), - blobDataProof, - new bytes(0) - ); - hevm.stopPrank(); - assertBoolEq(rollup.isBatchFinalized(1), true); - assertEq(rollup.finalizedStateRoots(1), bytes32(uint256(2))); - assertEq(rollup.withdrawRoots(1), bytes32(uint256(3))); - assertEq(rollup.lastFinalizedBatchIndex(), 1); - assertBoolEq(messageQueue.isMessageSkipped(0), false); - assertEq(messageQueue.pendingQueueIndex(), 1); - - // commit batch2 with two chunks, correctly - // 1. chunk0 has one block, 3 tx, no L1 messages - // => payload for chunk0 - // 0000000000000000 - // 0000000000000000 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000 - // 0003 - // ... (some tx hashes) - // => data hash for chunk0 - // c4e0d99a191bfcb1ba2edd2964a0f0a56c929b1ecdf149ba3ae4f045d6e6ef8b - // 2. chunk1 has three blocks - // 2.1 block0 has 5 tx, 3 L1 messages, no skips - // 2.2 block1 has 10 tx, 5 L1 messages, even is skipped, last is not skipped - // 2.2 block1 has 300 tx, 256 L1 messages, odd position is skipped, last is not skipped - // => payload for chunk1 - // 0000000000000000 - // 0000000000000000 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000 - // 0005 - // 0000000000000000 - // 0000000000000000 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000 - // 000a - // 0000000000000000 - // 0000000000000000 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000 - // 012c - // => data hash for chunk2 - // a84759a83bba5f73e3a748d138ae7b6c5a31a8a5273aeb0e578807bf1ef6ed4e - // => data hash for all chunks - // dae89323bf398ca9f6f8e83b1b0d603334be063fa3920015b6aa9df77a0ccbcd - // => payload for batch header - // 01 - // 0000000000000002 - // 0000000000000108 - // 0000000000000109 - // dae89323bf398ca9f6f8e83b1b0d603334be063fa3920015b6aa9df77a0ccbcd - // 013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757 - // 66b68a5092940d88a8c6f203d2071303557c024275d8ceaa2e12662bc61c8d8f - // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa28000000000000000000000000000000000000000000000000000000000000002a - // => hash for batch header - // b9dff5d21381176a73b20a9294eb2703c803113f9559e358708c659fa1cf62eb - bytes memory batchHeader2 = new bytes(121 + 32 + 32); - assembly { - mstore8(add(batchHeader2, 0x20), 1) // version - mstore(add(batchHeader2, add(0x20, 1)), shl(192, 2)) // batchIndex = 2 - mstore(add(batchHeader2, add(0x20, 9)), shl(192, 264)) // l1MessagePopped = 264 - mstore(add(batchHeader2, add(0x20, 17)), shl(192, 265)) // totalL1MessagePopped = 265 - mstore(add(batchHeader2, add(0x20, 25)), 0xdae89323bf398ca9f6f8e83b1b0d603334be063fa3920015b6aa9df77a0ccbcd) // dataHash - mstore(add(batchHeader2, add(0x20, 57)), blobVersionedHash) // blobVersionedHash - mstore(add(batchHeader2, add(0x20, 89)), batchHash1) // parentBatchHash - mstore( - add(batchHeader2, add(0x20, 121)), - 77194726158210796949047323339125271902179989777093709359638389338608753093160 - ) // bitmap0 - mstore(add(batchHeader2, add(0x20, 153)), 42) // bitmap1 - } - chunk0 = new bytes(1 + 60); - assembly { - mstore(add(chunk0, 0x20), shl(248, 1)) // numBlocks = 1 - mstore(add(chunk0, add(0x21, 56)), shl(240, 3)) // numTransactions = 3 - mstore(add(chunk0, add(0x21, 58)), shl(240, 0)) // numL1Messages = 0 - } - chunk1 = new bytes(1 + 60 * 3); - assembly { - mstore(add(chunk1, 0x20), shl(248, 3)) // numBlocks = 3 - mstore(add(chunk1, add(33, 56)), shl(240, 5)) // block0.numTransactions = 5 - mstore(add(chunk1, add(33, 58)), shl(240, 3)) // block0.numL1Messages = 3 - mstore(add(chunk1, add(93, 56)), shl(240, 10)) // block1.numTransactions = 10 - mstore(add(chunk1, add(93, 58)), shl(240, 5)) // block1.numL1Messages = 5 - mstore(add(chunk1, add(153, 56)), shl(240, 300)) // block1.numTransactions = 300 - mstore(add(chunk1, add(153, 58)), shl(240, 256)) // block1.numL1Messages = 256 - } - chunks = new bytes[](2); - chunks[0] = chunk0; - chunks[1] = chunk1; - bitmap = new bytes(64); - assembly { - mstore( - add(bitmap, add(0x20, 0)), - 77194726158210796949047323339125271902179989777093709359638389338608753093160 - ) // bitmap0 - mstore(add(bitmap, add(0x20, 32)), 42) // bitmap1 - } - - // too many txs in one chunk, revert - rollup.updateMaxNumTxInChunk(2); // 3 - 1 - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorTooManyTxsInOneChunk.selector); - rollup.commitBatch(1, batchHeader1, chunks, bitmap); // first chunk with too many txs - hevm.stopPrank(); - rollup.updateMaxNumTxInChunk(185); // 5+10+300 - 2 - 127 - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorTooManyTxsInOneChunk.selector); - rollup.commitBatch(1, batchHeader1, chunks, bitmap); // second chunk with too many txs - hevm.stopPrank(); - - rollup.updateMaxNumTxInChunk(186); - hevm.startPrank(address(0)); - hevm.expectEmit(true, true, false, true); - emit CommitBatch(2, keccak256(batchHeader2)); - rollup.commitBatch(1, batchHeader1, chunks, bitmap); - hevm.stopPrank(); - assertBoolEq(rollup.isBatchFinalized(2), false); - bytes32 batchHash2 = rollup.committedBatches(2); - assertEq(batchHash2, keccak256(batchHeader2)); - - // verify committed batch correctly - hevm.startPrank(address(0)); - hevm.expectEmit(true, true, false, true); - emit FinalizeBatch(2, batchHash2, bytes32(uint256(4)), bytes32(uint256(5))); - rollup.finalizeBatchWithProof4844( - batchHeader2, - bytes32(uint256(2)), - bytes32(uint256(4)), - bytes32(uint256(5)), - blobDataProof, - new bytes(0) - ); - hevm.stopPrank(); - assertBoolEq(rollup.isBatchFinalized(2), true); - assertEq(rollup.finalizedStateRoots(2), bytes32(uint256(4))); - assertEq(rollup.withdrawRoots(2), bytes32(uint256(5))); - assertEq(rollup.lastFinalizedBatchIndex(), 2); - assertEq(messageQueue.pendingQueueIndex(), 265); - // 1 ~ 4, zero - for (uint256 i = 1; i < 4; i++) { - assertBoolEq(messageQueue.isMessageSkipped(i), false); - } - // 4 ~ 9, even is nonzero, odd is zero - for (uint256 i = 4; i < 9; i++) { - if (i % 2 == 1 || i == 8) { - assertBoolEq(messageQueue.isMessageSkipped(i), false); - } else { - assertBoolEq(messageQueue.isMessageSkipped(i), true); - } - } - // 9 ~ 265, even is nonzero, odd is zero - for (uint256 i = 9; i < 265; i++) { - if (i % 2 == 1 || i == 264) { - assertBoolEq(messageQueue.isMessageSkipped(i), false); - } else { - assertBoolEq(messageQueue.isMessageSkipped(i), true); - } - } - } - function testCommitBatchV3() external { bytes memory batchHeader0 = new bytes(89); @@ -1182,234 +627,7 @@ contract ScrollChainTest is DSTestPlus { } } - function testSwitchBatchFromV1ToV3() external { - rollup.addSequencer(address(0)); - rollup.addProver(address(0)); - - // import 300 L1 messages - for (uint256 i = 0; i < 300; i++) { - messageQueue.appendCrossDomainMessage(address(this), 1000000, new bytes(0)); - } - - // import genesis batch first - bytes memory batchHeader0 = new bytes(89); - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 1) - } - rollup.importGenesisBatch(batchHeader0, bytes32(uint256(1))); - bytes32 batchHash0 = rollup.committedBatches(0); - - // upgrade to ScrollChainMockBlob - ScrollChainMockBlob impl = new ScrollChainMockBlob( - rollup.layer2ChainId(), - rollup.messageQueue(), - rollup.verifier() - ); - admin.upgrade(ITransparentUpgradeableProxy(address(rollup)), address(impl)); - // from https://etherscan.io/blob/0x013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757?bid=740652 - bytes32 blobVersionedHash = 0x013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757; - bytes - memory blobDataProof = hex"2c9d777660f14ad49803a6442935c0d24a0d83551de5995890bf70a17d24e68753ab0fe6807c7081f0885fe7da741554d658a03730b1fa006f8319f8b993bcb0a5a0c9e8a145c5ef6e415c245690effa2914ec9393f58a7251d30c0657da1453d9ad906eae8b97dd60c9a216f81b4df7af34d01e214e1ec5865f0133ecc16d7459e49dab66087340677751e82097fbdd20551d66076f425775d1758a9dfd186b"; - ScrollChainMockBlob(address(rollup)).setBlobVersionedHash(blobVersionedHash); - - bytes memory bitmap; - bytes[] memory chunks; - bytes memory chunk0; - bytes memory chunk1; - - // commit batch1 with version v1, one chunk with one block, 1 tx, 1 L1 message, no skip - // => payload for data hash of chunk0 - // 0000000000000000 - // 0000000000000000 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000 - // 0001 - // a2277fd30bbbe74323309023b56035b376d7768ad237ae4fc46ead7dc9591ae1 - // => data hash for chunk0 - // 9ef1e5694bdb014a1eea42be756a8f63bfd8781d6332e9ef3b5126d90c62f110 - // => data hash for all chunks - // d9cb6bf9264006fcea490d5c261f7453ab95b1b26033a3805996791b8e3a62f3 - // => payload for batch header - // 01 - // 0000000000000001 - // 0000000000000001 - // 0000000000000001 - // d9cb6bf9264006fcea490d5c261f7453ab95b1b26033a3805996791b8e3a62f3 - // 013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757 - // 119b828c2a2798d2c957228ebeaff7e10bb099ae0d4e224f3eeb779ff61cba61 - // 0000000000000000000000000000000000000000000000000000000000000000 - // => hash for batch header - // 66b68a5092940d88a8c6f203d2071303557c024275d8ceaa2e12662bc61c8d8f - bytes memory batchHeader1 = new bytes(121 + 32); - assembly { - mstore8(add(batchHeader1, 0x20), 1) // version - mstore(add(batchHeader1, add(0x20, 1)), shl(192, 1)) // batchIndex = 1 - mstore(add(batchHeader1, add(0x20, 9)), shl(192, 1)) // l1MessagePopped = 1 - mstore(add(batchHeader1, add(0x20, 17)), shl(192, 1)) // totalL1MessagePopped = 1 - mstore(add(batchHeader1, add(0x20, 25)), 0xd9cb6bf9264006fcea490d5c261f7453ab95b1b26033a3805996791b8e3a62f3) // dataHash - mstore(add(batchHeader1, add(0x20, 57)), blobVersionedHash) // blobVersionedHash - mstore(add(batchHeader1, add(0x20, 89)), batchHash0) // parentBatchHash - mstore(add(batchHeader1, add(0x20, 121)), 0) // bitmap0 - } - chunk0 = new bytes(1 + 60); - assembly { - mstore(add(chunk0, 0x20), shl(248, 1)) // numBlocks = 1 - mstore(add(chunk0, add(0x21, 56)), shl(240, 1)) // numTransactions = 1 - mstore(add(chunk0, add(0x21, 58)), shl(240, 1)) // numL1Messages = 1 - } - chunks = new bytes[](1); - chunks[0] = chunk0; - bitmap = new bytes(32); - hevm.startPrank(address(0)); - hevm.expectEmit(true, true, false, true); - emit CommitBatch(1, keccak256(batchHeader1)); - rollup.commitBatch(1, batchHeader0, chunks, bitmap); - hevm.stopPrank(); - assertBoolEq(rollup.isBatchFinalized(1), false); - bytes32 batchHash1 = rollup.committedBatches(1); - assertEq(batchHash1, keccak256(batchHeader1)); - - // commit batch2 with version v2, with two chunks, correctly - // 1. chunk0 has one block, 3 tx, no L1 messages - // => payload for chunk0 - // 0000000000000000 - // 0000000000000456 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000 - // 0003 - // ... (some tx hashes) - // => data hash for chunk0 - // 1c7649f248aed8448fa7997e44db7b7028581deb119c6d6aa1a2d126d62564cf - // 2. chunk1 has three blocks - // 2.1 block0 has 5 tx, 3 L1 messages, no skips - // 2.2 block1 has 10 tx, 5 L1 messages, even is skipped, last is not skipped - // 2.2 block1 has 300 tx, 256 L1 messages, odd position is skipped, last is not skipped - // => payload for chunk1 - // 0000000000000000 - // 0000000000000789 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000 - // 0005 - // 0000000000000000 - // 0000000000001234 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000 - // 000a - // 0000000000000000 - // 0000000000005678 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000 - // 012c - // => data hash for chunk1 - // 4e82cb576135a69a0ecc2b2070c432abfdeb20076594faaa1aeed77f48d7c856 - // => data hash for all chunks - // 166e9d20206ae8cddcdf0f30093e3acc3866937172df5d7f69fb5567d9595239 - // => payload for batch header - // 03 - // 0000000000000002 - // 0000000000000108 - // 0000000000000109 - // 166e9d20206ae8cddcdf0f30093e3acc3866937172df5d7f69fb5567d9595239 - // 013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757 - // 66b68a5092940d88a8c6f203d2071303557c024275d8ceaa2e12662bc61c8d8f - // 0000000000005678 - // 2c9d777660f14ad49803a6442935c0d24a0d83551de5995890bf70a17d24e687 - // 53ab0fe6807c7081f0885fe7da741554d658a03730b1fa006f8319f8b993bcb0 - // => hash for batch header - // f212a256744ca658dfc4eb32665aa0fe845eb757a030bd625cb2880055e3cc92 - bytes memory batchHeader2 = new bytes(193); - assembly { - mstore8(add(batchHeader2, 0x20), 3) // version - mstore(add(batchHeader2, add(0x20, 1)), shl(192, 2)) // batchIndex = 2 - mstore(add(batchHeader2, add(0x20, 9)), shl(192, 264)) // l1MessagePopped = 264 - mstore(add(batchHeader2, add(0x20, 17)), shl(192, 265)) // totalL1MessagePopped = 265 - mstore(add(batchHeader2, add(0x20, 25)), 0x166e9d20206ae8cddcdf0f30093e3acc3866937172df5d7f69fb5567d9595239) // dataHash - mstore(add(batchHeader2, add(0x20, 57)), blobVersionedHash) // blobVersionedHash - mstore(add(batchHeader2, add(0x20, 89)), batchHash1) // parentBatchHash - mstore(add(batchHeader2, add(0x20, 121)), shl(192, 0x5678)) // lastBlockTimestamp - mcopy(add(batchHeader2, add(0x20, 129)), add(blobDataProof, 0x20), 64) // blobDataProof - } - chunk0 = new bytes(1 + 60); - assembly { - mstore(add(chunk0, 0x20), shl(248, 1)) // numBlocks = 1 - mstore(add(chunk0, add(0x21, 8)), shl(192, 0x456)) // timestamp = 0x456 - mstore(add(chunk0, add(0x21, 56)), shl(240, 3)) // numTransactions = 3 - mstore(add(chunk0, add(0x21, 58)), shl(240, 0)) // numL1Messages = 0 - } - chunk1 = new bytes(1 + 60 * 3); - assembly { - mstore(add(chunk1, 0x20), shl(248, 3)) // numBlocks = 3 - mstore(add(chunk1, add(33, 8)), shl(192, 0x789)) // block0.timestamp = 0x789 - mstore(add(chunk1, add(33, 56)), shl(240, 5)) // block0.numTransactions = 5 - mstore(add(chunk1, add(33, 58)), shl(240, 3)) // block0.numL1Messages = 3 - mstore(add(chunk1, add(93, 8)), shl(192, 0x1234)) // block1.timestamp = 0x1234 - mstore(add(chunk1, add(93, 56)), shl(240, 10)) // block1.numTransactions = 10 - mstore(add(chunk1, add(93, 58)), shl(240, 5)) // block1.numL1Messages = 5 - mstore(add(chunk1, add(153, 8)), shl(192, 0x5678)) // block1.timestamp = 0x5678 - mstore(add(chunk1, add(153, 56)), shl(240, 300)) // block1.numTransactions = 300 - mstore(add(chunk1, add(153, 58)), shl(240, 256)) // block1.numL1Messages = 256 - } - chunks = new bytes[](2); - chunks[0] = chunk0; - chunks[1] = chunk1; - bitmap = new bytes(64); - assembly { - mstore( - add(bitmap, add(0x20, 0)), - 77194726158210796949047323339125271902179989777093709359638389338608753093160 - ) // bitmap0 - mstore(add(bitmap, add(0x20, 32)), 42) // bitmap1 - } - - rollup.updateMaxNumTxInChunk(186); - // should revert, when all v1 batch not finalized - hevm.startPrank(address(0)); - hevm.expectRevert("start index mismatch"); - rollup.commitBatchWithBlobProof(3, batchHeader1, chunks, bitmap, blobDataProof); - hevm.stopPrank(); - - // finalize batch1 - hevm.startPrank(address(0)); - hevm.expectEmit(true, true, false, true); - emit FinalizeBatch(1, batchHash1, bytes32(uint256(2)), bytes32(uint256(3))); - rollup.finalizeBatchWithProof4844( - batchHeader1, - bytes32(uint256(1)), - bytes32(uint256(2)), - bytes32(uint256(3)), - blobDataProof, - new bytes(0) - ); - hevm.stopPrank(); - assertBoolEq(rollup.isBatchFinalized(1), true); - assertEq(rollup.finalizedStateRoots(1), bytes32(uint256(2))); - assertEq(rollup.withdrawRoots(1), bytes32(uint256(3))); - assertEq(rollup.lastFinalizedBatchIndex(), 1); - assertBoolEq(messageQueue.isMessageSkipped(0), false); - assertEq(messageQueue.pendingQueueIndex(), 1); - assertEq(messageQueue.nextUnfinalizedQueueIndex(), 1); - - hevm.startPrank(address(0)); - hevm.expectEmit(true, true, false, true); - emit CommitBatch(2, keccak256(batchHeader2)); - rollup.commitBatchWithBlobProof(3, batchHeader1, chunks, bitmap, blobDataProof); - hevm.stopPrank(); - bytes32 batchHash2 = rollup.committedBatches(2); - assertEq(batchHash2, keccak256(batchHeader2)); - assertEq(messageQueue.pendingQueueIndex(), 265); - assertEq(messageQueue.nextUnfinalizedQueueIndex(), 1); - - // finalize batch2 - assertBoolEq(rollup.isBatchFinalized(2), false); - hevm.startPrank(address(0)); - rollup.finalizeBundleWithProof(batchHeader2, bytes32(uint256(2)), bytes32(uint256(3)), new bytes(0)); - hevm.stopPrank(); - assertBoolEq(rollup.isBatchFinalized(2), true); - assertEq(rollup.finalizedStateRoots(2), bytes32(uint256(2))); - assertEq(rollup.withdrawRoots(2), bytes32(uint256(3))); - assertEq(rollup.lastFinalizedBatchIndex(), 2); - } - + /* function testRevertBatch() external { // upgrade to ScrollChainMockBlob ScrollChainMockBlob impl = new ScrollChainMockBlob( @@ -1518,6 +736,7 @@ contract ScrollChainTest is DSTestPlus { assertEq(uint256(rollup.committedBatches(1)), 0); assertEq(uint256(rollup.committedBatches(2)), 0); } + */ function testAddAndRemoveSequencer(address _sequencer) external { // set by non-owner, should revert @@ -1589,12 +808,8 @@ contract ScrollChainTest is DSTestPlus { hevm.startPrank(address(0)); hevm.expectRevert("Pausable: paused"); - rollup.commitBatch(1, new bytes(0), new bytes[](0), new bytes(0)); - hevm.expectRevert("Pausable: paused"); rollup.commitBatchWithBlobProof(3, new bytes(0), new bytes[](0), new bytes(0), new bytes(0)); hevm.expectRevert("Pausable: paused"); - rollup.finalizeBatchWithProof4844(new bytes(0), bytes32(0), bytes32(0), bytes32(0), new bytes(0), new bytes(0)); - hevm.expectRevert("Pausable: paused"); rollup.finalizeBundleWithProof(new bytes(0), bytes32(0), bytes32(0), new bytes(0)); hevm.stopPrank();