Finish Pausable and AccessControl

This commit is contained in:
ernestognw
2023-08-10 21:55:53 -06:00
parent 70578bbb44
commit c6f2de736e
12 changed files with 293 additions and 234 deletions

View File

@ -0,0 +1,11 @@
--- access/AccessControl.sol 2023-08-10 22:02:18
+++ access/AccessControl.sol 2023-08-10 22:11:07
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
-pragma solidity ^0.8.20;
+pragma solidity ^0.8.19;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";

View File

@ -0,0 +1,11 @@
--- access/IAccessControl.sol 2023-08-10 22:02:20
+++ access/IAccessControl.sol 2023-08-10 22:11:07
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
-pragma solidity ^0.8.20;
+pragma solidity ^0.8.19;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.

View File

@ -0,0 +1,11 @@
--- security/Pausable.sol 2023-08-10 21:54:54
+++ security/Pausable.sol 2023-08-10 22:11:07
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
-pragma solidity ^0.8.20;
+pragma solidity ^0.8.19;
import {Context} from "../utils/Context.sol";

View File

@ -1,6 +1,6 @@
--- token/ERC721/ERC721.sol 2023-03-07 10:48:47.736822221 +0100 --- token/ERC721/ERC721.sol 2023-08-10 16:45:36
+++ token/ERC721/ERC721.sol 2023-03-09 19:49:39.669338673 +0100 +++ token/ERC721/ERC721.sol 2023-08-10 22:11:07
@@ -199,6 +199,11 @@ @@ -208,6 +208,11 @@
return _owners[tokenId]; return _owners[tokenId];
} }

View File

