diff --git a/packages/protocol/contracts/L1/ITaikoL1.sol b/packages/protocol/contracts/L1/ITaikoL1.sol index b01e83131e..6f32ba2017 100644 --- a/packages/protocol/contracts/L1/ITaikoL1.sol +++ b/packages/protocol/contracts/L1/ITaikoL1.sol @@ -20,7 +20,7 @@ interface ITaikoL1 { returns (TaikoData.BlockMetadata memory meta_, TaikoData.EthDeposit[] memory deposits_); /// @notice Proposes a Taiko L2 block (version 2) - /// @param _params Block parameters, currently an encoded BlockParams object. + /// @param _params Block parameters, an encoded BlockParamsV2 object. /// @param _txList txList data if calldata is used for DA. /// @return meta_ The metadata of the proposed L2 block. function proposeBlockV2( @@ -30,6 +30,17 @@ interface ITaikoL1 { external returns (TaikoData.BlockMetadataV2 memory meta_); + /// @notice Proposes a Taiko L2 block (version 2) + /// @param _paramsArr A list of encoded BlockParamsV2 objects. + /// @param _txListArr A list of txList. + /// @return metaArr_ The metadata objects of the proposed L2 blocks. + function proposeBlocksV2( + bytes[] calldata _paramsArr, + bytes[] calldata _txListArr + ) + external + returns (TaikoData.BlockMetadataV2[] memory metaArr_); + /// @notice Proves or contests a block transition. /// @param _blockId The index of the block to prove. This is also used to /// select the right implementation version. diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 664d18ff31..a4a03717b2 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -25,6 +25,7 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents { uint256[50] private __gap; error L1_FORK_ERROR(); + error L1_INVALID_PARAMS(); error L1_RECEIVE_DISABLED(); modifier whenProvingNotPaused() { @@ -37,6 +38,11 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents { emit StateVariablesUpdated(state.slotB); } + modifier onlyRegisteredProposer() { + LibProposing.checkProposerPermission(this); + _; + } + /// @dev Allows for receiving Ether from Hooks receive() external payable { if (!inNonReentrant()) revert L1_RECEIVE_DISABLED(); @@ -76,6 +82,7 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents { ) external payable + onlyRegisteredProposer whenNotPaused nonReentrant emitEventForClient @@ -96,18 +103,36 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents { bytes calldata _txList ) external + onlyRegisteredProposer whenNotPaused nonReentrant emitEventForClient - returns (TaikoData.BlockMetadataV2 memory meta_) + returns (TaikoData.BlockMetadataV2 memory) { - TaikoData.Config memory config = getConfig(); + return _proposeBlock(_params, _txList, getConfig()); + } - (, meta_,) = LibProposing.proposeBlock(state, config, this, _params, _txList); - if (meta_.id < config.ontakeForkHeight) revert L1_FORK_ERROR(); + /// @inheritdoc ITaikoL1 + function proposeBlocksV2( + bytes[] calldata _paramsArr, + bytes[] calldata _txListArr + ) + external + onlyRegisteredProposer + whenNotPaused + nonReentrant + emitEventForClient + returns (TaikoData.BlockMetadataV2[] memory metaArr_) + { + if (_paramsArr.length == 0 || _paramsArr.length != _txListArr.length) { + revert L1_INVALID_PARAMS(); + } - if (LibUtils.shouldVerifyBlocks(config, meta_.id, true) && !state.slotB.provingPaused) { - LibVerifying.verifyBlocks(state, config, this, config.maxBlocksToVerify); + metaArr_ = new TaikoData.BlockMetadataV2[](_paramsArr.length); + TaikoData.Config memory config = getConfig(); + + for (uint256 i; i < _paramsArr.length; ++i) { + metaArr_[i] = _proposeBlock(_paramsArr[i], _txListArr[i], config); } } @@ -276,6 +301,22 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents { }); } + function _proposeBlock( + bytes calldata _params, + bytes calldata _txList, + TaikoData.Config memory _config + ) + internal + returns (TaikoData.BlockMetadataV2 memory meta_) + { + (, meta_,) = LibProposing.proposeBlock(state, _config, this, _params, _txList); + if (meta_.id < _config.ontakeForkHeight) revert L1_FORK_ERROR(); + + if (LibUtils.shouldVerifyBlocks(_config, meta_.id, true) && !state.slotB.provingPaused) { + LibVerifying.verifyBlocks(state, _config, this, _config.maxBlocksToVerify); + } + } + /// @dev chain_pauser is supposed to be a cold wallet. function _authorizePause( address, diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 6edd718781..df332d5596 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -17,7 +17,6 @@ library LibProposing { struct Local { TaikoData.SlotB b; TaikoData.BlockParamsV2 params; - address proposerAccess; ITierProvider tierProvider; bytes32 parentMetaHash; bool postFork; @@ -82,15 +81,6 @@ library LibProposing { { // Checks proposer access. Local memory local; - - local.proposerAccess = _resolver.resolve(LibStrings.B_PROPOSER_ACCESS, true); - if ( - local.proposerAccess != address(0) - && !IProposerAccess(local.proposerAccess).isProposerEligible(msg.sender) - ) { - revert L1_INVALID_PROPOSER(); - } - local.b = _state.slotB; local.postFork = local.b.numBlocks >= _config.ontakeForkHeight; @@ -263,4 +253,13 @@ library LibProposing { }); } } + + function checkProposerPermission(IAddressResolver _resolver) internal view { + address proposerAccess = _resolver.resolve(LibStrings.B_PROPOSER_ACCESS, true); + if (proposerAccess == address(0)) return; + + if (!IProposerAccess(proposerAccess).isProposerEligible(msg.sender)) { + revert L1_INVALID_PROPOSER(); + } + } } diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index 345957f440..b894396814 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -135,7 +135,7 @@ library LibProving { uint64 _blockId, bytes calldata _input ) - internal + public { Local memory local;