Skip to content

Commit

Permalink
Merge pull request #644 from bosonprotocol/unpause_regions
Browse files Browse the repository at this point in the history
Unpause individual regions
  • Loading branch information
mischat committed Jul 6, 2023
2 parents f14d952 + 0fcf7c0 commit ad78b61
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 163 deletions.
62 changes: 31 additions & 31 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -194,34 +194,34 @@ jobs:
if: failure()
uses: andymckay/cancel-action@0.2

analyze:
needs: setup
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v3.1.0
- name: Setup node
uses: actions/setup-node@v3.5.1
with:
node-version: 16.14.x
cache: "npm"
- name: Install Dependencies
run: npm install
- name: Prepare Environment
shell: bash
run: |
cp .env.example .env
- name: Slither analyzer
uses: crytic/slither-action@v0.3.0
id: slither
with:
node-version: 16
sarif: results.sarif
fail-on: none
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: ${{ steps.slither.outputs.sarif }}
# analyze:
# needs: setup
# runs-on: ubuntu-latest
# permissions:
# contents: read
# security-events: write
# steps:
# - name: Checkout repository
# uses: actions/checkout@v3.1.0
# - name: Setup node
# uses: actions/setup-node@v3.5.1
# with:
# node-version: 16.14.x
# cache: "npm"
# - name: Install Dependencies
# run: npm install
# - name: Prepare Environment
# shell: bash
# run: |
# cp .env.example .env
# - name: Slither analyzer
# uses: crytic/slither-action@v0.3.0
# id: slither
# with:
# node-version: 16
# sarif: results.sarif
# fail-on: none
# - name: Upload SARIF file
# uses: github/codeql-action/upload-sarif@v2
# with:
# sarif_file: ${{ steps.slither.outputs.sarif }}
7 changes: 4 additions & 3 deletions contracts/domain/BosonConstants.sol
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import "./BosonTypes.sol";

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

Expand All @@ -10,12 +12,11 @@ bytes32 constant UPGRADER = keccak256("UPGRADER"); // Role for performing contra
bytes32 constant FEE_COLLECTOR = keccak256("FEE_COLLECTOR"); // Role for collecting fees from the protocol

// Revert Reasons: Pause related
string constant NO_REGIONS_SPECIFIED = "Must specify at least one region to pause";
string constant REGION_DUPLICATED = "A region may only be specified once";
string constant ALREADY_PAUSED = "Protocol is already paused";
string constant NOT_PAUSED = "Protocol is not currently paused";
string constant REGION_PAUSED = "This region of the protocol is currently paused";

uint256 constant ALL_REGIONS_MASK = (1 << (uint256(type(BosonTypes.PausableRegion).max) + 1)) - 1;

// Revert Reasons: General
string constant INVALID_ADDRESS = "Invalid address";
string constant INVALID_STATE = "Invalid state";
Expand Down
4 changes: 3 additions & 1 deletion contracts/interfaces/events/IBosonPauseEvents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { BosonTypes } from "../../domain/BosonTypes.sol";
* @notice Defines events related to pausing of the protocol.
*/
interface IBosonPauseEvents {
// When array of regions is empty, all regions are paused
event ProtocolPaused(BosonTypes.PausableRegion[] regions, address executedBy);
event ProtocolUnpaused(address executedBy);
// When array of regions is empty, all regions are unpaused
event ProtocolUnpaused(BosonTypes.PausableRegion[] regions, address executedBy);
}
16 changes: 12 additions & 4 deletions contracts/interfaces/handlers/IBosonPauseHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { IBosonPauseEvents } from "../events/IBosonPauseEvents.sol";
*
* @notice Handles pausing all or part of the protocol.
*
* The ERC-165 identifier for this interface is: 0x9ddb8ca6
* The ERC-165 identifier for this interface is: 0x770b96d0
*/
interface IBosonPauseHandler is IBosonPauseEvents {
/**
Expand All @@ -19,8 +19,6 @@ interface IBosonPauseHandler is IBosonPauseEvents {
*
* Reverts if:
* - Caller does not have PAUSER role
* - No regions are specified
* - Protocol is already paused
* - A region is specified more than once
*
* @param _regions - an array of regions to pause. See: {BosonTypes.PausableRegion}
Expand All @@ -35,6 +33,16 @@ interface IBosonPauseHandler is IBosonPauseEvents {
* Reverts if:
* - Caller does not have PAUSER role
* - Protocol is not currently paused
* - A region is specified more than once
*
* @param _regions - an array of regions to pause. See: {BosonTypes.PausableRegion}
*/
function unpause(BosonTypes.PausableRegion[] calldata _regions) external;

/**
* @notice Returns the regions paused
*
* @return regions - an array of regions that are currently paused. See: {BosonTypes.PausableRegion}
*/
function unpause() external;
function getPausedRegions() external view returns (BosonTypes.PausableRegion[] memory regions);
}
126 changes: 89 additions & 37 deletions contracts/protocol/facets/PauseHandlerFacet.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

