GovernorCountingSimple.spec 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. import "GovernorBase.spec"
  2. using ERC20VotesHarness as erc20votes
  3. methods {
  4. ghost_sum_vote_power_by_id(uint256) returns uint256 envfree
  5. // castVote(uint256, uint8) returns uint256
  6. // castVoteBySig(uint256,uint8,uint8,bytes32,bytes32) returns uint256
  7. quorum(uint256) returns uint256
  8. proposalVotes(uint256) returns (uint256, uint256, uint256) envfree
  9. quorumNumerator() returns uint256
  10. _executor() returns address
  11. erc20votes._getPastVotes(address, uint256) returns uint256
  12. getExecutor() returns address
  13. //0xe38335e5 => DISPATCHER(true)
  14. }
  15. //////////////////////////////////////////////////////////////////////////////
  16. ///////////////////////////////// GHOSTS /////////////////////////////////////
  17. //////////////////////////////////////////////////////////////////////////////
  18. ghost hasVoteGhost(uint256) returns uint256 {
  19. init_state axiom forall uint256 pId. hasVoteGhost(pId) == 0;
  20. }
  21. hook Sstore _proposalVotes[KEY uint256 pId].hasVoted[KEY address user] bool current_voting_State (bool old_voting_state) STORAGE{
  22. havoc hasVoteGhost assuming forall uint256 p. ((p == pId && current_voting_State && !old_voting_state) ? (hasVoteGhost@new(p) == hasVoteGhost@old(p) + 1) :
  23. (hasVoteGhost@new(p) == hasVoteGhost@old(p)));
  24. }
  25. ghost sum_all_votes_power() returns uint256 {
  26. init_state axiom sum_all_votes_power() == 0;
  27. }
  28. hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE {
  29. havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power;
  30. }
  31. ghost tracked_weight(uint256) returns uint256 {
  32. init_state axiom forall uint256 p. tracked_weight(p) == 0;
  33. }
  34. ghost sum_tracked_weight() returns uint256 {
  35. init_state axiom sum_tracked_weight() == 0;
  36. }
  37. ghost votesAgainst() returns uint256 {
  38. init_state axiom votesAgainst() == 0;
  39. }
  40. ghost votesFor() returns uint256 {
  41. init_state axiom votesFor() == 0;
  42. }
  43. ghost votesAbstain() returns uint256 {
  44. init_state axiom votesAbstain() == 0;
  45. }
  46. hook Sstore _proposalVotes [KEY uint256 pId].againstVotes uint256 votes(uint256 old_votes) STORAGE {
  47. havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) &&
  48. (p != pId => tracked_weight@new(p) == tracked_weight@old(p));
  49. havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes;
  50. havoc votesAgainst assuming votesAgainst@new() == votesAgainst@old() - old_votes + votes;
  51. }
  52. hook Sstore _proposalVotes [KEY uint256 pId].forVotes uint256 votes(uint256 old_votes) STORAGE {
  53. havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) &&
  54. (p != pId => tracked_weight@new(p) == tracked_weight@old(p));
  55. havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes;
  56. havoc votesFor assuming votesFor@new() == votesFor@old() - old_votes + votes;
  57. }
  58. hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 old_votes) STORAGE {
  59. havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) &&
  60. (p != pId => tracked_weight@new(p) == tracked_weight@old(p));
  61. havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes;
  62. havoc votesAbstain assuming votesAbstain@new() == votesAbstain@old() - old_votes + votes;
  63. }
  64. ghost totalVotesPossible(uint256) returns uint256 {
  65. init_state axiom forall uint256 bn. totalVotesPossible(bn) == 0;
  66. }
  67. // Was done with _getVotes by mistake. Should check GovernorBasicHarness but _getVotes is from GovernorHarness
  68. hook Sstore erc20votes._getPastVotes[KEY address uId][KEY uint256 blockNumber] uint256 voteWeight(uint256 old_voteWeight) STORAGE {
  69. havoc totalVotesPossible assuming forall uint256 bn.
  70. (bn == blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn) - old_voteWeight + voteWeight)
  71. && (bn != blockNumber => totalVotesPossible@new(bn) == totalVotesPossible@old(bn));
  72. }
  73. invariant checkGetVotesGhost(address uId, uint256 blockNumber, env e)
  74. erc20votes._getPastVotes(e, uId, blockNumber) == erc20votes.getPastVotes(e, uId, blockNumber)
  75. rule whatCahnges(uint256 blockNumber, method f) {
  76. env e;
  77. calldataarg args;
  78. uint256 ghostBefore = totalVotesPossible(blockNumber);
  79. f(e,args);
  80. uint256 ghostAfter = totalVotesPossible(blockNumber);
  81. assert ghostAfter == ghostBefore, "ghost was changed";
  82. }
  83. //////////////////////////////////////////////////////////////////////////////
  84. ////////////////////////////// INVARIANTS ////////////////////////////////////
  85. //////////////////////////////////////////////////////////////////////////////
  86. /*
  87. * sum of all votes casted is equal to the sum of voting power of those who voted, per each proposal
  88. */
  89. invariant SumOfVotesCastEqualSumOfPowerOfVotedPerProposal(uint256 pId)
  90. tracked_weight(pId) == ghost_sum_vote_power_by_id(pId)
  91. /*
  92. * sum of all votes casted is equal to the sum of voting power of those who voted
  93. */
  94. invariant SumOfVotesCastEqualSumOfPowerOfVoted()
  95. sum_tracked_weight() == sum_all_votes_power()
  96. /*
  97. * totalVoted >= vote(id)
  98. */
  99. invariant OneIsNotMoreThanAll(uint256 pId)
  100. sum_all_votes_power() >= tracked_weight(pId)
  101. //NEED GHOST FIX
  102. /*
  103. * totalVotesPossible >= votePower(id)
  104. */
  105. invariant possibleTotalVotes(uint256 pId, env e)
  106. tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId))
  107. invariant voteGettersCheck(uint256 pId, address acc, env e)
  108. erc20votes.getPastVotes(e, acc, proposalSnapshot(pId)) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId))
  109. /*
  110. * totalVotesPossible >= votePower(id)
  111. */
  112. // invariant possibleTotalVotes(uint pId)
  113. //invariant trackedVsTotal(uint256 pId)
  114. // tracked_weight(pId) <= possibleMaxOfVoters(pId)
  115. /*
  116. rule someOtherRuleToRemoveLater(uint256 num){
  117. env e; calldataarg args; method f;
  118. uint256 x = hasVoteGhost(num);
  119. f(e, args);
  120. assert(false);
  121. }
  122. */
  123. /*
  124. * Checks that only one user is updated in the system when calling cast vote functions (assuming hasVoted is changing correctly, false->true, with every vote cast)
  125. */
  126. rule oneUserVotesInCast(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector ||
  127. f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} {
  128. env e; calldataarg args;
  129. uint256 ghost_Before = hasVoteGhost(pId);
  130. if (f.selector == castVote(uint256, uint8).selector)
  131. {
  132. castVote(e, pId, sup);
  133. } /*else if (f.selector == 0x7b3c71d3) {
  134. require(_pId_Harness() == proposalId);
  135. f(e,args);
  136. }*/ else if (f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) {
  137. uint8 v; bytes32 r; bytes32 s;
  138. castVoteBySig(e, pId, sup, v, r, s);
  139. } else{
  140. f(e, args);
  141. }
  142. uint256 ghost_After = hasVoteGhost(pId);
  143. assert(ghost_After == ghost_Before + 1, "Raised by more than 1");
  144. }
  145. /*
  146. * Checks that in any call to cast vote functions only the sender's value is updated
  147. */
  148. rule noVoteForSomeoneElse(uint256 pId, uint8 sup, method f) filtered {f -> f.selector == castVote(uint256, uint8).selector ||
  149. f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector} {
  150. env e; calldataarg args;
  151. address voter = e.msg.sender;
  152. address user;
  153. bool hasVotedBefore_Voter = hasVoted(e, pId, voter);
  154. bool hasVotedBefore_User = hasVoted(e, pId, user);
  155. require(!hasVotedBefore_Voter);
  156. if (f.selector == castVote(uint256, uint8).selector)
  157. {
  158. castVote(e, pId, sup);
  159. } /*else if (f.selector == 0x7b3c71d3) {
  160. require(_pId_Harness() == proposalId);
  161. f(e,args);
  162. }*/ else if (f.selector == castVoteBySig(uint256, uint8, uint8, bytes32, bytes32).selector) {
  163. uint8 v; bytes32 r; bytes32 s;
  164. castVoteBySig(e, pId, sup, v, r, s);
  165. } else{
  166. f(e, args);
  167. }
  168. bool hasVotedAfter_Voter = hasVoted(e, pId, voter);
  169. bool hasVotedAfter_User = hasVoted(e, pId, user);
  170. assert hasVotedBefore_Voter != hasVotedAfter_Voter => (user != voter => hasVotedBefore_User == hasVotedAfter_User);
  171. }
  172. rule votingWeightMonotonicity(method f){
  173. uint256 votingWeightBefore = sum_tracked_weight();
  174. env e;
  175. calldataarg args;
  176. f(e, args);
  177. uint256 votingWeightAfter = sum_tracked_weight();
  178. assert votingWeightBefore <= votingWeightAfter, "Voting weight was decreased somehow";
  179. }
  180. function callFunctionWithParams(method f, uint256 proposalId, env e) {
  181. address[] targets;
  182. uint256[] values;
  183. bytes[] calldatas;
  184. bytes32 descriptionHash;
  185. uint8 support;
  186. uint8 v; bytes32 r; bytes32 s;
  187. if (f.selector == callPropose(address[], uint256[], bytes[]).selector) {
  188. uint256 result = callPropose(e, targets, values, calldatas);
  189. require result == proposalId;
  190. } else if (f.selector == execute(address[], uint256[], bytes[], bytes32).selector) {
  191. uint256 result = execute(e, targets, values, calldatas, descriptionHash);
  192. require result == proposalId;
  193. } else if (f.selector == castVote(uint256, uint8).selector) {
  194. castVote(e, proposalId, support);
  195. //} else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) {
  196. // castVoteWithReason(e, proposalId, support, reason);
  197. } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) {
  198. castVoteBySig(e, proposalId, support, v, r, s);
  199. } else {
  200. calldataarg args;
  201. f(e,args);
  202. }
  203. }
  204. // run on ALex's branch to avoid tiomeouts: --staging alex/external-timeout-for-solvers
  205. // --staging shelly/forSasha
  206. rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) filtered {f -> f.selector != 0x7d5e81e2}{
  207. uint256 againstBefore = votesAgainst();
  208. uint256 forBefore = votesFor();
  209. uint256 abstainBefore = votesAbstain();
  210. //againstBefore, forBefore, abstainBefore = proposalVotes(pId);
  211. address acc = e.msg.sender;
  212. bool hasVotedBefore = hasVoted(e, pId, acc);
  213. uint256 votesBefore = getVotes(e, acc, bn);
  214. require votesBefore > 0;
  215. //calldataarg args;
  216. //f(e, args);
  217. callFunctionWithParams(f, pId, e);
  218. uint256 againstAfter = votesAgainst();
  219. uint256 forAfter = votesFor();
  220. uint256 abstainAfter = votesAbstain();
  221. //againstAfter, forAfter, abstainAfter = proposalVotes(pId);
  222. uint256 votesAfter = getVotes(e, acc, bn);
  223. bool hasVotedAfter = hasVoted(e, pId, acc);
  224. assert hasVotedBefore != hasVotedAfter => againstBefore <= againstAfter || forBefore <= forAfter || abstainBefore <= abstainAfter, "no correlation";
  225. }
  226. /*
  227. * Check privileged operations
  228. */
  229. rule privilegedOnly(method f, uint256 newQuorumNumerator){
  230. env e;
  231. calldataarg arg;
  232. uint256 quorumNumBefore = quorumNumerator(e);
  233. //require e.msg.sender == currentContract;
  234. f(e, arg);
  235. //updateQuorumNumerator(e, newQuorumNumerator);
  236. uint256 quorumNumAfter = quorumNumerator(e);
  237. address executorCheck = getExecutor(e);
  238. // address executorCheck = _executor(e);
  239. assert quorumNumBefore != quorumNumAfter => e.msg.sender == executorCheck, "non priveleged user changed quorum numerator";
  240. }