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

Reseal manager reseals only one resealable #77

Merged
merged 8 commits into from
Aug 7, 2024
Merged
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
9 changes: 7 additions & 2 deletions contracts/DualGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,19 @@ contract DualGovernance is IDualGovernance {
// Reseal executor
// ---

function resealSealables(address[] memory sealables) external {
function resealSealable(address sealable) external {
if (msg.sender != _resealCommittee) {
revert NotResealCommittee(msg.sender);
}
if (_stateMachine.getCurrentState() == State.Normal) {
revert ResealIsNotAllowedInNormalState();
}
RESEAL_MANAGER.reseal(sealables);
RESEAL_MANAGER.reseal(sealable);
}

function setResealCommittee(address resealCommittee) external {
_checkSenderIsAdminExecutor();
_resealCommittee = resealCommittee;
}

// ---
Expand Down
37 changes: 28 additions & 9 deletions contracts/ResealManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import {ITimelock} from "./interfaces/ITimelock.sol";
import {IResealManager} from "./interfaces/IResealManager.sol";

/// @title ResealManager
/// @dev Allows to extend pause of temporarily paused contracts to permanent pause or resume it.
contract ResealManager is IResealManager {
error SealableWrongPauseState();
error SenderIsNotGovernance();
Expand All @@ -15,31 +17,48 @@
uint256 public constant PAUSE_INFINITELY = type(uint256).max;
ITimelock public immutable EMERGENCY_PROTECTED_TIMELOCK;

/// @notice Initializes the ResealManager contract.
/// @param emergencyProtectedTimelock The address of the EmergencyProtectedTimelock contract.
constructor(ITimelock emergencyProtectedTimelock) {
EMERGENCY_PROTECTED_TIMELOCK = emergencyProtectedTimelock;
EMERGENCY_PROTECTED_TIMELOCK = ITimelock(emergencyProtectedTimelock);
}

function reseal(address[] memory sealables) public {
/// @notice Extends the pause of the specified sealable contract.
/// @dev Works only if conditions are met:
/// - ResealManager has PAUSE_ROLE for target contract;
/// - Contract are paused until timestamp after current timestamp and not for infinite time;
/// - The DAO governance is blocked by DualGovernance;
/// - Function is called by the governance contract.
/// @param sealable The address of the sealable contract.
function reseal(address sealable) public {
_checkSenderIsGovernance();
for (uint256 i = 0; i < sealables.length; ++i) {
uint256 sealableResumeSinceTimestamp = ISealable(sealables[i]).getResumeSinceTimestamp();
if (sealableResumeSinceTimestamp < block.timestamp || sealableResumeSinceTimestamp == PAUSE_INFINITELY) {
revert SealableWrongPauseState();
}
Address.functionCall(sealables[i], abi.encodeWithSelector(ISealable.resume.selector));
Address.functionCall(sealables[i], abi.encodeWithSelector(ISealable.pauseFor.selector, PAUSE_INFINITELY));

uint256 sealableResumeSinceTimestamp = ISealable(sealable).getResumeSinceTimestamp();
if (sealableResumeSinceTimestamp < block.timestamp || sealableResumeSinceTimestamp == PAUSE_INFINITELY) {
revert SealableWrongPauseState();
}
Address.functionCall(sealable, abi.encodeWithSelector(ISealable.resume.selector));
Address.functionCall(sealable, abi.encodeWithSelector(ISealable.pauseFor.selector, PAUSE_INFINITELY));
}

Check warning

Code scanning / Slither

Unused return Medium

Check warning

Code scanning / Slither

Unused return Medium


/// @notice Resumes the specified sealable contract if it is scheduled to resume in the future.
/// @dev Works only if conditions are met:
/// - ResealManager has RESUME_ROLE for target contract;
/// - Contract are paused until timestamp after current timestamp;
/// - Function is called by the governance contract.
/// @param sealable The address of the sealable contract.
function resume(address sealable) public {
_checkSenderIsGovernance();

uint256 sealableResumeSinceTimestamp = ISealable(sealable).getResumeSinceTimestamp();
if (sealableResumeSinceTimestamp < block.timestamp) {
revert SealableWrongPauseState();
}
Address.functionCall(sealable, abi.encodeWithSelector(ISealable.resume.selector));
}

/// @notice Ensures that the function can only be called by the governance address.
/// @dev Reverts if the sender is not the governance address.
function _checkSenderIsGovernance() internal view {
address governance = EMERGENCY_PROTECTED_TIMELOCK.getGovernance();
if (msg.sender != governance) {
Expand Down
14 changes: 6 additions & 8 deletions contracts/committees/EmergencyActivationCommittee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
pragma solidity 0.8.26;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {HashConsensus} from "./HashConsensus.sol";

interface IEmergencyProtectedTimelock {
function emergencyActivate() external;
}
import {HashConsensus} from "./HashConsensus.sol";
import {ITimelock} from "../interfaces/ITimelock.sol";

/// @title Emergency Activation Committee Contract
/// @notice This contract allows a committee to approve and execute an emergency activation
Expand All @@ -27,7 +25,7 @@

/// @notice Approves the emergency activation by casting a vote
/// @dev Only callable by committee members
function approveEmergencyActivate() public {
function approveActivateEmergencyMode() public {
_checkSenderIsMember();
_vote(EMERGENCY_ACTIVATION_HASH, true);
}
Expand All @@ -36,7 +34,7 @@
/// @return support The number of votes in support of the activation
/// @return execuitionQuorum The required number of votes for execution
/// @return isExecuted Whether the activation has been executed
function getEmergencyActivateState()
function getActivateEmergencyModeState()
public
view
returns (uint256 support, uint256 execuitionQuorum, bool isExecuted)
Expand All @@ -46,10 +44,10 @@

/// @notice Executes the emergency activation if the quorum is reached
/// @dev Calls the emergencyActivate function on the Emergency Protected Timelock contract
function executeEmergencyActivate() external {
function executeActivateEmergencyMode() external {
_markUsed(EMERGENCY_ACTIVATION_HASH);
Address.functionCall(
EMERGENCY_PROTECTED_TIMELOCK, abi.encodeWithSelector(IEmergencyProtectedTimelock.emergencyActivate.selector)
EMERGENCY_PROTECTED_TIMELOCK, abi.encodeWithSelector(ITimelock.activateEmergencyMode.selector)
);
}
}
13 changes: 3 additions & 10 deletions contracts/committees/EmergencyExecutionCommittee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ pragma solidity 0.8.26;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {HashConsensus} from "./HashConsensus.sol";
import {ProposalsList} from "./ProposalsList.sol";

interface IEmergencyProtectedTimelock {
function emergencyExecute(uint256 proposalId) external;
function emergencyReset() external;
}
import {ITimelock} from "../interfaces/ITimelock.sol";

enum ProposalType {
EmergencyExecute,
Expand Down Expand Up @@ -65,8 +61,7 @@ contract EmergencyExecutionCommittee is HashConsensus, ProposalsList {
(, bytes32 key) = _encodeEmergencyExecute(proposalId);
_markUsed(key);
Address.functionCall(
EMERGENCY_PROTECTED_TIMELOCK,
abi.encodeWithSelector(IEmergencyProtectedTimelock.emergencyExecute.selector, proposalId)
EMERGENCY_PROTECTED_TIMELOCK, abi.encodeWithSelector(ITimelock.emergencyExecute.selector, proposalId)
);
}

Expand Down Expand Up @@ -113,9 +108,7 @@ contract EmergencyExecutionCommittee is HashConsensus, ProposalsList {
function executeEmergencyReset() external {
bytes32 proposalKey = _encodeEmergencyResetProposalKey();
_markUsed(proposalKey);
Address.functionCall(
EMERGENCY_PROTECTED_TIMELOCK, abi.encodeWithSelector(IEmergencyProtectedTimelock.emergencyReset.selector)
);
Address.functionCall(EMERGENCY_PROTECTED_TIMELOCK, abi.encodeWithSelector(ITimelock.emergencyReset.selector));
}

/// @notice Encodes the proposal key for an emergency reset
Expand Down
36 changes: 18 additions & 18 deletions contracts/committees/ResealCommittee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import {ProposalsList} from "./ProposalsList.sol";

interface IDualGovernance {
function reseal(address[] memory sealables) external;
function resealSealable(address sealables) external;
}

/// @title Reseal Committee Contract
Expand All @@ -28,52 +28,52 @@
}

/// @notice Votes on a reseal proposal
/// @dev Allows committee members to vote on resealing a set of addresses
/// @param sealables The addresses to reseal
/// @dev Allows committee members to vote on resealing a sealed address
/// @param sealable The address to reseal
/// @param support Indicates whether the member supports the proposal
function voteReseal(address[] memory sealables, bool support) public {
function voteReseal(address sealable, bool support) public {
_checkSenderIsMember();
(bytes memory proposalData, bytes32 key) = _encodeResealProposal(sealables);
(bytes memory proposalData, bytes32 key) = _encodeResealProposal(sealable);
_vote(key, support);
_pushProposal(key, 0, proposalData);
}

/// @notice Gets the current state of a reseal proposal
/// @dev Retrieves the state of the reseal proposal for a set of addresses
/// @param sealables The addresses for the reseal proposal
/// @dev Retrieves the state of the reseal proposal for a sealed address
/// @param sealable The addresses for the reseal proposal
/// @return support The number of votes in support of the proposal
/// @return execuitionQuorum The required number of votes for execution
/// @return isExecuted Whether the proposal has been executed
function getResealState(address[] memory sealables)
function getResealState(address sealable)
public
view
returns (uint256 support, uint256 execuitionQuorum, bool isExecuted)
{
(, bytes32 key) = _encodeResealProposal(sealables);
(, bytes32 key) = _encodeResealProposal(sealable);
return _getHashState(key);
}

/// @notice Executes an approved reseal proposal
/// @dev Executes the reseal proposal by calling the reseal function on the Dual Governance contract
/// @param sealables The addresses to reseal
function executeReseal(address[] memory sealables) external {
(, bytes32 key) = _encodeResealProposal(sealables);
/// @param sealable The address to reseal
function executeReseal(address sealable) external {
(, bytes32 key) = _encodeResealProposal(sealable);
_markUsed(key);

Address.functionCall(DUAL_GOVERNANCE, abi.encodeWithSelector(IDualGovernance.reseal.selector, sealables));
Address.functionCall(DUAL_GOVERNANCE, abi.encodeWithSelector(IDualGovernance.resealSealable.selector, sealable));

bytes32 resealNonceHash = keccak256(abi.encode(sealables));
bytes32 resealNonceHash = keccak256(abi.encode(sealable));
_resealNonces[resealNonceHash]++;
}

Check warning

Code scanning / Slither

Reentrancy vulnerabilities Medium


/// @notice Encodes a reseal proposal
/// @dev Internal function to encode the proposal data and generate the proposal key
/// @param sealables The addresses to reseal
/// @param sealable The address to reseal
/// @return data The encoded proposal data
/// @return key The generated proposal key
function _encodeResealProposal(address[] memory sealables) internal view returns (bytes memory data, bytes32 key) {
bytes32 resealNonceHash = keccak256(abi.encode(sealables));
data = abi.encode(sealables, _resealNonces[resealNonceHash]);
function _encodeResealProposal(address sealable) internal view returns (bytes memory data, bytes32 key) {
bytes32 resealNonceHash = keccak256(abi.encode(sealable));
data = abi.encode(sealable, _resealNonces[resealNonceHash]);
key = keccak256(data);
}
}
2 changes: 1 addition & 1 deletion contracts/interfaces/IResealManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ pragma solidity 0.8.26;

interface IResealManager {
function resume(address sealable) external;
function reseal(address[] memory sealables) external;
function reseal(address sealable) external;
}
8 changes: 7 additions & 1 deletion contracts/interfaces/ITimelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@ interface ITimelock {
function canSchedule(uint256 proposalId) external view returns (bool);
function canExecute(uint256 proposalId) external view returns (bool);

function getGovernance() external view returns (address);
function getAdminExecutor() external view returns (address);

function getProposal(uint256 proposalId) external view returns (Proposal memory proposal);
function getProposalInfo(uint256 proposalId)
external
view
returns (uint256 id, ProposalStatus status, address executor, Timestamp submittedAt, Timestamp scheduledAt);

function getGovernance() external view returns (address);
function setGovernance(address governance) external;

function activateEmergencyMode() external;
function emergencyExecute(uint256 proposalId) external;
function emergencyReset() external;
}
12 changes: 6 additions & 6 deletions docs/plan-b.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,27 +333,27 @@ Initializes the contract with an owner, committee members, a quorum, and the add
#### Preconditions
- `executionQuorum` MUST be greater than 0.

### Function: `EmergencyActivationCommittee.approveEmergencyActivate`
### Function: `EmergencyActivationCommittee.approveActivateEmergencyMode`
```solidity
function approveEmergencyActivate() public
function approveActivateEmergencyMode() public onlyMember
```
Approves the emergency activation by voting on the `EMERGENCY_ACTIVATION_HASH`.

#### Preconditions
- MUST be called by a committee member.

### Function: `EmergencyActivationCommittee.getEmergencyActivateState`
### Function: `EmergencyActivationCommittee.getActivateEmergencyModeState`
```solidity
function getEmergencyActivateState()
function getActivateEmergencyModeState()
public
view
returns (uint256 support, uint256 executionQuorum, bool isExecuted)
```
Returns the state of the emergency activation proposal, including the support count, quorum, and execution status.

### Function: `EmergencyActivationCommittee.executeEmergencyActivate`
### Function: `EmergencyActivationCommittee.executeActivateEmergencyMode`
```solidity
function executeEmergencyActivate() external
function executeActivateEmergencyMode() external
```
Executes the emergency activation by calling the `emergencyActivate` function on the `EmergencyProtectedTimelock` contract.

Expand Down
20 changes: 10 additions & 10 deletions docs/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,15 +417,15 @@ Initializes the contract with the address of the EmergencyProtectedTimelock cont
### Function ResealManager.reseal

```solidity
function reseal(address[] memory sealables) public
function reseal(address sealable) public
```

Extends the pause of the specified `sealables` contracts. This function can be called by the governance address defined in the `EmergencyProtectedTimelock`.
Extends the pause of the specified `sealable` contract. This function can be called by the governance address defined in the `EmergencyProtectedTimelock`.

#### Preconditions

- The `ResealManager` MUST have `PAUSE_ROLE` and `RESUME_ROLE` for the target contracts.
- The target contracts MUST be paused until a future timestamp and not indefinitely.
- The `ResealManager` MUST have `PAUSE_ROLE` and `RESUME_ROLE` for the target contract.
- The target contract MUST be paused until a future timestamp and not indefinitely.
- The function MUST be called by the governance address defined in `EmergencyProtectedTimelock`.

#### Errors
Expand Down Expand Up @@ -1318,10 +1318,10 @@ executionQuorum MUST be greater than 0.
emergencyProtectedTimelock MUST be a valid address.


### Function: EmergencyActivationCommittee.approveEmergencyActivate
### Function: EmergencyActivationCommittee.approveActivateEmergencyMode

```solidity
function approveEmergencyActivate() public
function approveActivateEmergencyMode() public
```

Approves the emergency activation by voting on the EMERGENCY_ACTIVATION_HASH.
Expand All @@ -1330,21 +1330,21 @@ Approves the emergency activation by voting on the EMERGENCY_ACTIVATION_HASH.

* MUST be called by a member.

### Function: EmergencyActivationCommittee.getEmergencyActivateState
### Function: EmergencyActivationCommittee.getActivateEmergencyModeState

```solidity
function getEmergencyActivateState()
function getActivateEmergencyModeState()
public
view
returns (uint256 support, uint256 executionQuorum, bool isExecuted)
```

Returns the state of the emergency activation proposal including support count, quorum, and execution status.

### Function: EmergencyActivationCommittee.executeEmergencyActivate
### Function: EmergencyActivationCommittee.executeActivateEmergencyMode

```solidity
function executeEmergencyActivate() external
function executeActivateEmergencyMode() external
```

Executes the emergency activation by calling the emergencyActivate function on the EmergencyProtectedTimelock contract.
Expand Down
Loading