import "../../domain/BosonConstants.sol";
import { DiamondLib } from "../../diamond/DiamondLib.sol";
import "../../domain/BosonConstants.sol";
import { BosonTypes } from "../../domain/BosonTypes.sol";
import { ProtocolBase } from "../bases/OfferBase.sol";
import { ProtocolLib } from "../libs/ProtocolLib.sol";
Expand All @@ -28,43 +30,12 @@ contract PauseHandlerFacet is ProtocolBase, IBosonPauseHandler {
*
* Reverts if:
* - Caller does not have PAUSER role
* - No regions are specified
* - Protocol is already paused
* - A region is specified more than once
*
* @param _regions - an array of regions to pause. See: {BosonTypes.PausableRegion}
*/
function pause(BosonTypes.PausableRegion[] calldata _regions) external onlyRole(PAUSER) nonReentrant {
// Cache protocol status for reference
ProtocolLib.ProtocolStatus storage status = protocolStatus();

// Make sure at least one region is specified
require(_regions.length > 0, NO_REGIONS_SPECIFIED);

// Make sure the protocol isn't already paused
require(status.pauseScenario == 0, ALREADY_PAUSED);

// Build the pause scenario by summing the supplied
// enum values, first converted to powers of two
uint8 enumVal;
uint256 region;
uint256 scenario;
uint256[] memory used = new uint256[](20); // arbitrarily a little more than # of regions
for (uint256 i = 0; i < _regions.length; i++) {
// Get enum value as power of 2
enumVal = uint8(_regions[i]);
region = 2 ** uint256(enumVal);

// Prevent duplicates
require(used[enumVal] != region, REGION_DUPLICATED);
used[enumVal] = region;

// Sum maskable region representation into scenario
scenario += region;
}

// Store the pause scenario
status.pauseScenario = scenario;
togglePause(_regions, true);

// Notify watchers of state change
emit ProtocolPaused(_regions, msgSender());
Expand All @@ -78,18 +49,99 @@ contract PauseHandlerFacet is ProtocolBase, IBosonPauseHandler {
* Reverts if:
* - Caller does not have PAUSER role
* - Protocol is not currently paused
* - A region is specified more than once
*/
function unpause() external onlyRole(PAUSER) nonReentrant {
function unpause(BosonTypes.PausableRegion[] calldata _regions) external onlyRole(PAUSER) nonReentrant {
// Cache protocol status for reference
ProtocolLib.ProtocolStatus storage status = protocolStatus();

// Make sure the protocol is already paused
// Make sure the protocol is paused
require(status.pauseScenario > 0, NOT_PAUSED);

// Clear the pause scenario
status.pauseScenario = 0;
togglePause(_regions, false);

// Notify watchers of state change
emit ProtocolUnpaused(msgSender());
emit ProtocolUnpaused(_regions, msgSender());
}

/**
* @notice Returns the regions paused
*
* @return regions - an array of regions that are currently paused. See: {BosonTypes.PausableRegion}
*/
function getPausedRegions() external view returns (BosonTypes.PausableRegion[] memory regions) {
// Cache protocol status for reference
ProtocolLib.ProtocolStatus storage status = protocolStatus();
uint256 totalRegions = uint256(type(BosonTypes.PausableRegion).max);

regions = new BosonTypes.PausableRegion[](totalRegions);

// Return all regions if all are paused.
if (status.pauseScenario == ALL_REGIONS_MASK) {
for (uint256 i = 0; i < totalRegions; i++) {
regions[i] = BosonTypes.PausableRegion(i);
}
} else {
uint256 count = 0;

for (uint256 i = 0; i < totalRegions; i++) {
// Check if the region is paused by bitwise AND operation with shifted 1
if ((status.pauseScenario & (1 << i)) != 0) {
regions[count] = BosonTypes.PausableRegion(i);

count++;
}
}

// setting the correct number of regions
assembly {
mstore(regions, count)
}
}
}

/**
* @notice Toggles pause/unpause for some or all of the protocol.
*
* Toggle all regions if none are specified.
*
* Reverts if:
* - A region is specified more than once
*
* @param _regions - an array of regions to pause/unpause. See: {BosonTypes.PausableRegion}
* @param _pause - a boolean indicating whether to pause (true) or unpause (false)
*/
function togglePause(BosonTypes.PausableRegion[] calldata _regions, bool _pause) internal {
// Cache protocol status for reference
ProtocolLib.ProtocolStatus storage status = protocolStatus();

// Toggle all regions if none are specified.
if (_regions.length == 0) {
// Store the toggle scenario
status.pauseScenario = _pause ? ALL_REGIONS_MASK : 0;
return;
}

uint256 region;
uint256 incomingScenario;

// Calculate the incoming scenario as the sum of individual regions
// Use "or" to get the correct value even if the same region is specified more than once
for (uint256 i = 0; i < _regions.length; i++) {
// Get enum value as power of 2
region = 1 << uint256(_regions[i]);
incomingScenario |= region;
}

// Store the toggle scenario
if (_pause) {
// for pausing, just "or" the incoming scenario with the existing one
// equivalent to summation
status.pauseScenario |= incomingScenario;
} else {
// for unpausing, "and" the inverse of the incoming scenario with the existing one
// equivalent to subtraction
status.pauseScenario &= ~incomingScenario;
}
}
}
3 changes: 0 additions & 3 deletions scripts/config/revert-reasons.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ exports.RevertReasons = {
CAN_ONLY_REVOKE_SELF: "AccessControl: can only renounce roles for self",

// Pause related
NO_REGIONS_SPECIFIED: "Must specify at least one region to pause",
REGION_DUPLICATED: "A region may only be specified once",
ALREADY_PAUSED: "Protocol is already paused",
NOT_PAUSED: "Protocol is not currently paused",
REGION_PAUSED: "This region of the protocol is currently paused",

Expand Down
Loading

0 comments on commit ad78b61

Please sign in to comment.