Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Demo code changes #718

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 83 additions & 11 deletions contracts/src/security/pausing/PauseManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,33 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
/// @notice This is used to unpause all unpausable functions.
bytes32 public constant UNPAUSE_ALL_ROLE = keccak256("UNPAUSE_ALL_ROLE");

// @dev DEPRECATED. USE _pauseTypeStatusesBitMap INSTEAD
/// @notice This is used to unpause all unpausable functions.
bytes32 public constant SECURITY_COUNCIL_ROLE = keccak256("SECURITY_COUNCIL_ROLE");

/// @notice Duration of cooldown period after a pause expires, in which no further
/// @dev SECURITY_COUNCIL_ROLE role will ignore cooldown
uint256 public constant COOLDOWN_DURATION = 24 hours;

/// @notice Duration of pauses
/// @dev All pauses have a limited duration, except for pauses enacted by the SECURITY_COUNCIL_ROLE
uint256 public constant PAUSE_DURATION = 72 hours;

/// @dev custom:storage-location erc7201:linea.storage.PauseManager
struct PauseManagerStorage {
uint256 _pauseExpiry;
}

/// @notice ERC-7201 Namespaced storage slot
/// @dev keccak256(abi.encode(uint256(keccak256("linea.storage.PauseManager")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PauseManagerStorageLocation = 0x477c83544ba7ee7a035c74dbc9991f7ec4e921343919039e273014d568e17a00;

function _getPauseManagerStorage() private pure returns (PauseManagerStorage storage $) {
assembly {
$.slot := PauseManagerStorageLocation
}
}

/// @dev DEPRECATED. USE _pauseTypeStatusesBitMap INSTEAD
mapping(bytes32 pauseType => bool pauseStatus) public pauseTypeStatuses;

/// @dev The bitmap containing the pause statuses mapped by type.
Expand All @@ -28,10 +54,9 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
/// @dev This maps the unpause type to the role that is allowed to unpause it.
mapping(PauseType unPauseType => bytes32 role) private _unPauseTypeRoles;

/// @dev Total contract storage is 11 slots with the gap below.
/// @dev Keep 7 free storage slots for future implementation updates to avoid storage collision.
/// @dev Note: This was reduced previously to cater for new functionality.
uint256[7] private __gap;
/// @dev Do not add any new storage variables to the contract. Instead add new variables to the PauseManagerStorage struct.
/// @dev __gap is deprecated in favour of the ERC-7201 Namespaced Storage pattern
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// @dev __gap is deprecated in favour of the ERC-7201 Namespaced Storage pattern
/// @dev __gap is deprecated in favor of the ERC-7201 Namespaced Storage pattern and remains for layout structure.

uint256[7] private __gap_DEPRECATED;

/**
* @dev Modifier to prevent usage of unused PauseType.
Expand Down Expand Up @@ -133,27 +158,65 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
revert IsPaused(_pauseType);
}

PauseManagerStorage storage $ = _getPauseManagerStorage();
// TO-DISCUSS - Introduces duplicate SLOAD, but the alternative is to create a dedicated function for the security council. Perhaps is ok for an emergency function.
bool isSecurityCouncil = hasRole(SECURITY_COUNCIL_ROLE, _msgSender());
if (isSecurityCouncil) {
Comment on lines +163 to +164
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool isSecurityCouncil = hasRole(SECURITY_COUNCIL_ROLE, _msgSender());
if (isSecurityCouncil) {
if (hasRole(SECURITY_COUNCIL_ROLE, _msgSender())) {

$._pauseExpiry = type(uint256).max;
} else {
uint256 cooldownEnd = $._pauseExpiry + COOLDOWN_DURATION;
if (block.timestamp < cooldownEnd) {
revert PauseUnavailableDueToCooldown(cooldownEnd);
}
$._pauseExpiry = block.timestamp + PAUSE_DURATION;
}

_pauseTypeStatusesBitMap |= 1 << uint256(_pauseType);
emit Paused(_msgSender(), _pauseType);
}

/**
* @notice Unpauses functionality by specific type.
* @dev Throws if UNUSED pause type is used.
* @dev Throws if UNUSED pause type is used, or is not called by SECURITY_COUNCIL_ROLE
* @dev Requires the role mapped in `_unPauseTypeRoles` for the pauseType.
* @param _pauseType The pause type value.
*/
function unPauseByType(
PauseType _pauseType
) external onlyUsedPausedTypes(_pauseType) onlyRole(_unPauseTypeRoles[_pauseType]) {
) external onlyUsedPausedTypes(_pauseType) onlyRole(SECURITY_COUNCIL_ROLE) {
if (!isPaused(_pauseType)) {
revert IsNotPaused(_pauseType);
}

PauseManagerStorage storage $ = _getPauseManagerStorage();
$._pauseExpiry = block.timestamp - COOLDOWN_DURATION;
_pauseTypeStatusesBitMap &= ~(1 << uint256(_pauseType));
emit UnPaused(_msgSender(), _pauseType);
}

/**
* @notice Unpauses functionality by specific type when pause period has expired.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @notice Unpauses functionality by specific type when pause period has expired.
* @notice Unpauses functionality by pause type when pause period has expired.

* @dev Throws if UNUSED pause type is used, or the pause expiry period has not passed.
* @dev Requires the role mapped in `_unPauseTypeRoles` for the pauseType.
* @param _pauseType The pause type value.
*/
function unPauseDueToExpiry(
PauseType _pauseType
) external onlyUsedPausedTypes(_pauseType) {
if (!isPaused(_pauseType)) {
revert IsNotPaused(_pauseType);
}

PauseManagerStorage storage $ = _getPauseManagerStorage();
uint256 pauseExpiry = $._pauseExpiry;
if (block.timestamp < pauseExpiry) {
revert PauseNotExpired(pauseExpiry);
}

_pauseTypeStatusesBitMap &= ~(1 << uint256(_pauseType));
emit UnPausedDueToExpiry(_pauseType);
}

