CountingSimpleMoreCleanAndAddedMoreRules

This commit is contained in:
Aleksander Kryukov
2021-11-11 17:28:22 +02:00
parent 0d724ca892
commit 0598a3ac43
2 changed files with 151 additions and 133 deletions

View File

@ -1,6 +1,7 @@
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
///////////////////// Governor.sol base definitions ////////////////////////// ///////////////////// Governor.sol base definitions //////////////////////////
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
methods { methods {
proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart
proposalDeadline(uint256) returns uint256 envfree proposalDeadline(uint256) returns uint256 envfree
@ -17,51 +18,66 @@ methods {
// internal functions made public in harness: // internal functions made public in harness:
_quorumReached(uint256) returns bool envfree _quorumReached(uint256) returns bool envfree
_voteSucceeded(uint256) returns bool envfree _voteSucceeded(uint256) returns bool envfree
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
////////////////////////////// INVARIANTS //////////////////////////////////// ////////////////////////////// INVARIANTS ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/* /*
* A proposal cannot end unless it started. * A proposal cannot end unless it started.
*/ */
invariant voteStartBeforeVoteEnd(uint256 pId) invariant voteStartBeforeVoteEnd(uint256 pId)
(proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId)) (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) < proposalDeadline(pId))
&& (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0) && (proposalSnapshot(pId) == 0 => proposalDeadline(pId) == 0)
/* /**
* A proposal cannot be both executed and canceled. * A proposal cannot be both executed and canceled.
*/ */
invariant noBothExecutedAndCanceled(uint256 pId) invariant noBothExecutedAndCanceled(uint256 pId)
!isExecuted(pId) || !isCanceled(pId) !isExecuted(pId) || !isCanceled(pId)
/* /**
* A proposal cannot be neither executed nor canceled before it starts
*/
invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId)
e.block.number < proposalSnapshot(pId)
=> !isExecuted(pId) && !isCanceled(pId)
/*
* A proposal could be executed only if quorum was reached and vote succeeded * A proposal could be executed only if quorum was reached and vote succeeded
*/ */
invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId)
isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId) isExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId)
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/////////////////////////////////// RULES //////////////////////////////////// /////////////////////////////////// RULES ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/*
/*
* No functions should be allowed to run after a job is deemed as canceled
*/
rule cannotSetIfCanceled(uint256 pId, method f) filtered { f-> !f.isView }{
require(isCanceled(pId));
env e; calldataarg args;
f(e, args);
assert(isCanceled(pId) => lastReverted == true, "Function did not revert when canceled");
}
/*
* No functions should be allowed to run after a job is deemed as executed
*/
rule cannotSetIfExecuted(uint256 pId, method f) filtered { f-> !f.isView }{
require(isExecuted(pId));
env e; calldataarg args;
f(e, args);
assert(isExecuted(pId) => lastReverted == true, "Function did not revert after executed");
}
/*
* The voting must start not before the proposals creation time * The voting must start not before the proposals creation time
*/ */
rule noStartBeforeCreation(uint256 pId) { rule noStartBeforeCreation(uint256 pId) {
uint previousStart = proposalSnapshot(pId); uint previousStart = proposalSnapshot(pId);
require previousStart == 0; require previousStart == 0;
env e; env e;
@ -71,13 +87,13 @@ methods {
uint newStart = proposalSnapshot(pId); uint newStart = proposalSnapshot(pId);
// if created, start is after creation // if created, start is after creation
assert(newStart != 0 => newStart >= e.block.number); assert(newStart != 0 => newStart >= e.block.number);
} }
/* /*
* Once a proposal is created, voteStart and voteEnd are immutable * Once a proposal is created, voteStart and voteEnd are immutable
*/ */
rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { rule immutableFieldsAfterProposalCreation(uint256 pId, method f) {
uint _voteStart = proposalSnapshot(pId); uint _voteStart = proposalSnapshot(pId);
uint _voteEnd = proposalDeadline(pId); uint _voteEnd = proposalDeadline(pId);
require _voteStart > 0; // proposal was created require _voteStart > 0; // proposal was created
@ -90,43 +106,42 @@ methods {
uint voteEnd_ = proposalDeadline(pId); uint voteEnd_ = proposalDeadline(pId);
assert _voteStart == voteStart_; assert _voteStart == voteStart_;
assert _voteEnd == voteEnd_; assert _voteEnd == voteEnd_;
} }
/* /*
* A user cannot vote twice * A user cannot vote twice
*/ */
rule doubleVoting(uint256 pId, uint8 sup) { rule doubleVoting(uint256 pId, uint8 sup) {
env e; env e;
address user = e.msg.sender; address user = e.msg.sender;
bool votedCheck = hasVoted(e, pId, user); bool votedCheck = hasVoted(e, pId, user);
require votedCheck == true;
castVote@withrevert(e, pId, sup); castVote@withrevert(e, pId, sup);
bool reverted = lastReverted; bool reverted = lastReverted;
assert votedCheck => reverted, "double voting accured"; assert votedCheck => reverted, "double voting accured";
} }
/* /*
* When a proposal is created the start and end date are created in the future. * When a proposal is created the start and end date are created in the future.
*/ */
rule proposalCreatedStartAndEndDateInFuture(address[] targets, uint256[] values, bytes[] calldatas){ rule proposalCreatedStartAndEndDateInFuture(address[] targets, uint256[] values, bytes[] calldatas){
env e; env e;
uint256 pId = callPropose(e, targets, values, calldatas); uint256 pId = callPropose(e, targets, values, calldatas);
uint256 startDate = proposalSnapshot(pId); uint256 startDate = proposalSnapshot(pId);
uint256 endDate = proposalDeadline(pId); uint256 endDate = proposalDeadline(pId);
assert(startDate >= e.block.number && endDate >= e.block.number, "Start or end date were set in the past"); assert(startDate >= e.block.number && endDate >= e.block.number, "Start or end date were set in the past");
} }
/** /**
* Check hashProposal hashing is reliable (different inputs lead to different buffers hashed) * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed)
*/ */
/* /*
rule checkHashProposal { rule checkHashProposal {
address[] t1; address[] t1;
address[] t2; address[] t2;
uint256[] v1; uint256[] v1;
@ -143,5 +158,35 @@ methods {
assert equalHashes => v1.length == v2.length; assert equalHashes => v1.length == v2.length;
assert equalHashes => c1.length == c2.length; assert equalHashes => c1.length == c2.length;
assert equalHashes => d1 == d2; assert equalHashes => d1 == d2;
} }
*/
/**
* A proposal cannot be neither executed nor canceled before it starts
*/ */
rule noExecuteOrCancelBeforeStarting(uint256 pId, method f){
env e;
require !isExecuted(pId) && !isCanceled(pId);
calldataarg arg;
f(e, arg);
assert e.block.number < proposalSnapshot(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start";
}
/**
* A proposal cannot be neither executed nor canceled before proposal's deadline
*/
rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){
env e;
requireInvariant voteStartBeforeVoteEnd(pId);
require !isExecuted(pId) && !isCanceled(pId);
calldataarg arg;
f(e, arg);
assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before start";
}

View File

@ -1,23 +1,6 @@
////////////////////////////////////////////////////////////////////////////// import "GovernorBase.spec"
///////////////////// Governor.sol base definitions //////////////////////////
//////////////////////////////////////////////////////////////////////////////
methods { methods {
proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart
proposalDeadline(uint256) returns uint256 envfree
hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree
isExecuted(uint256) returns bool envfree
isCanceled(uint256) returns bool envfree
// initialized(uint256) returns bool envfree
hasVoted(uint256, address) returns bool
castVote(uint256, uint8) returns uint256
// internal functions made public in harness:
_quorumReached(uint256) returns bool envfree
_voteSucceeded(uint256) returns bool envfree
// getter for checking the sums
ghost_sum_vote_power_by_id(uint256) returns uint256 envfree ghost_sum_vote_power_by_id(uint256) returns uint256 envfree
} }
@ -40,29 +23,19 @@ ghost sum_tracked_weight() returns uint256 {
init_state axiom sum_tracked_weight() == 0; init_state axiom sum_tracked_weight() == 0;
} }
/*
function update_tracked_weights(uint256 pId, uint256 votes, uint256 old_votes) {
havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) &&
(p != pId => tracked_weight@new(p) == tracked_weight@old(p));
havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes;
}*/
hook Sstore _proposalVotes[KEY uint256 pId].againstVotes uint256 votes (uint256 old_votes) STORAGE { hook Sstore _proposalVotes[KEY uint256 pId].againstVotes uint256 votes (uint256 old_votes) STORAGE {
//update_tracked_weights(pId, votes, old_votes);
havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) &&
(p != pId => tracked_weight@new(p) == tracked_weight@old(p)); (p != pId => tracked_weight@new(p) == tracked_weight@old(p));
havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes;
} }
hook Sstore _proposalVotes[KEY uint256 pId].forVotes uint256 votes (uint256 old_votes) STORAGE { hook Sstore _proposalVotes[KEY uint256 pId].forVotes uint256 votes (uint256 old_votes) STORAGE {
//update_tracked_weights(pId, votes, old_votes);
havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) &&
(p != pId => tracked_weight@new(p) == tracked_weight@old(p)); (p != pId => tracked_weight@new(p) == tracked_weight@old(p));
havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes;
} }
hook Sstore _proposalVotes[KEY uint256 pId].abstainVotes uint256 votes (uint256 old_votes) STORAGE { hook Sstore _proposalVotes[KEY uint256 pId].abstainVotes uint256 votes (uint256 old_votes) STORAGE {
//update_tracked_weights(pId, votes, old_votes);
havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && havoc tracked_weight assuming forall uint256 p. (p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) &&
(p != pId => tracked_weight@new(p) == tracked_weight@old(p)); (p != pId => tracked_weight@new(p) == tracked_weight@old(p));
havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes;