mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-09 14:08:03 -05:00
Upgrade#4 (#1394)
Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> Co-authored-by: Rohit Narurkar <rohit.narurkar@proton.me> Co-authored-by: colinlyguo <colinlyguo@scroll.io> Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com> Co-authored-by: Mengran Lan <mengran@scroll.io> Co-authored-by: amoylan2 <amoylan2@users.noreply.github.com> Co-authored-by: Mengran Lan <lanmengran@qq.com> Co-authored-by: Suuuuuuperrrrr fred <FredrikaPhililip@proton.me> Co-authored-by: sbaizet <74511063+sbaizet-ledger@users.noreply.github.com> Co-authored-by: caseylove <casey4love@foxmail.com> Co-authored-by: BoxChen <13927203+nishuzumi@users.noreply.github.com> Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> Co-authored-by: georgehao <georgehao@users.noreply.github.com>
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import {BatchHeaderV0Codec} from "../../../scroll-contracts/src/libraries/codec/BatchHeaderV0Codec.sol";
|
||||
import {BatchHeaderV1Codec} from "../../../scroll-contracts/src/libraries/codec/BatchHeaderV1Codec.sol";
|
||||
import {BatchHeaderV3Codec} from "../../../scroll-contracts/src/libraries/codec/BatchHeaderV3Codec.sol";
|
||||
import {ChunkCodecV0} from "../../../scroll-contracts/src/libraries/codec/ChunkCodecV0.sol";
|
||||
import {ChunkCodecV1} from "../../../scroll-contracts/src/libraries/codec/ChunkCodecV1.sol";
|
||||
|
||||
contract MockBridge {
|
||||
/**********
|
||||
* Errors *
|
||||
**********/
|
||||
|
||||
/// @dev Thrown when committing a committed batch.
|
||||
error ErrorBatchIsAlreadyCommitted();
|
||||
|
||||
@@ -20,7 +25,7 @@ contract MockBridge {
|
||||
error ErrorCallPointEvaluationPrecompileFailed();
|
||||
|
||||
/// @dev Thrown when the transaction has multiple blobs.
|
||||
error ErrorFoundMultipleBlob();
|
||||
error ErrorFoundMultipleBlobs();
|
||||
|
||||
/// @dev Thrown when some fields are not zero in genesis batch.
|
||||
error ErrorGenesisBatchHasNonZeroField();
|
||||
@@ -43,11 +48,8 @@ contract MockBridge {
|
||||
/// @dev Thrown when the batch index is incorrect.
|
||||
error ErrorIncorrectBatchIndex();
|
||||
|
||||
/// @dev Thrown when the previous state root doesn't match stored one.
|
||||
error ErrorIncorrectPreviousStateRoot();
|
||||
|
||||
/// @dev Thrown when the batch header version is invalid.
|
||||
error ErrorInvalidBatchHeaderVersion();
|
||||
/// @dev Thrown when the batch version is incorrect.
|
||||
error ErrorIncorrectBatchVersion();
|
||||
|
||||
/// @dev Thrown when no blob found in the transaction.
|
||||
error ErrorNoBlobFound();
|
||||
@@ -55,9 +57,6 @@ contract MockBridge {
|
||||
/// @dev Thrown when the number of transactions is less than number of L1 message in one block.
|
||||
error ErrorNumTxsLessThanNumL1Msgs();
|
||||
|
||||
/// @dev Thrown when the given previous state is zero.
|
||||
error ErrorPreviousStateRootIsZero();
|
||||
|
||||
/// @dev Thrown when the given state root is zero.
|
||||
error ErrorStateRootIsZero();
|
||||
|
||||
@@ -70,24 +69,37 @@ contract MockBridge {
|
||||
event CommitBatch(uint256 indexed batchIndex, bytes32 indexed batchHash);
|
||||
event FinalizeBatch(uint256 indexed batchIndex, bytes32 indexed batchHash, bytes32 stateRoot, bytes32 withdrawRoot);
|
||||
|
||||
struct L2MessageProof {
|
||||
uint256 batchIndex;
|
||||
bytes merkleProof;
|
||||
}
|
||||
/*************
|
||||
* Constants *
|
||||
*************/
|
||||
|
||||
/// @dev Address of the point evaluation precompile used for EIP-4844 blob verification.
|
||||
address constant POINT_EVALUATION_PRECOMPILE_ADDR = address(0x0A);
|
||||
address internal constant POINT_EVALUATION_PRECOMPILE_ADDR = address(0x0A);
|
||||
|
||||
/// @dev BLS Modulus value defined in EIP-4844 and the magic value returned from a successful call to the
|
||||
/// point evaluation precompile
|
||||
uint256 constant BLS_MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513;
|
||||
uint256 internal constant BLS_MODULUS =
|
||||
52435875175126190479447740508185965837690552500527637822603658699938581184513;
|
||||
|
||||
/// @notice The chain id of the corresponding layer 2 chain.
|
||||
uint64 public immutable layer2ChainId;
|
||||
|
||||
/*************
|
||||
* Variables *
|
||||
*************/
|
||||
|
||||
/// @notice The maximum number of transactions allowed in each chunk.
|
||||
uint256 public maxNumTxInChunk;
|
||||
|
||||
uint256 public l1BaseFee;
|
||||
uint256 public l1BlobBaseFee;
|
||||
uint256 public l2BaseFee;
|
||||
uint256 public lastFinalizedBatchIndex;
|
||||
|
||||
mapping(uint256 => bytes32) public committedBatches;
|
||||
|
||||
mapping(uint256 => bytes32) public finalizedStateRoots;
|
||||
|
||||
mapping(uint256 => bytes32) public withdrawRoots;
|
||||
|
||||
function setL1BaseFee(uint256 _l1BaseFee) external {
|
||||
@@ -108,6 +120,8 @@ contract MockBridge {
|
||||
*****************************/
|
||||
|
||||
/// @notice Import layer 2 genesis block
|
||||
/// @param _batchHeader The header of the genesis batch.
|
||||
/// @param _stateRoot The state root of the genesis block.
|
||||
function importGenesisBatch(bytes calldata _batchHeader, bytes32 _stateRoot) external {
|
||||
// check genesis batch header length
|
||||
if (_stateRoot == bytes32(0)) revert ErrorStateRootIsZero();
|
||||
@@ -141,16 +155,10 @@ contract MockBridge {
|
||||
bytes[] memory _chunks,
|
||||
bytes calldata
|
||||
) external {
|
||||
// check whether the batch is empty
|
||||
if (_chunks.length == 0) revert ErrorBatchIsEmpty();
|
||||
|
||||
(, bytes32 _parentBatchHash, uint256 _batchIndex, uint256 _totalL1MessagesPoppedOverall) = _loadBatchHeader(
|
||||
_parentBatchHeader
|
||||
(bytes32 _parentBatchHash, uint256 _batchIndex, uint256 _totalL1MessagesPoppedOverall) = _beforeCommitBatch(
|
||||
_parentBatchHeader,
|
||||
_chunks
|
||||
);
|
||||
unchecked {
|
||||
_batchIndex += 1;
|
||||
}
|
||||
if (committedBatches[_batchIndex] != 0) revert ErrorBatchIsAlreadyCommitted();
|
||||
|
||||
bytes32 _batchHash;
|
||||
uint256 batchPtr;
|
||||
@@ -166,7 +174,7 @@ contract MockBridge {
|
||||
_totalL1MessagesPoppedOverall := add(_totalL1MessagesPoppedOverall, _totalL1MessagesPoppedInBatch)
|
||||
}
|
||||
// store entries, the order matters
|
||||
BatchHeaderV0Codec.storeVersion(batchPtr, _version);
|
||||
BatchHeaderV0Codec.storeVersion(batchPtr, 0);
|
||||
BatchHeaderV0Codec.storeBatchIndex(batchPtr, _batchIndex);
|
||||
BatchHeaderV0Codec.storeL1MessagePopped(batchPtr, _totalL1MessagesPoppedInBatch);
|
||||
BatchHeaderV0Codec.storeTotalL1MessagePopped(batchPtr, _totalL1MessagesPoppedOverall);
|
||||
@@ -177,9 +185,10 @@ contract MockBridge {
|
||||
batchPtr,
|
||||
BatchHeaderV0Codec.BATCH_HEADER_FIXED_LENGTH
|
||||
);
|
||||
} else {
|
||||
bytes32 blobVersionedHash;
|
||||
(blobVersionedHash, _dataHash, _totalL1MessagesPoppedInBatch) = _commitChunksV1(
|
||||
} else if (_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
|
||||
);
|
||||
@@ -187,56 +196,125 @@ contract MockBridge {
|
||||
batchPtr := mload(0x40)
|
||||
_totalL1MessagesPoppedOverall := add(_totalL1MessagesPoppedOverall, _totalL1MessagesPoppedInBatch)
|
||||
}
|
||||
|
||||
// store entries, the order matters
|
||||
BatchHeaderV1Codec.storeVersion(batchPtr, _version);
|
||||
BatchHeaderV1Codec.storeBatchIndex(batchPtr, _batchIndex);
|
||||
BatchHeaderV1Codec.storeL1MessagePopped(batchPtr, _totalL1MessagesPoppedInBatch);
|
||||
BatchHeaderV1Codec.storeTotalL1MessagePopped(batchPtr, _totalL1MessagesPoppedOverall);
|
||||
BatchHeaderV1Codec.storeDataHash(batchPtr, _dataHash);
|
||||
BatchHeaderV1Codec.storeBlobVersionedHash(batchPtr, blobVersionedHash);
|
||||
// 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);
|
||||
// compute batch hash
|
||||
_batchHash = BatchHeaderV1Codec.computeBatchHash(
|
||||
// compute batch hash, V1 and V2 has same code as V0
|
||||
_batchHash = BatchHeaderV0Codec.computeBatchHash(
|
||||
batchPtr,
|
||||
BatchHeaderV1Codec.BATCH_HEADER_FIXED_LENGTH
|
||||
);
|
||||
} else {
|
||||
revert ErrorIncorrectBatchVersion();
|
||||
}
|
||||
|
||||
committedBatches[_batchIndex] = _batchHash;
|
||||
emit CommitBatch(_batchIndex, _batchHash);
|
||||
_afterCommitBatch(_batchIndex, _batchHash);
|
||||
}
|
||||
|
||||
/// @dev This function will revert unless all V0/V1/V2 batches are finalized. This is because we start to
|
||||
/// pop L1 messages in `commitBatchWithBlobProof` but not in `commitBatch`. We also introduce `finalizedQueueIndex`
|
||||
/// in `L1MessageQueue`. If one of V0/V1/V2 batches not finalized, `L1MessageQueue.pendingQueueIndex` will not
|
||||
/// match `parentBatchHeader.totalL1MessagePopped` and thus revert.
|
||||
function commitBatchWithBlobProof(
|
||||
uint8 _version,
|
||||
bytes calldata _parentBatchHeader,
|
||||
bytes[] memory _chunks,
|
||||
bytes calldata,
|
||||
bytes calldata _blobDataProof
|
||||
) external {
|
||||
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
|
||||
);
|
||||
unchecked {
|
||||
_totalL1MessagesPoppedOverall += _totalL1MessagesPoppedInBatch;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// @dev We keep this function to upgrade to 4844 more smoothly.
|
||||
function finalizeBatchWithProof(
|
||||
bytes calldata _batchHeader,
|
||||
bytes32 _prevStateRoot,
|
||||
bytes32, /*_prevStateRoot*/
|
||||
bytes32 _postStateRoot,
|
||||
bytes32 _withdrawRoot,
|
||||
bytes calldata
|
||||
) external {
|
||||
if (_prevStateRoot == bytes32(0)) revert ErrorPreviousStateRootIsZero();
|
||||
if (_postStateRoot == bytes32(0)) revert ErrorStateRootIsZero();
|
||||
(uint256 batchPtr, bytes32 _batchHash, uint256 _batchIndex) = _beforeFinalizeBatch(
|
||||
_batchHeader,
|
||||
_postStateRoot
|
||||
);
|
||||
|
||||
// compute batch hash and verify
|
||||
(, bytes32 _batchHash, uint256 _batchIndex, ) = _loadBatchHeader(_batchHeader);
|
||||
|
||||
// verify previous state root.
|
||||
if (finalizedStateRoots[_batchIndex - 1] != _prevStateRoot) revert ErrorIncorrectPreviousStateRoot();
|
||||
|
||||
// avoid duplicated verification
|
||||
if (finalizedStateRoots[_batchIndex] != bytes32(0)) revert ErrorBatchIsAlreadyVerified();
|
||||
|
||||
// check and update lastFinalizedBatchIndex
|
||||
unchecked {
|
||||
if (lastFinalizedBatchIndex + 1 != _batchIndex) revert ErrorIncorrectBatchIndex();
|
||||
lastFinalizedBatchIndex = _batchIndex;
|
||||
// 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)
|
||||
);
|
||||
}
|
||||
|
||||
// record state root and withdraw root
|
||||
finalizedStateRoots[_batchIndex] = _postStateRoot;
|
||||
withdrawRoots[_batchIndex] = _withdrawRoot;
|
||||
|
||||
emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
// Pop finalized and non-skipped message from L1MessageQueue.
|
||||
uint256 _totalL1MessagesPoppedOverall = BatchHeaderV0Codec.getTotalL1MessagePopped(batchPtr);
|
||||
_afterFinalizeBatch(_totalL1MessagesPoppedOverall, _batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
/// @dev Memory layout of `_blobDataProof`:
|
||||
@@ -247,37 +325,140 @@ contract MockBridge {
|
||||
/// ```
|
||||
function finalizeBatchWithProof4844(
|
||||
bytes calldata _batchHeader,
|
||||
bytes32 _prevStateRoot,
|
||||
bytes32,
|
||||
bytes32 _postStateRoot,
|
||||
bytes32 _withdrawRoot,
|
||||
bytes calldata _blobDataProof,
|
||||
bytes calldata
|
||||
) external {
|
||||
if (_prevStateRoot == bytes32(0)) revert ErrorPreviousStateRootIsZero();
|
||||
(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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Pop finalized and non-skipped message from L1MessageQueue.
|
||||
uint256 _totalL1MessagesPoppedOverall = BatchHeaderV0Codec.getTotalL1MessagePopped(batchPtr);
|
||||
_afterFinalizeBatch(_totalL1MessagesPoppedOverall, _batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
function finalizeBundleWithProof(
|
||||
bytes calldata _batchHeader,
|
||||
bytes32 _postStateRoot,
|
||||
bytes32 _withdrawRoot,
|
||||
bytes calldata
|
||||
) external {
|
||||
if (_postStateRoot == bytes32(0)) revert ErrorStateRootIsZero();
|
||||
|
||||
// retrieve finalized state root and batch hash from storage
|
||||
uint256 _finalizedBatchIndex = lastFinalizedBatchIndex;
|
||||
|
||||
// compute pending batch hash and verify
|
||||
(, bytes32 _batchHash, uint256 _batchIndex, ) = _loadBatchHeader(_batchHeader);
|
||||
if (_batchIndex <= _finalizedBatchIndex) revert ErrorBatchIsAlreadyVerified();
|
||||
|
||||
// store in state
|
||||
// @note we do not store intermediate finalized roots
|
||||
lastFinalizedBatchIndex = _batchIndex;
|
||||
finalizedStateRoots[_batchIndex] = _postStateRoot;
|
||||
withdrawRoots[_batchIndex] = _withdrawRoot;
|
||||
|
||||
emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* Internal Functions *
|
||||
**********************/
|
||||
|
||||
/// @dev Internal function to do common checks before actual batch committing.
|
||||
/// @param _parentBatchHeader The parent batch header in calldata.
|
||||
/// @param _chunks The list of chunks in memory.
|
||||
/// @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)
|
||||
private
|
||||
view
|
||||
returns (
|
||||
bytes32 _parentBatchHash,
|
||||
uint256 _batchIndex,
|
||||
uint256 _totalL1MessagesPoppedOverall
|
||||
)
|
||||
{
|
||||
// check whether the batch is empty
|
||||
if (_chunks.length == 0) revert ErrorBatchIsEmpty();
|
||||
(, _parentBatchHash, _batchIndex, _totalL1MessagesPoppedOverall) = _loadBatchHeader(_parentBatchHeader);
|
||||
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 {
|
||||
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
|
||||
(uint256 memPtr, bytes32 _batchHash, uint256 _batchIndex, ) = _loadBatchHeader(_batchHeader);
|
||||
bytes32 _blobVersionedHash = BatchHeaderV1Codec.getBlobVersionedHash(memPtr);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// verify previous state root.
|
||||
if (finalizedStateRoots[_batchIndex - 1] != _prevStateRoot) revert ErrorIncorrectPreviousStateRoot();
|
||||
(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
|
||||
/// @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,
|
||||
uint256 _batchIndex,
|
||||
bytes32 _batchHash,
|
||||
bytes32 _postStateRoot,
|
||||
bytes32 _withdrawRoot
|
||||
) internal {
|
||||
// check and update lastFinalizedBatchIndex
|
||||
unchecked {
|
||||
if (lastFinalizedBatchIndex + 1 != _batchIndex) revert ErrorIncorrectBatchIndex();
|
||||
@@ -291,19 +472,43 @@ contract MockBridge {
|
||||
emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* Internal Functions *
|
||||
**********************/
|
||||
/// @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 get the blob versioned hash.
|
||||
/// @return _blobVersionedHash The retrieved blob versioned hash.
|
||||
function _getBlobVersionedHash() internal virtual returns (bytes32 _blobVersionedHash) {
|
||||
bytes32 _secondBlob;
|
||||
// Get blob's versioned hash
|
||||
assembly {
|
||||
_blobVersionedHash := blobhash(0)
|
||||
_secondBlob := blobhash(1)
|
||||
}
|
||||
if (_blobVersionedHash == bytes32(0)) revert ErrorNoBlobFound();
|
||||
if (_secondBlob != bytes32(0)) revert ErrorFoundMultipleBlobs();
|
||||
}
|
||||
|
||||
/// @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.
|
||||
/// @return _batchDataHash The computed data hash for the list of chunks.
|
||||
/// @return _totalL1MessagesPoppedInBatch The total number of L1 messages poped in this batch, including skipped one.
|
||||
/// @return _totalL1MessagesPoppedInBatch The total number of L1 messages popped in this batch, including skipped one.
|
||||
function _commitChunksV0(
|
||||
uint256 _totalL1MessagesPoppedOverall,
|
||||
bytes[] memory _chunks
|
||||
) internal pure returns (bytes32 _batchDataHash, uint256 _totalL1MessagesPoppedInBatch) {
|
||||
) internal view returns (bytes32 _batchDataHash, uint256 _totalL1MessagesPoppedInBatch) {
|
||||
uint256 _chunksLength = _chunks.length;
|
||||
|
||||
// load `batchDataHashPtr` and reserve the memory region for chunk data hashes
|
||||
@@ -341,32 +546,12 @@ contract MockBridge {
|
||||
/// @dev Internal function to commit chunks with version 1
|
||||
/// @param _totalL1MessagesPoppedOverall The number of L1 messages popped before the list of chunks.
|
||||
/// @param _chunks The list of chunks to commit.
|
||||
/// @return _blobVersionedHash The blob versioned hash for the blob carried in this transaction.
|
||||
/// @return _batchDataHash The computed data hash for the list of chunks.
|
||||
/// @return _totalL1MessagesPoppedInBatch The total number of L1 messages poped in this batch, including skipped one.
|
||||
/// @return _totalL1MessagesPoppedInBatch The total number of L1 messages popped in this batch, including skipped one.
|
||||
function _commitChunksV1(
|
||||
uint256 _totalL1MessagesPoppedOverall,
|
||||
bytes[] memory _chunks
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (
|
||||
bytes32 _blobVersionedHash,
|
||||
bytes32 _batchDataHash,
|
||||
uint256 _totalL1MessagesPoppedInBatch
|
||||
)
|
||||
{
|
||||
{
|
||||
bytes32 _secondBlob;
|
||||
// Get blob's versioned hash
|
||||
assembly {
|
||||
_blobVersionedHash := blobhash(0)
|
||||
_secondBlob := blobhash(1)
|
||||
}
|
||||
if (_blobVersionedHash == bytes32(0)) revert ErrorNoBlobFound();
|
||||
if (_secondBlob != bytes32(0)) revert ErrorFoundMultipleBlob();
|
||||
}
|
||||
|
||||
) internal view returns (bytes32 _batchDataHash, uint256 _totalL1MessagesPoppedInBatch) {
|
||||
uint256 _chunksLength = _chunks.length;
|
||||
|
||||
// load `batchDataHashPtr` and reserve the memory region for chunk data hashes
|
||||
@@ -424,22 +609,25 @@ contract MockBridge {
|
||||
version := shr(248, calldataload(_batchHeader.offset))
|
||||
}
|
||||
|
||||
// version should be always 0 or 1 in current code
|
||||
uint256 _length;
|
||||
if (version == 0) {
|
||||
(batchPtr, _length) = BatchHeaderV0Codec.loadAndValidate(_batchHeader);
|
||||
_batchHash = BatchHeaderV0Codec.computeBatchHash(batchPtr, _length);
|
||||
_batchIndex = BatchHeaderV0Codec.getBatchIndex(batchPtr);
|
||||
} else {
|
||||
} else if (version <= 2) {
|
||||
(batchPtr, _length) = BatchHeaderV1Codec.loadAndValidate(_batchHeader);
|
||||
_batchHash = BatchHeaderV1Codec.computeBatchHash(batchPtr, _length);
|
||||
_batchIndex = BatchHeaderV1Codec.getBatchIndex(batchPtr);
|
||||
} else if (version >= 3) {
|
||||
(batchPtr, _length) = BatchHeaderV3Codec.loadAndValidate(_batchHeader);
|
||||
}
|
||||
|
||||
// the code for compute batch hash is the same for V0, V1, V2, V3
|
||||
// also the `_batchIndex` and `_totalL1MessagesPoppedOverall`.
|
||||
_batchHash = BatchHeaderV0Codec.computeBatchHash(batchPtr, _length);
|
||||
_batchIndex = BatchHeaderV0Codec.getBatchIndex(batchPtr);
|
||||
_totalL1MessagesPoppedOverall = BatchHeaderV0Codec.getTotalL1MessagePopped(batchPtr);
|
||||
|
||||
// only check when genesis is imported
|
||||
if (committedBatches[_batchIndex] != _batchHash && finalizedStateRoots[0] != bytes32(0)) {
|
||||
revert ErrorIncorrectBatchHash();
|
||||
}
|
||||
_totalL1MessagesPoppedOverall = BatchHeaderV0Codec.getTotalL1MessagePopped(batchPtr);
|
||||
}
|
||||
|
||||
/// @dev Internal function to commit a chunk with version 0.
|
||||
@@ -452,7 +640,7 @@ contract MockBridge {
|
||||
bytes memory _chunk,
|
||||
uint256 _totalL1MessagesPoppedInBatch,
|
||||
uint256 _totalL1MessagesPoppedOverall
|
||||
) internal pure returns (bytes32 _dataHash, uint256 _totalNumL1MessagesInChunk) {
|
||||
) internal view returns (bytes32 _dataHash, uint256 _totalNumL1MessagesInChunk) {
|
||||
uint256 chunkPtr;
|
||||
uint256 startDataPtr;
|
||||
uint256 dataPtr;
|
||||
@@ -481,6 +669,8 @@ contract MockBridge {
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -510,6 +700,9 @@ contract MockBridge {
|
||||
}
|
||||
}
|
||||
|
||||
// check the actual number of transactions in the chunk
|
||||
if ((dataPtr - txHashStartDataPtr) / 32 > maxNumTxInChunk) revert ErrorTooManyTxsInOneChunk();
|
||||
|
||||
assembly {
|
||||
chunkPtr := add(_chunk, 0x20)
|
||||
}
|
||||
@@ -532,7 +725,7 @@ contract MockBridge {
|
||||
bytes memory _chunk,
|
||||
uint256 _totalL1MessagesPoppedInBatch,
|
||||
uint256 _totalL1MessagesPoppedOverall
|
||||
) internal pure returns (bytes32 _dataHash, uint256 _totalNumL1MessagesInChunk) {
|
||||
) internal view returns (bytes32 _dataHash, uint256 _totalNumL1MessagesInChunk) {
|
||||
uint256 chunkPtr;
|
||||
uint256 startDataPtr;
|
||||
uint256 dataPtr;
|
||||
@@ -568,7 +761,7 @@ contract MockBridge {
|
||||
uint256 _numTransactionsInBlock = ChunkCodecV1.getNumTransactions(chunkPtr);
|
||||
if (_numTransactionsInBlock < _numL1MessagesInBlock) revert ErrorNumTxsLessThanNumL1Msgs();
|
||||
unchecked {
|
||||
_totalTransactionsInChunk += dataPtr - startPtr; // number of non-skipped l1 messages
|
||||
_totalTransactionsInChunk += (dataPtr - startPtr) / 32; // number of non-skipped l1 messages
|
||||
_totalTransactionsInChunk += _numTransactionsInBlock - _numL1MessagesInBlock; // number of l2 txs
|
||||
_totalL1MessagesPoppedInBatch += _numL1MessagesInBlock;
|
||||
_totalL1MessagesPoppedOverall += _numL1MessagesInBlock;
|
||||
@@ -578,6 +771,11 @@ contract MockBridge {
|
||||
}
|
||||
}
|
||||
|
||||
// check the actual number of transactions in the chunk
|
||||
if (_totalTransactionsInChunk > maxNumTxInChunk) {
|
||||
revert ErrorTooManyTxsInOneChunk();
|
||||
}
|
||||
|
||||
// compute data hash and store to memory
|
||||
assembly {
|
||||
_dataHash := keccak256(startDataPtr, sub(dataPtr, startDataPtr))
|
||||
|
||||
Reference in New Issue
Block a user