From b2176d30868d55342c4ec19271d55999bf6bb0f2 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Mon, 29 Jan 2024 16:51:52 +0800 Subject: [PATCH] fix(protocol): fix cooldown/proof window caused by pausing (TKO-12) (#15585) --- packages/protocol/contracts/L1/TaikoData.sol | 4 ++++ packages/protocol/contracts/L1/TaikoL1.sol | 11 ++++++++++ .../protocol/contracts/L1/libs/LibProving.sol | 21 ++++++++++++++----- .../contracts/L1/libs/LibProvingAlt.sol | 9 ++++++-- .../contracts/L1/libs/LibVerifying.sol | 5 ++++- .../contracts/common/OwnerUUPSUpgradable.sol | 9 +++++--- 6 files changed, 48 insertions(+), 11 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 6877b7c11c0..95d27ce31ec 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -177,6 +177,10 @@ library TaikoData { uint64 numBlocks; uint64 lastVerifiedBlockId; bool provingPaused; + uint8 __reserved1; + uint16 __reserved2; + uint32 __reserved3; + uint64 lastUnpausedAt; } /// @dev Struct holding the state variables for the {TaikoL1} contract. diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 405f908744e..16b7fad5d65 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -113,6 +113,11 @@ contract TaikoL1 is LibVerifying.verifyBlocks(state, getConfig(), AddressResolver(this), maxBlocksToVerify); } + function unpause() public override { + OwnerUUPSUpgradable.unpause(); + state.slotB.lastUnpausedAt = uint64(block.timestamp); + } + /// @notice Pause block proving. /// @param pause True if paused. function pauseProving(bool pause) external onlyOwner { @@ -242,4 +247,10 @@ contract TaikoL1 is function isConfigValid() public view returns (bool) { return LibVerifying.isConfigValid(getConfig()); } + + function _authorizePause(address) internal override { + if (msg.sender != owner() && msg.sender != resolve("rollup_watchdog", true)) { + revert L1_UNAUTHORIZED(); + } + } } diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index bdb1a8a6aa9..dea23c4058b 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -16,6 +16,7 @@ pragma solidity 0.8.20; import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "../../common/AddressResolver.sol"; +import "../../libs/LibMath.sol"; import "../tiers/ITierProvider.sol"; import "../verifiers/IVerifier.sol"; import "../TaikoData.sol"; @@ -25,6 +26,8 @@ import "./LibUtils.sol"; /// @notice A library for handling block contestation and proving in the Taiko /// protocol. library LibProving { + using LibMath for uint256; + bytes32 public constant RETURN_LIVENESS_BOND = keccak256("RETURN_LIVENESS_BOND"); // Warning: Any events defined here must also be defined in TaikoEvents.sol. @@ -58,11 +61,15 @@ library LibProving { error L1_NOT_ASSIGNED_PROVER(); error L1_UNEXPECTED_TRANSITION_TIER(); - function pauseProving(TaikoData.State storage state, bool pause) external { - if (state.slotB.provingPaused == pause) revert L1_INVALID_PAUSE_STATUS(); + function pauseProving(TaikoData.State storage state, bool toPause) external { + if (state.slotB.provingPaused == toPause) revert L1_INVALID_PAUSE_STATUS(); + + state.slotB.provingPaused = toPause; - state.slotB.provingPaused = pause; - emit ProvingPaused(pause); + if (!toPause) { + state.slotB.lastUnpausedAt = uint64(block.timestamp); + } + emit ProvingPaused(toPause); } /// @dev Proves or contests a block transition. @@ -335,7 +342,11 @@ library LibProving { revert L1_ALREADY_PROVED(); } - if (tid == 1 && ts.tier == 0 && block.timestamp <= ts.timestamp + tier.provingWindow) { + if ( + tid == 1 && ts.tier == 0 + && block.timestamp + <= uint256(ts.timestamp).max(state.slotB.lastUnpausedAt) + tier.provingWindow + ) { // For the first transition, (1) if the previous prover is // still the assigned prover, we exclusively grant permission to // the assigned approver to re-prove the block, (2) unless the diff --git a/packages/protocol/contracts/L1/libs/LibProvingAlt.sol b/packages/protocol/contracts/L1/libs/LibProvingAlt.sol index 7d9bd2623a9..600d4303248 100644 --- a/packages/protocol/contracts/L1/libs/LibProvingAlt.sol +++ b/packages/protocol/contracts/L1/libs/LibProvingAlt.sol @@ -16,6 +16,7 @@ pragma solidity 0.8.20; import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "../../common/AddressResolver.sol"; +import "../../libs/LibMath.sol"; import "../tiers/ITierProvider.sol"; import "../verifiers/IVerifier.sol"; import "../TaikoData.sol"; @@ -25,6 +26,8 @@ import "./LibUtils.sol"; /// @notice An alternative library for handling block contestation and proving in the Taiko /// protocol. library LibProvingAlt { + using LibMath for uint256; + bytes32 public constant RETURN_LIVENESS_BOND = keccak256("RETURN_LIVENESS_BOND"); // Warning: Any events defined here must also be defined in TaikoEvents.sol. @@ -118,7 +121,7 @@ library LibProvingAlt { ITierProvider.Tier memory tier = ITierProvider(resolver.resolve("tier_provider", false)).getTier(proof.tier); - _checkProverPermission(blk, ts, tid, tier); + _checkProverPermission(state, blk, ts, tid, tier); // We must verify the proof, and any failure in proof verification will // result in a revert. @@ -373,6 +376,7 @@ library LibProvingAlt { /// @dev Check the msg.sender (the new prover) against the block's assigned prover. function _checkProverPermission( + TaikoData.State storage state, TaikoData.Block storage blk, TaikoData.TransitionState storage ts, uint32 tid, @@ -384,7 +388,8 @@ library LibProvingAlt { // The highest tier proof can always submit new proofs if (tier.contestBond == 0) return; - bool inProvingWindow = block.timestamp <= ts.timestamp + tier.provingWindow; + bool inProvingWindow = uint256(ts.timestamp).max(state.slotB.lastUnpausedAt) + + tier.provingWindow >= block.timestamp; bool isAssignedPover = msg.sender == blk.assignedProver; // The assigned prover can only submit the very first transition. diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 1e41e8ec743..fb8a349f7b1 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -16,6 +16,7 @@ pragma solidity 0.8.20; import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "../../common/AddressResolver.sol"; +import "../../libs/LibMath.sol"; import "../../signal/ISignalService.sol"; import "../tiers/ITierProvider.sol"; import "../TaikoData.sol"; @@ -24,6 +25,8 @@ import "./LibUtils.sol"; /// @title LibVerifying /// @notice A library for handling block verification in the Taiko protocol. library LibVerifying { + using LibMath for uint256; + // Warning: Any events defined here must also be defined in TaikoEvents.sol. event BlockVerified( uint256 indexed blockId, @@ -166,7 +169,7 @@ library LibVerifying { } if ( uint256(ITierProvider(tierProvider).getTier(ts.tier).cooldownWindow) - + ts.timestamp > block.timestamp + + uint256(ts.timestamp).max(state.slotB.lastUnpausedAt) > block.timestamp ) { // If cooldownWindow is 0, the block can theoretically // be proved and verified within the same L1 block. diff --git a/packages/protocol/contracts/common/OwnerUUPSUpgradable.sol b/packages/protocol/contracts/common/OwnerUUPSUpgradable.sol index c67d4e3b8d2..a3d92b288c0 100644 --- a/packages/protocol/contracts/common/OwnerUUPSUpgradable.sol +++ b/packages/protocol/contracts/common/OwnerUUPSUpgradable.sol @@ -57,12 +57,14 @@ abstract contract OwnerUUPSUpgradable is UUPSUpgradeable, OwnableUpgradeable { _disableInitializers(); } - function pause() external whenNotPaused onlyOwner { + function pause() public virtual whenNotPaused { + _authorizePause(msg.sender); _paused = _TRUE; emit Paused(msg.sender); } - function unpause() external whenPaused onlyOwner { + function unpause() public virtual whenPaused { + _authorizePause(msg.sender); _paused = _FALSE; emit Unpaused(msg.sender); } @@ -71,7 +73,8 @@ abstract contract OwnerUUPSUpgradable is UUPSUpgradeable, OwnableUpgradeable { return _paused == _TRUE; } - function _authorizeUpgrade(address) internal override onlyOwner { } + function _authorizeUpgrade(address) internal virtual override onlyOwner { } + function _authorizePause(address) internal virtual onlyOwner { } /// @notice Initializes the contract with an address manager. // solhint-disable-next-line func-name-mixedcase