diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index c661609ea7e..b5779fe8472 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -27,6 +27,7 @@ abstract contract TaikoErrors { error L1_INVALID_TRANSITION(); error L1_LIVENESS_BOND_NOT_RECEIVED(); error L1_NOT_ASSIGNED_PROVER(); + error L1_NO_HOOKS(); error L1_PROVING_PAUSED(); error L1_RECEIVE_DISABLED(); error L1_TOO_LATE(); diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index adb499adab5..9c8244e3439 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -40,6 +40,7 @@ library LibProposing { error L1_INVALID_PROVER(); error L1_INVALID_SIG(); error L1_LIVENESS_BOND_NOT_RECEIVED(); + error L1_NOT_SAME_ADDRESS(); error L1_TOO_MANY_BLOCKS(); error L1_UNEXPECTED_PARENT(); @@ -180,7 +181,10 @@ library LibProposing { ++_state.slotB.numBlocks; } - { + if (params.hookCalls.length == 0) { + if (params.assignedProver != msg.sender) revert L1_NOT_SAME_ADDRESS(); + _tko.transferFrom(msg.sender, address(this), _config.livenessBond); + } else { uint256 tkoBalance = _tko.balanceOf(address(this)); // Run all hooks. @@ -202,10 +206,6 @@ library LibProposing { prevHook = params.hookCalls[i].hook; } - // Refund Ether - if (address(this).balance != 0) { - msg.sender.sendEtherAndVerify(address(this).balance); - } // Check that after hooks, the Taiko Token balance of this contract // have increased by the same amount as _config.livenessBond (to prevent) @@ -216,6 +216,11 @@ library LibProposing { } } + // Refund Ether + if (address(this).balance != 0) { + msg.sender.sendEtherAndVerify(address(this).balance); + } + deposits_ = new TaikoData.EthDeposit[](0); emit BlockProposed({ blockId: blk.blockId, diff --git a/packages/protocol/contracts/team/proving/ProverSet.sol b/packages/protocol/contracts/team/proving/ProverSet.sol index a483d52b5e6..00ba19a7ebf 100644 --- a/packages/protocol/contracts/team/proving/ProverSet.sol +++ b/packages/protocol/contracts/team/proving/ProverSet.sol @@ -26,7 +26,6 @@ contract ProverSet is EssentialContract, IERC1271 { uint256[48] private __gap; event ProverEnabled(address indexed prover, bool indexed enabled); - event BlockProvenBy(address indexed prover, uint64 indexed blockId); error INVALID_STATUS(); error PERMISSION_DENIED(); @@ -78,9 +77,20 @@ contract ProverSet is EssentialContract, IERC1271 { IERC20(tkoToken()).transfer(admin, _amount); } + /// @notice Propose a Taiko block. + function proposeBlock( + bytes calldata _params, + bytes calldata _txList + ) + external + onlyProver + nonReentrant + { + ITaikoL1(taikoL1()).proposeBlock(_params, _txList); + } + /// @notice Proves or contests a Taiko block. function proveBlock(uint64 _blockId, bytes calldata _input) external onlyProver nonReentrant { - emit BlockProvenBy(msg.sender, _blockId); ITaikoL1(taikoL1()).proveBlock(_blockId, _input); } diff --git a/packages/protocol/test/L1/TaikoL1TestBase.sol b/packages/protocol/test/L1/TaikoL1TestBase.sol index 525660c06fa..adf4cbed7e8 100644 --- a/packages/protocol/test/L1/TaikoL1TestBase.sol +++ b/packages/protocol/test/L1/TaikoL1TestBase.sol @@ -172,10 +172,13 @@ abstract contract TaikoL1TestBase is TaikoTest { meta.difficulty = bytes32(_difficulty); meta.gasLimit = gasLimit; - TaikoData.HookCall[] memory hookcalls = new TaikoData.HookCall[](1); - - hookcalls[0] = TaikoData.HookCall(address(assignmentHook), abi.encode(assignment)); - + TaikoData.HookCall[] memory hookcalls; + if (prover != proposer) { + hookcalls = new TaikoData.HookCall[](1); + hookcalls[0] = TaikoData.HookCall(address(assignmentHook), abi.encode(assignment)); + } else { + hookcalls = new TaikoData.HookCall[](0); + } vm.prank(proposer, proposer); (meta, ethDeposits) = L1.proposeBlock{ value: msgValue }( abi.encode(TaikoData.BlockParams(prover, address(0), 0, 0, hookcalls, "")), diff --git a/packages/protocol/test/L1/TaikoL1TestGroup1.t.sol b/packages/protocol/test/L1/TaikoL1TestGroup1.t.sol index 3197e55517c..a9f8bd93f30 100644 --- a/packages/protocol/test/L1/TaikoL1TestGroup1.t.sol +++ b/packages/protocol/test/L1/TaikoL1TestGroup1.t.sol @@ -471,4 +471,34 @@ contract TaikoL1TestGroup1 is TaikoL1TestGroupBase { assertEq(tko.balanceOf(Bob), 10_000 ether - livenessBond); } } + + // Test summary: + // 1. Alice proposes a block, assigning herself as the prover. + function test_taikoL1_group_1_case_7_no_hooks() external { + vm.warp(1_000_000); + printBlockAndTrans(0); + + giveEthAndTko(Alice, 10_000 ether, 1000 ether); + + console2.log("====== Alice propose a block with herself as the assigned prover"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, Alice, ""); + + uint96 livenessBond = L1.getConfig().livenessBond; + uint256 proposedAt; + { + printBlockAndTrans(meta.id); + TaikoData.Block memory blk = L1.getBlock(meta.id); + assertEq(meta.minTier, LibTiers.TIER_OPTIMISTIC); + + assertEq(blk.nextTransitionId, 1); + assertEq(blk.verifiedTransitionId, 0); + assertEq(blk.proposedAt, block.timestamp); + assertEq(blk.assignedProver, Alice); + assertEq(blk.livenessBond, livenessBond); + + proposedAt = blk.proposedAt; + + assertEq(tko.balanceOf(Alice), 10_000 ether - livenessBond); + } + } } diff --git a/packages/protocol/test/bridge/QuotaManager.t.sol b/packages/protocol/test/bridge/QuotaManager.t.sol index 367c1063835..3c7985e6294 100644 --- a/packages/protocol/test/bridge/QuotaManager.t.sol +++ b/packages/protocol/test/bridge/QuotaManager.t.sol @@ -72,7 +72,7 @@ contract QuotaManagerTest is TaikoTest { assertEq(qm.availableQuota(token, 0), type(uint256).max); } - function test_calc_quota() public { + function test_calc_quota() public pure { uint24 quotaPeriod = 24 hours; uint104 value = 4_000_000; // USD uint104 priceETH = 4000; // USD