123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- import "GovernorBase.spec"
- methods {
- ghost_sum_vote_power_by_id(uint256) returns uint256 envfree
- // castVote(uint256, uint8) returns uint256
- // castVoteBySig(uint256,uint8,uint8,bytes32,bytes32) returns uint256
-
- snapshot(uint256) returns uint64 envfree
- quorum(uint256) returns uint256 envfree
- proposalVotes(uint256) returns (uint256, uint256, uint256) envfree
- getVotes(address, uint256) returns uint256 envfree
- //getVotes(address, uint256) => CONSTANT
- }
- //////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////// GHOSTS /////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////
- ghost hasVoteGhost(uint256) returns uint256 {
- init_state axiom forall uint256 pId. hasVoteGhost(pId) == 0;
- }
- hook Sstore _proposalVotes[KEY uint256 pId].hasVoted[KEY address user] bool current_voting_State (bool old_voting_state) STORAGE{
- havoc hasVoteGhost assuming forall uint256 p. ((p == pId && current_voting_State && !old_voting_state) ? (hasVoteGhost@new(p) == hasVoteGhost@old(p) + 1) :
- (hasVoteGhost@new(p) == hasVoteGhost@old(p)));
- }
- ghost sum_all_votes_power() returns uint256 {
- init_state axiom sum_all_votes_power() == 0;
- }
- hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE {
- havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power;
- }
- ghost tracked_weight(uint256) returns uint256 {
- init_state axiom forall uint256 p. tracked_weight(p) == 0;
- }
- ghost sum_tracked_weight() returns uint256 {
- init_state axiom sum_tracked_weight() == 0;
- }
- ghost votesAgainst() returns uint256 {
- init_state axiom votesAgainst() == 0;
- }
- ghost votesFor() returns uint256 {
- init_state axiom votesFor() == 0;
- }
- ghost votesAbstain() returns uint256 {
- init_state axiom votesAbstain() == 0;
- }
- hook Sstore _proposalVotes [KEY uint256 pId].againstVotes uint256 votes(uint256 old_votes) STORAGE {
- 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;
- havoc votesAgainst assuming votesAgainst@new() == votesAgainst@old() - old_votes + votes;
- }
- hook Sstore _proposalVotes [KEY uint256 pId].forVotes uint256 votes(uint256 old_votes) STORAGE {
- 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;
- havoc votesFor assuming votesFor@new() == votesFor@old() - old_votes + votes;
- }
- hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 old_votes) STORAGE {
- 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;
- havoc votesAbstain assuming votesAbstain@new() == votesAbstain@old() - old_votes + votes;
- }
- /*
- ghost totalVotesPossible(uint256) returns uint256 {
- init_state axiom forall uint256 bn. totalVotesPossible(bn) == 0;
- }
- ghost possibleMaxOfVoters(uint256) returns uint256{
- init_state axiom forall uint256 pId. possibleMaxOfVoters(pId) == 0;
- }
- hook Sstore _getVotes[KEY address uId][KEY uint256 blockNumber] uint256 voteWeight(uint256 old_voteWeight) STORAGE {
- havoc totalVotesPossible assuming forall uint256 bn.
- (bn == blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn) - old_voteWeight + voteWeight)
- && (bn != blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn));
- //somehow havoc possibleMaxOfVoters based on scheme. Probably can be implemented if previous havoc is possible
- }
- */
- /*
- _proposalVotes[pId].hasVoted[account] -> bool
- ^
- |
- go through all accounts,
- if true =>
- possibleMaxOfVoters(pId) += getVotes(pId, acount);
- */
- //////////////////////////////////////////////////////////////////////////////
- ////////////////////////////// INVARIANTS ////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////
- /*
- * sum of all votes casted is equal to the sum of voting power of those who voted, per each proposal
- */
- invariant SumOfVotesCastEqualSumOfPowerOfVotedPerProposal(uint256 pId)
- tracked_weight(pId) == ghost_sum_vote_power_by_id(pId)
-
- /*
- * sum of all votes casted is equal to the sum of voting power of those who voted
- */
- invariant SumOfVotesCastEqualSumOfPowerOfVoted()
- sum_tracked_weight() == sum_all_votes_power()
-
- /*
- * totalVoted >= vote(id)
- */
- invariant OneIsNotMoreThanAll(uint256 pId)
- sum_all_votes_power() >= tracked_weight(pId)
- //NEED GHOST FIX
- /*
- * totalVotesPossible >= votePower(id)
- */
- //invariant possibleTotalVotes(uint256 pId)
- // tracked_weight(pId) <= totalVotesPossible(snapshot(pId))
- /*
- * totalVotesPossible >= votePower(id)
- */
- // invariant possibleTotalVotes(uint pId)
- //invariant trackedVsTotal(uint256 pId)
- // tracked_weight(pId) <= possibleMaxOfVoters(pId)
- /*
- rule someOtherRuleToRemoveLater(uint256 num){
- env e; calldataarg args; method f;
- uint256 x = hasVoteGhost(num);
- f(e, args);
- assert(false);
- }
- */
- rule oneUserVotesInCast(uint256 pId, method f) filtered { f -> ((f.selector == castVote(uint256, uint8).selector) ||
- f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) }{
- env e; calldataarg args;
- uint256 ghost_Before = hasVoteGhost(pId);
- f(e, args);
- uint256 ghost_After = hasVoteGhost(pId);
- assert(ghost_After == ghost_Before + 1, "Raised by more than 1");
- }
- rule noVoteForSomeoneElse(uint256 pId, uint8 support){
- env e;
- uint256 voter = e.msg.sender;
- castVote(e, pId, support);
- }
- //ok
- rule votingWeightMonotonicity(method f){
- uint256 votingWeightBefore = sum_tracked_weight();
- env e;
- calldataarg args;
- f(e, args);
- uint256 votingWeightAfter = sum_tracked_weight();
- assert votingWeightBefore <= votingWeightAfter, "Voting weight was decreased somehow";
- }
- // timeouts and strange violations
- // https://vaas-stg.certora.com/output/3106/23f63c84c58b06285f4f/?anonymousKey=e5a7dc2e0ce7829cf5af8eb29a4ba231d915704c
- rule quorumMonotonicity(method f, uint256 blockNumber){
- uint256 quorumBefore = quorum(blockNumber);
- env e;
- calldataarg args;
- f(e, args);
- uint256 quorumAfter = quorum(blockNumber);
- assert quorumBefore <= quorumAfter, "Quorum was decreased somehow";
- }
- // getVotes() returns different results.
- // are we assuming that a person with 0 votes can vote? are we assuming that a person may have 0 votes
- // it seems like a weight can be 0. At least there is no check for it
- // If it can be 0 then < should be changed to <= but it gives less coverage
- rule hasVotedCorrelation(uint256 pId, method f, address acc, env e){
- uint256 againstBefore = votesAgainst();
- uint256 forBefore = votesFor();
- uint256 abstainBefore = votesAbstain();
- //againstBefore, forBefore, abstainBefore = proposalVotes(pId);
- bool hasVotedBefore = hasVoted(e, pId, acc);
- uint256 votesBefore = getVotes(acc, pId);
- require votesBefore > 0;
- calldataarg args;
- f(e, args);
- uint256 againstAfter = votesAgainst();
- uint256 forAfter = votesFor();
- uint256 abstainAfter = votesAbstain();
- //againstAfter, forAfter, abstainAfter = proposalVotes(pId);
- uint256 votesAfter = getVotes(acc, pId);
- bool hasVotedAfter = hasVoted(e, pId, acc);
- assert hasVotedBefore != hasVotedAfter => againstBefore < againstAfter || forBefore < forAfter || abstainBefore < abstainAfter, "no correlation";
- }
|