GovernorStates.spec 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import "helpers.spec"
  2. import "methods/IGovernor.spec"
  3. import "Governor.helpers.spec"
  4. /*
  5. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  6. โ”‚ Rule: state returns one of the value in the enumeration โ”‚
  7. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  8. */
  9. rule stateConsistency(env e, uint256 pId) {
  10. uint8 result = state(e, pId);
  11. assert (
  12. result == PENDING() ||
  13. result == ACTIVE() ||
  14. result == CANCELED() ||
  15. result == DEFEATED() ||
  16. result == SUCCEEDED() ||
  17. result == QUEUED() ||
  18. result == EXECUTED()
  19. );
  20. }
  21. /*
  22. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  23. โ”‚ Rule: State transition โ”‚
  24. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  25. */
  26. rule stateTransitionFn(uint256 pId, env e, method f, calldataarg args)
  27. filtered { f -> !skip(f) }
  28. {
  29. require clock(e) > 0; // Sanity
  30. uint8 stateBefore = state(e, pId);
  31. f(e, args);
  32. uint8 stateAfter = state(e, pId);
  33. assert (stateBefore != stateAfter) => (
  34. stateBefore == UNSET() => (
  35. stateAfter == PENDING() && f.selector == propose(address[],uint256[],bytes[],string).selector
  36. ) &&
  37. stateBefore == PENDING() => (
  38. (stateAfter == CANCELED() && f.selector == cancel(address[],uint256[],bytes[],bytes32).selector)
  39. ) &&
  40. stateBefore == SUCCEEDED() => (
  41. (stateAfter == QUEUED() && f.selector == queue(address[],uint256[],bytes[],bytes32).selector) ||
  42. (stateAfter == EXECUTED() && f.selector == execute(address[],uint256[],bytes[],bytes32).selector)
  43. ) &&
  44. stateBefore == QUEUED() => (
  45. (stateAfter == EXECUTED() && f.selector == execute(address[],uint256[],bytes[],bytes32).selector)
  46. ) &&
  47. stateBefore == ACTIVE() => false &&
  48. stateBefore == CANCELED() => false &&
  49. stateBefore == DEFEATED() => false &&
  50. stateBefore == EXECUTED() => false
  51. );
  52. }
  53. rule stateTransitionWait(uint256 pId, env e1, env e2) {
  54. require clock(e1) > 0; // Sanity
  55. require clock(e2) > clock(e1);
  56. uint8 stateBefore = state(e1, pId);
  57. uint8 stateAfter = state(e2, pId);
  58. assert (stateBefore != stateAfter) => (
  59. stateBefore == PENDING() => (
  60. stateAfter == ACTIVE()
  61. ) &&
  62. stateBefore == ACTIVE() => (
  63. stateAfter == SUCCEEDED() ||
  64. stateAfter == DEFEATED()
  65. ) &&
  66. stateBefore == UNSET() => false &&
  67. stateBefore == SUCCEEDED() => false &&
  68. stateBefore == QUEUED() => false &&
  69. stateBefore == CANCELED() => false &&
  70. stateBefore == DEFEATED() => false &&
  71. stateBefore == EXECUTED() => false
  72. );
  73. }
  74. /*
  75. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  76. โ”‚ Rule: Once a proposal is created, voteStart, voteEnd and proposer are immutable โ”‚
  77. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  78. */
  79. rule immutableFieldsAfterProposalCreation(uint256 pId, env e, method f, calldataarg arg)
  80. filtered { f -> !skip(f) }
  81. {
  82. require state(e, pId) != UNSET();
  83. uint256 voteStart = proposalSnapshot(pId);
  84. uint256 voteEnd = proposalDeadline(pId);
  85. address proposer = proposalProposer(pId);
  86. f(e, arg);
  87. assert voteStart == proposalSnapshot(pId), "Start date was changed";
  88. assert voteEnd == proposalDeadline(pId), "End date was changed";
  89. assert proposer == proposalProposer(pId), "Proposer was changed";
  90. }
  91. /*
  92. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  93. โ”‚ Rule: propose effect and liveness. Includes "no double proposition" โ”‚
  94. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  95. */
  96. rule propose(uint256 pId, env e) {
  97. require nonpayable(e);
  98. uint256 otherId;
  99. uint8 stateBefore = state(e, pId);
  100. uint8 otherStateBefore = state(e, otherId);
  101. uint256 otherVoteStart = proposalSnapshot(otherId);
  102. uint256 otherVoteEnd = proposalDeadline(otherId);
  103. address otherProposer = proposalProposer(otherId);
  104. address[] targets; uint256[] values; bytes[] calldatas; string reason;
  105. require pId == propose@withrevert(e, targets, values, calldatas, reason);
  106. bool success = !lastReverted;
  107. // liveness & double proposal
  108. assert success <=> stateBefore == UNSET();
  109. // effect
  110. assert success => (
  111. state(e, pId) == PENDING() &&
  112. proposalProposer(pId) == e.msg.sender &&
  113. proposalSnapshot(pId) == clock(e) + votingDelay() &&
  114. proposalDeadline(pId) == clock(e) + votingDelay() + votingPeriod()
  115. );
  116. // no side-effect
  117. assert state(e, otherId) != otherStateBefore => otherId == pId;
  118. assert proposalSnapshot(otherId) == otherVoteStart;
  119. assert proposalDeadline(otherId) == otherVoteEnd;
  120. assert proposalProposer(otherId) == otherProposer;
  121. }
  122. /*
  123. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  124. โ”‚ Rule: votes effect and liveness. Includes "A user cannot vote twice" โ”‚
  125. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  126. */
  127. rule castVote(uint256 pId, env e, method f)
  128. filtered { f -> voting(f) }
  129. {
  130. require nonpayable(e);
  131. uint8 support;
  132. address voter;
  133. address otherVoter;
  134. uint256 otherId;
  135. uint8 stateBefore = state(e, pId);
  136. bool hasVotedBefore = hasVoted(pId, voter);
  137. bool otherVotedBefore = hasVoted(otherId, otherVoter);
  138. uint256 againstVotesBefore = getAgainstVotes(pId);
  139. uint256 forVotesBefore = getForVotes(pId);
  140. uint256 abstainVotesBefore = getAbstainVotes(pId);
  141. uint256 otherAgainstVotesBefore = getAgainstVotes(otherId);
  142. uint256 otherForVotesBefore = getForVotes(otherId);
  143. uint256 otherAbstainVotesBefore = getAbstainVotes(otherId);
  144. // voting weight overflow check
  145. uint256 voterWeight = token_getPastVotes(voter, proposalSnapshot(pId));
  146. require againstVotesBefore + voterWeight <= max_uint256;
  147. require forVotesBefore + voterWeight <= max_uint256;
  148. require abstainVotesBefore + voterWeight <= max_uint256;
  149. uint256 weight = helperVoteWithRevert(e, f, pId, voter, support);
  150. bool success = !lastReverted;
  151. assert success <=> (
  152. stateBefore == ACTIVE() &&
  153. !hasVotedBefore &&
  154. (support == 0 || support == 1 || support == 2)
  155. );
  156. assert success => (
  157. state(e, pId) == ACTIVE() &&
  158. voterWeight == weight &&
  159. getAgainstVotes(pId) == againstVotesBefore + (support == 0 ? weight : 0) &&
  160. getForVotes(pId) == forVotesBefore + (support == 1 ? weight : 0) &&
  161. getAbstainVotes(pId) == abstainVotesBefore + (support == 2 ? weight : 0) &&
  162. hasVoted(pId, voter)
  163. );
  164. // no side-effect
  165. assert hasVoted(otherId, otherVoter) != otherVotedBefore => (otherId == pId && otherVoter == voter);
  166. assert getAgainstVotes(otherId) != otherAgainstVotesBefore => (otherId == pId);
  167. assert getForVotes(otherId) != otherForVotesBefore => (otherId == pId);
  168. assert getAbstainVotes(otherId) != otherAbstainVotesBefore => (otherId == pId);
  169. }