@ -0,0 +1,11 @@
--- utils/Context.sol 2023-08-10 21:54:56
+++ utils/Context.sol 2023-08-10 22:11:07
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
-pragma solidity ^0.8.20;
+pragma solidity ^0.8.19;
/**
* @dev Provides information about the current execution context, including the

View File

@ -0,0 +1,11 @@
--- utils/introspection/ERC165.sol 2023-08-10 22:02:24
+++ utils/introspection/ERC165.sol 2023-08-10 22:11:07
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
-pragma solidity ^0.8.20;
+pragma solidity ^0.8.19;
import {IERC165} from "./IERC165.sol";

View File

@ -0,0 +1,11 @@
--- utils/introspection/IERC165.sol 2023-08-09 11:45:05
+++ utils/introspection/IERC165.sol 2023-08-10 22:11:07
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
-pragma solidity ^0.8.20;
+pragma solidity ^0.8.19;
/**
* @dev Interface of the ERC165 standard, as defined in the

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20; pragma solidity ^0.8.19;
import "../patched/access/AccessControl.sol"; import {AccessControl} from "../patched/access/AccessControl.sol";
contract AccessControlHarness is AccessControl {} contract AccessControlHarness is AccessControl {}

View File

@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20; pragma solidity ^0.8.19;
import "../patched/security/Pausable.sol"; import {Pausable} from "../patched/security/Pausable.sol";
contract PausableHarness is Pausable { contract PausableHarness is Pausable {
function pause() external { function pause() external {

View File

@ -1,126 +1,119 @@
import "helpers/helpers.spec" import "helpers/helpers.spec";
import "methods/IAccessControl.spec" import "methods/IAccessControl.spec";
/* /*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
Definitions Identify entrypoints: only grantRole, revokeRole and renounceRole can alter permissions
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
definition DEFAULT_ADMIN_ROLE() returns bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000; rule onlyGrantCanGrant(env e, method f, bytes32 role, address account) {
calldataarg args;
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ bool hasRoleBefore = hasRole(role, account);
Identify entrypoints: only grantRole, revokeRole and renounceRole can alter permissions f(e, args);
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ bool hasRoleAfter = hasRole(role, account);
*/
rule onlyGrantCanGrant(env e, method f, bytes32 role, address account) { assert (
calldataarg args; !hasRoleBefore &&
hasRoleAfter
bool hasRoleBefore = hasRole(role, account); ) => (
f(e, args); f.selector == sig:grantRole(bytes32, address).selector
bool hasRoleAfter = hasRole(role, account); );
assert ( assert (
!hasRoleBefore && hasRoleBefore &&
hasRoleAfter !hasRoleAfter
) => ( ) => (
f.selector == grantRole(bytes32, address).selector f.selector == sig:revokeRole(bytes32, address).selector ||
); f.selector == sig:renounceRole(bytes32, address).selector
);
assert ( }
hasRoleBefore &&
!hasRoleAfter /*
) => ( ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
f.selector == revokeRole(bytes32, address).selector || Function correctness: grantRole only affects the specified user/role combo
f.selector == renounceRole(bytes32, address).selector └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
); */
} rule grantRoleEffect(env e, bytes32 role) {
require nonpayable(e);
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ bytes32 otherRole;
Function correctness: grantRole only affects the specified user/role combo address account;
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ address otherAccount;
*/
rule grantRoleEffect(env e, bytes32 role) { bool isCallerAdmin = hasRole(getRoleAdmin(role), e.msg.sender);
require nonpayable(e); bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
bytes32 otherRole; grantRole@withrevert(e, role, account);
address account; bool success = !lastReverted;
address otherAccount;
bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
bool isCallerAdmin = hasRole(getRoleAdmin(role), e.msg.sender);
bool hasOtherRoleBefore = hasRole(otherRole, otherAccount); // liveness
assert success <=> isCallerAdmin;
grantRole@withrevert(e, role, account);
bool success = !lastReverted; // effect
assert success => hasRole(role, account);
bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
// no side effect
// liveness assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount);
assert success <=> isCallerAdmin; }
// effect /*
assert success => hasRole(role, account); ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
Function correctness: revokeRole only affects the specified user/role combo
// no side effect └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount); */
} rule revokeRoleEffect(env e, bytes32 role) {
require nonpayable(e);
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ bytes32 otherRole;
Function correctness: revokeRole only affects the specified user/role combo address account;
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ address otherAccount;
*/
rule revokeRoleEffect(env e, bytes32 role) { bool isCallerAdmin = hasRole(getRoleAdmin(role), e.msg.sender);
require nonpayable(e); bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
bytes32 otherRole; revokeRole@withrevert(e, role, account);
address account; bool success = !lastReverted;
address otherAccount;
bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
bool isCallerAdmin = hasRole(getRoleAdmin(role), e.msg.sender);
bool hasOtherRoleBefore = hasRole(otherRole, otherAccount); // liveness
assert success <=> isCallerAdmin;
revokeRole@withrevert(e, role, account);
bool success = !lastReverted; // effect
assert success => !hasRole(role, account);
bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
// no side effect
// liveness assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount);
assert success <=> isCallerAdmin; }
// effect /*
assert success => !hasRole(role, account); ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
Function correctness: renounceRole only affects the specified user/role combo
// no side effect └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount); */
} rule renounceRoleEffect(env e, bytes32 role) {
require nonpayable(e);
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ bytes32 otherRole;
Function correctness: renounceRole only affects the specified user/role combo address account;
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ address otherAccount;
*/
rule renounceRoleEffect(env e, bytes32 role) { bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
require nonpayable(e);
renounceRole@withrevert(e, role, account);
bytes32 otherRole; bool success = !lastReverted;
address account;
address otherAccount; bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
bool hasOtherRoleBefore = hasRole(otherRole, otherAccount); // liveness
assert success <=> account == e.msg.sender;
renounceRole@withrevert(e, role, account);
bool success = !lastReverted; // effect
assert success => !hasRole(role, account);
bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
// no side effect
// liveness assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount);
assert success <=> account == e.msg.sender; }
// effect
assert success => !hasRole(role, account);
// no side effect
assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount);
}

View File

