Add Pausable FV (#4117)
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -68,3 +68,4 @@ contracts-exposed
|
||||
.certora*
|
||||
.last_confs
|
||||
certora_*
|
||||
.zip-output-url.txt
|
||||
|
||||
19
certora/harnesses/PausableHarness.sol
Normal file
19
certora/harnesses/PausableHarness.sol
Normal file
@ -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 {}
|
||||
}
|
||||
@ -1,4 +1,9 @@
|
||||
[
|
||||
{
|
||||
"spec": "Pausable",
|
||||
"contract": "PausableHarness",
|
||||
"files": ["certora/harnesses/PausableHarness.sol"]
|
||||
},
|
||||
{
|
||||
"spec": "AccessControl",
|
||||
"contract": "AccessControlHarness",
|
||||
|
||||
96
certora/specs/Pausable.spec
Normal file
96
certora/specs/Pausable.spec
Normal file
@ -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()";
|
||||
}
|
||||
Reference in New Issue
Block a user