Skip to content

Commit

Permalink
Add Pausable FV (OpenZeppelin#4117)
Browse files Browse the repository at this point in the history
Co-authored-by: Hadrien Croubois <[email protected]>
  • Loading branch information
ernestognw and Amxx authored Mar 16, 2023
1 parent 4f4b6ab commit 1a60b06
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,4 @@ contracts-exposed
.certora*
.last_confs
certora_*
.zip-output-url.txt
19 changes: 19 additions & 0 deletions certora/harnesses/PausableHarness.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../patched/security/Pausable.sol";

contract PausableHarness is Pausable {
function pause() external {
_pause();
}

function unpause() external {
_unpause();
}

function onlyWhenPaused() external whenPaused {}

function onlyWhenNotPaused() external whenNotPaused {}
}
5 changes: 5 additions & 0 deletions certora/specs.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
[
{
"spec": "Pausable",
"contract": "PausableHarness",
"files": ["certora/harnesses/PausableHarness.sol"]
},
{
"spec": "AccessControl",
"contract": "AccessControlHarness",
Expand Down
96 changes: 96 additions & 0 deletions certora/specs/Pausable.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import "helpers.spec"

methods {
paused() returns (bool) envfree
pause()
unpause()
onlyWhenPaused()
onlyWhenNotPaused()
}

/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Function correctness: _pause pauses the contract │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule pause(env e) {
require nonpayable(e);

bool pausedBefore = paused();

pause@withrevert(e);
bool success = !lastReverted;

bool pausedAfter = paused();

// liveness
assert success <=> !pausedBefore, "works if and only if the contract was not paused before";

// effect
assert success => pausedAfter, "contract must be paused after a successful call";
}

/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
Function correctness: _unpause unpauses the contract
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule unpause(env e) {
require nonpayable(e);

bool pausedBefore = paused();

unpause@withrevert(e);
bool success = !lastReverted;

bool pausedAfter = paused();

// liveness
assert success <=> pausedBefore, "works if and only if the contract was paused before";

// effect
assert success => !pausedAfter, "contract must be unpaused after a successful call";
}

/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Function correctness: whenPaused modifier can only be called if the contract is paused │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule whenPaused(env e) {
require nonpayable(e);

onlyWhenPaused@withrevert(e);
assert !lastReverted <=> paused(), "works if and only if the contract is paused";
}

/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Function correctness: whenNotPaused modifier can only be called if the contract is not paused │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule whenNotPaused(env e) {
require nonpayable(e);

onlyWhenNotPaused@withrevert(e);
assert !lastReverted <=> !paused(), "works if and only if the contract is not paused";
}

/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rules: only _pause and _unpause can change paused status │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule noPauseChange(env e) {
method f;
calldataarg args;

bool pausedBefore = paused();
f(e, args);
bool pausedAfter = paused();

assert pausedBefore != pausedAfter => (
(!pausedAfter && f.selector == unpause().selector) ||
(pausedAfter && f.selector == pause().selector)
), "contract's paused status can only be changed by _pause() or _unpause()";
}

0 comments on commit 1a60b06

Please sign in to comment.