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*
|
.certora*
|
||||||
.last_confs
|
.last_confs
|
||||||
certora_*
|
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",
|
"spec": "AccessControl",
|
||||||
"contract": "AccessControlHarness",
|
"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