|
@@ -2,13 +2,13 @@ import "helpers/helpers.spec";
|
|
|
import "methods/IAccessControl.spec";
|
|
|
|
|
|
methods {
|
|
|
- function PROPOSER_ROLE() returns (bytes32) envfree
|
|
|
- function EXECUTOR_ROLE() returns (bytes32) envfree
|
|
|
- function CANCELLER_ROLE() returns (bytes32) envfree
|
|
|
- function isOperation(bytes32) external returns (bool) envfree;
|
|
|
- function isOperationPending(bytes32) external returns (bool) envfree;
|
|
|
+ function PROPOSER_ROLE() external returns (bytes32) envfree;
|
|
|
+ function EXECUTOR_ROLE() external returns (bytes32) envfree;
|
|
|
+ function CANCELLER_ROLE() external returns (bytes32) envfree;
|
|
|
+ function isOperation(bytes32) external returns (bool);
|
|
|
+ function isOperationPending(bytes32) external returns (bool);
|
|
|
function isOperationReady(bytes32) external returns (bool);
|
|
|
- function isOperationDone(bytes32) external returns (bool) envfree;
|
|
|
+ function isOperationDone(bytes32) external returns (bool);
|
|
|
function getTimestamp(bytes32) external returns (uint256) envfree;
|
|
|
function getMinDelay() external returns (uint256) envfree;
|
|
|
|
|
@@ -71,30 +71,30 @@ definition UNSET() returns uint8 = 0x1;
|
|
|
definition PENDING() returns uint8 = 0x2;
|
|
|
definition DONE() returns uint8 = 0x4;
|
|
|
|
|
|
-definition isUnset(bytes32 id) returns bool = !isOperation(id);
|
|
|
-definition isPending(bytes32 id) returns bool = isOperationPending(id);
|
|
|
-definition isDone(bytes32 id) returns bool = isOperationDone(id);
|
|
|
-definition state(bytes32 id) returns uint8 = (isUnset(id) ? UNSET() : 0) | (isPending(id) ? PENDING() : 0) | (isDone(id) ? DONE() : 0);
|
|
|
+definition isUnset(env e, bytes32 id) returns bool = !isOperation(e, id);
|
|
|
+definition isPending(env e, bytes32 id) returns bool = isOperationPending(e, id);
|
|
|
+definition isDone(env e, bytes32 id) returns bool = isOperationDone(e, id);
|
|
|
+definition state(env e, bytes32 id) returns uint8 = (isUnset(e, id) ? UNSET() : 0) | (isPending(e, id) ? PENDING() : 0) | (isDone(e, id) ? DONE() : 0);
|
|
|
|
|
|
/*
|
|
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
โ Invariants: consistency of accessors โ
|
|
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
*/
|
|
|
-invariant isOperationCheck(bytes32 id)
|
|
|
- isOperation(id) <=> getTimestamp(id) > 0
|
|
|
+invariant isOperationCheck(env e, bytes32 id)
|
|
|
+ isOperation(e, id) <=> getTimestamp(id) > 0
|
|
|
filtered { f -> !f.isView }
|
|
|
|
|
|
-invariant isOperationPendingCheck(bytes32 id)
|
|
|
- isOperationPending(id) <=> getTimestamp(id) > DONE_TIMESTAMP()
|
|
|
+invariant isOperationPendingCheck(env e, bytes32 id)
|
|
|
+ isOperationPending(e, id) <=> getTimestamp(id) > DONE_TIMESTAMP()
|
|
|
filtered { f -> !f.isView }
|
|
|
|
|
|
-invariant isOperationDoneCheck(bytes32 id)
|
|
|
- isOperationDone(id) <=> getTimestamp(id) == DONE_TIMESTAMP()
|
|
|
+invariant isOperationDoneCheck(env e, bytes32 id)
|
|
|
+ isOperationDone(e, id) <=> getTimestamp(id) == DONE_TIMESTAMP()
|
|
|
filtered { f -> !f.isView }
|
|
|
|
|
|
invariant isOperationReadyCheck(env e, bytes32 id)
|
|
|
- isOperationReady(e, id) <=> (isOperationPending(id) && getTimestamp(id) <= e.block.timestamp)
|
|
|
+ isOperationReady(e, id) <=> (isOperationPending(e, id) && getTimestamp(id) <= e.block.timestamp)
|
|
|
filtered { f -> !f.isView }
|
|
|
|
|
|
/*
|
|
@@ -104,15 +104,15 @@ invariant isOperationReadyCheck(env e, bytes32 id)
|
|
|
*/
|
|
|
invariant stateConsistency(bytes32 id, env e)
|
|
|
// Check states are mutually exclusive
|
|
|
- (isUnset(id) <=> (!isPending(id) && !isDone(id) )) &&
|
|
|
- (isPending(id) <=> (!isUnset(id) && !isDone(id) )) &&
|
|
|
- (isDone(id) <=> (!isUnset(id) && !isPending(id))) &&
|
|
|
+ (isUnset(e, id) <=> (!isPending(e, id) && !isDone(e, id) )) &&
|
|
|
+ (isPending(e, id) <=> (!isUnset(e, id) && !isDone(e, id) )) &&
|
|
|
+ (isDone(e, id) <=> (!isUnset(e, id) && !isPending(e, id))) &&
|
|
|
// Check that the state helper behaves as expected:
|
|
|
- (isUnset(id) <=> state(id) == UNSET() ) &&
|
|
|
- (isPending(id) <=> state(id) == PENDING() ) &&
|
|
|
- (isDone(id) <=> state(id) == DONE() ) &&
|
|
|
+ (isUnset(e, id) <=> state(e, id) == UNSET() ) &&
|
|
|
+ (isPending(e, id) <=> state(e, id) == PENDING() ) &&
|
|
|
+ (isDone(e, id) <=> state(e, id) == DONE() ) &&
|
|
|
// Check substate
|
|
|
- isOperationReady(e, id) => isPending(id)
|
|
|
+ isOperationReady(e, id) => isPending(e, id)
|
|
|
filtered { f -> !f.isView }
|
|
|
|
|
|
/*
|
|
@@ -123,9 +123,9 @@ invariant stateConsistency(bytes32 id, env e)
|
|
|
rule stateTransition(bytes32 id, env e, method f, calldataarg args) {
|
|
|
require e.block.timestamp > 1; // Sanity
|
|
|
|
|
|
- uint8 stateBefore = state(id);
|
|
|
+ uint8 stateBefore = state(e, id);
|
|
|
f(e, args);
|
|
|
- uint8 stateAfter = state(id);
|
|
|
+ uint8 stateAfter = state(e, id);
|
|
|
|
|
|
// Cannot jump from UNSET to DONE
|
|
|
assert stateBefore == UNSET() => stateAfter != DONE();
|
|
@@ -183,7 +183,7 @@ rule schedule(env e, method f, bytes32 id, uint256 delay) filtered { f ->
|
|
|
|
|
|
bytes32 otherId; uint256 otherTimestamp = getTimestamp(otherId);
|
|
|
|
|
|
- uint8 stateBefore = state(id);
|
|
|
+ uint8 stateBefore = state(e, id);
|
|
|
bool isDelaySufficient = delay >= getMinDelay();
|
|
|
bool isProposerBefore = hasRole(PROPOSER_ROLE(), e.msg.sender);
|
|
|
|
|
@@ -198,8 +198,8 @@ rule schedule(env e, method f, bytes32 id, uint256 delay) filtered { f ->
|
|
|
);
|
|
|
|
|
|
// effect
|
|
|
- assert success => state(id) == PENDING(), "State transition violation";
|
|
|
- assert success => getTimestamp(id) == to_uint256(e.block.timestamp + delay), "Proposal timestamp not correctly set";
|
|
|
+ assert success => state(e, id) == PENDING(), "State transition violation";
|
|
|
+ assert success => getTimestamp(id) == require_uint256(e.block.timestamp + delay), "Proposal timestamp not correctly set";
|
|
|
|
|
|
// no side effect
|
|
|
assert otherTimestamp != getTimestamp(otherId) => id == otherId, "Other proposal affected";
|
|
@@ -216,10 +216,10 @@ rule execute(env e, method f, bytes32 id, bytes32 predecessor) filtered { f ->
|
|
|
} {
|
|
|
bytes32 otherId; uint256 otherTimestamp = getTimestamp(otherId);
|
|
|
|
|
|
- uint8 stateBefore = state(id);
|
|
|
+ uint8 stateBefore = state(e, id);
|
|
|
bool isOperationReadyBefore = isOperationReady(e, id);
|
|
|
bool isExecutorOrOpen = hasRole(EXECUTOR_ROLE(), e.msg.sender) || hasRole(EXECUTOR_ROLE(), 0);
|
|
|
- bool predecessorDependency = predecessor == 0 || isDone(predecessor);
|
|
|
+ bool predecessorDependency = predecessor == to_bytes32(0) || isDone(e, predecessor);
|
|
|
|
|
|
helperExecuteWithRevert(e, f, id, predecessor);
|
|
|
bool success = !lastReverted;
|
|
@@ -238,7 +238,7 @@ rule execute(env e, method f, bytes32 id, bytes32 predecessor) filtered { f ->
|
|
|
);
|
|
|
|
|
|
// effect
|
|
|
- assert success => state(id) == DONE(), "State transition violation";
|
|
|
+ assert success => state(e, id) == DONE(), "State transition violation";
|
|
|
|
|
|
// no side effect
|
|
|
assert otherTimestamp != getTimestamp(otherId) => id == otherId, "Other proposal affected";
|
|
@@ -254,7 +254,7 @@ rule cancel(env e, bytes32 id) {
|
|
|
|
|
|
bytes32 otherId; uint256 otherTimestamp = getTimestamp(otherId);
|
|
|
|
|
|
- uint8 stateBefore = state(id);
|
|
|
+ uint8 stateBefore = state(e, id);
|
|
|
bool isCanceller = hasRole(CANCELLER_ROLE(), e.msg.sender);
|
|
|
|
|
|
cancel@withrevert(e, id);
|
|
@@ -267,7 +267,7 @@ rule cancel(env e, bytes32 id) {
|
|
|
);
|
|
|
|
|
|
// effect
|
|
|
- assert success => state(id) == UNSET(), "State transition violation";
|
|
|
+ assert success => state(e, id) == UNSET(), "State transition violation";
|
|
|
|
|
|
// no side effect
|
|
|
assert otherTimestamp != getTimestamp(otherId) => id == otherId, "Other proposal affected";
|