/**
* @notice Check if a pause type is enabled.
* @param _pauseType The pause type value.
Expand All @@ -167,14 +230,14 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
* @notice Update the pause type role mapping.
* @dev Throws if UNUSED pause type is used.
* @dev Throws if role not different.
* @dev PAUSE_ALL_ROLE role is required to execute this function.
* @dev SECURITY_COUNCIL_ROLE role is required to execute this function.
* @param _pauseType The pause type value to update.
* @param _newRole The role to update to.
*/
function updatePauseTypeRole(
PauseType _pauseType,
bytes32 _newRole
) external onlyUsedPausedTypes(_pauseType) onlyRole(PAUSE_ALL_ROLE) {
) external onlyUsedPausedTypes(_pauseType) onlyRole(SECURITY_COUNCIL_ROLE) {
bytes32 previousRole = _pauseTypeRoles[_pauseType];
if (previousRole == _newRole) {
revert RolesNotDifferent();
Expand All @@ -188,14 +251,14 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
* @notice Update the unpause type role mapping.
* @dev Throws if UNUSED pause type is used.
* @dev Throws if role not different.
* @dev UNPAUSE_ALL_ROLE role is required to execute this function.
* @dev SECURITY_COUNCIL_ROLE role is required to execute this function.
* @param _pauseType The pause type value to update.
* @param _newRole The role to update to.
*/
function updateUnpauseTypeRole(
PauseType _pauseType,
bytes32 _newRole
) external onlyUsedPausedTypes(_pauseType) onlyRole(UNPAUSE_ALL_ROLE) {
) external onlyUsedPausedTypes(_pauseType) onlyRole(SECURITY_COUNCIL_ROLE) {
bytes32 previousRole = _unPauseTypeRoles[_pauseType];
if (previousRole == _newRole) {
revert RolesNotDifferent();
Expand All @@ -204,4 +267,13 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
_unPauseTypeRoles[_pauseType] = _newRole;
emit UnPauseTypeRoleUpdated(_pauseType, _newRole, previousRole);
}

/**
* @notice Returns the Unix timestamp for the pause expiry.
* @return pauseExpiry Unix timestamp for the pause expiry.
*/
function getPauseExpiry() external view returns (uint256 pauseExpiry) {
PauseManagerStorage storage $ = _getPauseManagerStorage();
return $._pauseExpiry;
}
}
30 changes: 30 additions & 0 deletions contracts/src/security/pausing/interfaces/IPauseManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ interface IPauseManager {
*/
event UnPaused(address messageSender, PauseType indexed pauseType);

/**
* @notice Emitted when a pause type is unpaused due to pause expiry period elapsing.
* @param pauseType The indexed pause type that was unpaused.
*/
event UnPausedDueToExpiry(PauseType pauseType);

/**
* @notice Emitted when a pause type and its associated role are set in the `_pauseTypeRoles` mapping.
* @param pauseType The indexed type of pause.
Expand Down Expand Up @@ -85,11 +91,21 @@ interface IPauseManager {
*/
error IsPaused(PauseType pauseType);

/**
* @dev Thrown when unpauseDueToExpiry is attempted before a pause has expired.
*/
error PauseNotExpired(uint256 expiryEnd);

/**
* @dev Thrown when a specific pause type is not paused and expected to be.
*/
error IsNotPaused(PauseType pauseType);

/**
* @dev Thrown when pausing is attempted during the cooldown period.
*/
error PauseUnavailableDueToCooldown(uint256 cooldownEnd);

/**
* @dev Thrown when the unused paused type is used.
*/
Expand All @@ -116,6 +132,14 @@ interface IPauseManager {
*/
function unPauseByType(PauseType _pauseType) external;

/**
* @notice Unpauses functionality by specific type when pause period has expired.
* @dev Throws if UNUSED pause type is used, or the pause expiry period has not passed.
* @dev Requires the role mapped in unPauseTypeRoles for the pauseType.
* @param _pauseType The pause type value.
*/
function unPauseDueToExpiry(PauseType _pauseType) external;

/**
* @notice Check if a pause type is enabled.
* @param _pauseType The pause type value.
Expand All @@ -142,4 +166,10 @@ interface IPauseManager {
* @param _newRole The role to update to.
*/
function updateUnpauseTypeRole(PauseType _pauseType, bytes32 _newRole) external;

/**
* @notice Returns the Unix timestamp for the pause expiry.
* @return pauseExpiry Unix timestamp for the pause expiry.
*/
function getPauseExpiry() external view returns (uint256 pauseExpiry);
}
1 change: 1 addition & 0 deletions contracts/test/hardhat/common/constants/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const VERIFIER_SETTER_ROLE = generateKeccak256(["string"], ["VERIFIER_SET
export const VERIFIER_UNSETTER_ROLE = generateKeccak256(["string"], ["VERIFIER_UNSETTER_ROLE"], true);
export const L1_MERKLE_ROOTS_SETTER_ROLE = generateKeccak256(["string"], ["L1_MERKLE_ROOTS_SETTER_ROLE"], true);
export const L2_MERKLE_ROOTS_SETTER_ROLE = generateKeccak256(["string"], ["L2_MERKLE_ROOTS_SETTER_ROLE"], true);
export const SECURITY_COUNCIL_ROLE = generateKeccak256(["string"], ["SECURITY_COUNCIL_ROLE"], true);
export const BAD_STARTING_HASH = generateKeccak256(["string"], ["BAD_STARTING_HASH"], true);

// TokenBridge roles
Expand Down
Loading