@ -1,96 +1,96 @@
import "helpers/helpers.spec" import "helpers/helpers.spec";
methods { methods {
paused() returns (bool) envfree function paused() external returns (bool) envfree;
pause() function pause() external;
unpause() function unpause() external;
onlyWhenPaused() function onlyWhenPaused() external;
onlyWhenNotPaused() function onlyWhenNotPaused() external;
} }
/* /*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
Function correctness: _pause pauses the contract Function correctness: _pause pauses the contract
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
rule pause(env e) { rule pause(env e) {
require nonpayable(e); require nonpayable(e);
bool pausedBefore = paused(); bool pausedBefore = paused();
pause@withrevert(e); pause@withrevert(e);
bool success = !lastReverted; bool success = !lastReverted;
bool pausedAfter = paused(); bool pausedAfter = paused();
// liveness // liveness
assert success <=> !pausedBefore, "works if and only if the contract was not paused before"; assert success <=> !pausedBefore, "works if and only if the contract was not paused before";
// effect // effect
assert success => pausedAfter, "contract must be paused after a successful call"; assert success => pausedAfter, "contract must be paused after a successful call";
} }
/* /*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
Function correctness: _unpause unpauses the contract Function correctness: _unpause unpauses the contract
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
rule unpause(env e) { rule unpause(env e) {
require nonpayable(e); require nonpayable(e);
bool pausedBefore = paused(); bool pausedBefore = paused();
unpause@withrevert(e); unpause@withrevert(e);
bool success = !lastReverted; bool success = !lastReverted;
bool pausedAfter = paused(); bool pausedAfter = paused();
// liveness // liveness
assert success <=> pausedBefore, "works if and only if the contract was paused before"; assert success <=> pausedBefore, "works if and only if the contract was paused before";
// effect // effect
assert success => !pausedAfter, "contract must be unpaused after a successful call"; assert success => !pausedAfter, "contract must be unpaused after a successful call";
} }
/* /*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
Function correctness: whenPaused modifier can only be called if the contract is paused Function correctness: whenPaused modifier can only be called if the contract is paused
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
rule whenPaused(env e) { rule whenPaused(env e) {
require nonpayable(e); require nonpayable(e);
onlyWhenPaused@withrevert(e); onlyWhenPaused@withrevert(e);
assert !lastReverted <=> paused(), "works if and only if the contract is paused"; 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 Function correctness: whenNotPaused modifier can only be called if the contract is not paused
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
rule whenNotPaused(env e) { rule whenNotPaused(env e) {
require nonpayable(e); require nonpayable(e);
onlyWhenNotPaused@withrevert(e); onlyWhenNotPaused@withrevert(e);
assert !lastReverted <=> !paused(), "works if and only if the contract is not paused"; assert !lastReverted <=> !paused(), "works if and only if the contract is not paused";
} }
/* /*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
Rules: only _pause and _unpause can change paused status Rules: only _pause and _unpause can change paused status
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
rule noPauseChange(env e) { rule noPauseChange(env e) {
method f; method f;
calldataarg args; calldataarg args;
bool pausedBefore = paused(); bool pausedBefore = paused();
f(e, args); f(e, args);
bool pausedAfter = paused(); bool pausedAfter = paused();
assert pausedBefore != pausedAfter => ( assert pausedBefore != pausedAfter => (
(!pausedAfter && f.selector == unpause().selector) || (!pausedAfter && f.selector == sig:unpause().selector) ||
(pausedAfter && f.selector == pause().selector) (pausedAfter && f.selector == sig:pause().selector)
), "contract's paused status can only be changed by _pause() or _unpause()"; ), "contract's paused status can only be changed by _pause() or _unpause()";
} }

View File

@ -1,7 +1,7 @@
methods { methods {
hasRole(bytes32, address) returns(bool) envfree function hasRole(bytes32, address) external returns(bool) envfree;
getRoleAdmin(bytes32) returns(bytes32) envfree function getRoleAdmin(bytes32) external returns(bytes32) envfree;
grantRole(bytes32, address) function grantRole(bytes32, address) external;
revokeRole(bytes32, address) function revokeRole(bytes32, address) external;
renounceRole(bytes32, address) function renounceRole(bytes32, address) external;
} }