GovernorFunctions.spec 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. import "helpers.spec"
  2. import "Governor.helpers.spec"
  3. /*
  4. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  5. โ”‚ Rule: propose effect and liveness. Includes "no double proposition" โ”‚
  6. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  7. */
  8. rule propose_liveness(uint256 pId, env e) {
  9. require nonpayable(e);
  10. require clockSanity(e);
  11. uint8 stateBefore = state(e, pId);
  12. address[] targets; uint256[] values; bytes[] calldatas; string descr;
  13. require pId == hashProposal(targets, values, calldatas, descr);
  14. //require validString(descr);
  15. propose@withrevert(e, targets, values, calldatas, descr);
  16. // liveness & double proposal
  17. assert !lastReverted <=> (
  18. stateBefore == UNSET() &&
  19. validProposal(targets, values, calldatas)
  20. );
  21. }
  22. rule propose_effect(uint256 pId, env e) {
  23. require nonpayable(e);
  24. require clockSanity(e);
  25. address[] targets; uint256[] values; bytes[] calldatas; string descr;
  26. require pId == hashProposal(targets, values, calldatas, descr);
  27. propose(e, targets, values, calldatas, descr);
  28. // effect
  29. assert state(e, pId) == PENDING();
  30. assert proposalProposer(pId) == e.msg.sender;
  31. assert proposalSnapshot(pId) == clock(e) + votingDelay();
  32. assert proposalDeadline(pId) == clock(e) + votingDelay() + votingPeriod();
  33. }
  34. rule propose_sideeffect(uint256 pId, env e) {
  35. require nonpayable(e);
  36. require clockSanity(e);
  37. uint256 otherId;
  38. uint8 otherStateBefore = state(e, otherId);
  39. uint256 otherVoteStart = proposalSnapshot(otherId);
  40. uint256 otherVoteEnd = proposalDeadline(otherId);
  41. address otherProposer = proposalProposer(otherId);
  42. address[] targets; uint256[] values; bytes[] calldatas; string descr;
  43. require pId == hashProposal(targets, values, calldatas, descr);
  44. propose(e, targets, values, calldatas, descr);
  45. // no side-effect
  46. assert state(e, otherId) != otherStateBefore => otherId == pId;
  47. assert proposalSnapshot(otherId) == otherVoteStart;
  48. assert proposalDeadline(otherId) == otherVoteEnd;
  49. assert proposalProposer(otherId) == otherProposer;
  50. }
  51. /*
  52. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  53. โ”‚ Rule: votes effect and liveness. Includes "A user cannot vote twice" โ”‚
  54. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  55. */
  56. rule castVote_liveness(uint256 pId, env e, method f)
  57. filtered { f -> voting(f) }
  58. {
  59. require nonpayable(e);
  60. require clockSanity(e);
  61. uint8 support;
  62. address voter;
  63. uint8 stateBefore = state(e, pId);
  64. bool hasVotedBefore = hasVoted(pId, voter);
  65. uint256 voterWeight = token_getPastVotes(voter, proposalSnapshot(pId));
  66. // voting weight overflow check
  67. require getAgainstVotes(pId) + voterWeight <= max_uint256;
  68. require getForVotes(pId) + voterWeight <= max_uint256;
  69. require getAbstainVotes(pId) + voterWeight <= max_uint256;
  70. helperVoteWithRevert(e, f, pId, voter, support);
  71. assert !lastReverted <=> (
  72. stateBefore == ACTIVE() &&
  73. !hasVotedBefore &&
  74. (support == 0 || support == 1 || support == 2)
  75. );
  76. }
  77. rule castVote_effect(uint256 pId, env e, method f)
  78. filtered { f -> voting(f) }
  79. {
  80. require nonpayable(e);
  81. require clockSanity(e);
  82. uint8 support;
  83. address voter;
  84. uint256 againstVotesBefore = getAgainstVotes(pId);
  85. uint256 forVotesBefore = getForVotes(pId);
  86. uint256 abstainVotesBefore = getAbstainVotes(pId);
  87. uint256 voterWeight = token_getPastVotes(voter, proposalSnapshot(pId));
  88. uint256 weight = helperVoteWithRevert(e, f, pId, voter, support);
  89. require !lastReverted;
  90. assert state(e, pId) == ACTIVE();
  91. assert voterWeight == weight;
  92. assert getAgainstVotes(pId) == againstVotesBefore + (support == 0 ? weight : 0);
  93. assert getForVotes(pId) == forVotesBefore + (support == 1 ? weight : 0);
  94. assert getAbstainVotes(pId) == abstainVotesBefore + (support == 2 ? weight : 0);
  95. assert hasVoted(pId, voter);
  96. }
  97. rule castVote_sideeffect(uint256 pId, env e, method f)
  98. filtered { f -> voting(f) }
  99. {
  100. require nonpayable(e);
  101. require clockSanity(e);
  102. uint8 support;
  103. address voter;
  104. address otherVoter;
  105. uint256 otherId;
  106. bool otherVotedBefore = hasVoted(otherId, otherVoter);
  107. uint256 otherAgainstVotesBefore = getAgainstVotes(otherId);
  108. uint256 otherForVotesBefore = getForVotes(otherId);
  109. uint256 otherAbstainVotesBefore = getAbstainVotes(otherId);
  110. helperVoteWithRevert(e, f, pId, voter, support);
  111. require !lastReverted;
  112. // no side-effect
  113. assert hasVoted(otherId, otherVoter) != otherVotedBefore => (otherId == pId && otherVoter == voter);
  114. assert getAgainstVotes(otherId) != otherAgainstVotesBefore => (otherId == pId);
  115. assert getForVotes(otherId) != otherForVotesBefore => (otherId == pId);
  116. assert getAbstainVotes(otherId) != otherAbstainVotesBefore => (otherId == pId);
  117. }
  118. /*
  119. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  120. โ”‚ Rule: queue effect and liveness. โ”‚
  121. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  122. */
  123. rule queue_liveness(uint256 pId, env e) {
  124. require nonpayable(e);
  125. require clockSanity(e);
  126. uint8 stateBefore = state(e, pId);
  127. address[] targets; uint256[] values; bytes[] calldatas; bytes32 descrHash;
  128. require pId == hashProposal(targets, values, calldatas, descrHash);
  129. queue@withrevert(e, targets, values, calldatas, descrHash);
  130. // liveness
  131. assert !lastReverted <=> stateBefore == SUCCEEDED();
  132. }
  133. rule queue_effect(uint256 pId, env e) {
  134. require nonpayable(e);
  135. require clockSanity(e);
  136. uint8 stateBefore = state(e, pId);
  137. bool queuedBefore = isQueued(pId);
  138. address[] targets; uint256[] values; bytes[] calldatas; bytes32 descrHash;
  139. require pId == hashProposal(targets, values, calldatas, descrHash);
  140. queue(e, targets, values, calldatas, descrHash);
  141. assert state(e, pId) == QUEUED();
  142. assert isQueued(pId);
  143. assert !queuedBefore;
  144. }
  145. rule queue_sideeffect(uint256 pId, env e) {
  146. require nonpayable(e);
  147. require clockSanity(e);
  148. uint256 otherId;
  149. uint8 otherStateBefore = state(e, otherId);
  150. bool otherQueuedBefore = isQueued(otherId);
  151. address[] targets; uint256[] values; bytes[] calldatas; bytes32 descrHash;
  152. require pId == hashProposal(targets, values, calldatas, descrHash);
  153. queue(e, targets, values, calldatas, descrHash);
  154. // no side-effect
  155. assert state(e, otherId) != otherStateBefore => otherId == pId;
  156. assert isQueued(otherId) != otherQueuedBefore => otherId == pId;
  157. }
  158. /*
  159. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  160. โ”‚ Rule: execute effect and liveness. โ”‚
  161. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  162. */
  163. rule execute_liveness(uint256 pId, env e) {
  164. require nonpayable(e);
  165. require clockSanity(e);
  166. uint8 stateBefore = state(e, pId);
  167. address[] targets; uint256[] values; bytes[] calldatas; bytes32 descrHash;
  168. require pId == hashProposal(targets, values, calldatas, descrHash);
  169. execute@withrevert(e, targets, values, calldatas, descrHash);
  170. // liveness: can't check full equivalence because of execution call reverts
  171. assert !lastReverted => (stateBefore == SUCCEEDED() || stateBefore == QUEUED());
  172. }
  173. rule execute_effect(uint256 pId, env e) {
  174. require nonpayable(e);
  175. require clockSanity(e);
  176. address[] targets; uint256[] values; bytes[] calldatas; bytes32 descrHash;
  177. require pId == hashProposal(targets, values, calldatas, descrHash);
  178. execute(e, targets, values, calldatas, descrHash);
  179. // effect
  180. assert state(e, pId) == EXECUTED();
  181. }
  182. rule execute_sideeffect(uint256 pId, env e) {
  183. require nonpayable(e);
  184. require clockSanity(e);
  185. uint256 otherId;
  186. uint8 otherStateBefore = state(e, otherId);
  187. address[] targets; uint256[] values; bytes[] calldatas; bytes32 descrHash;
  188. require pId == hashProposal(targets, values, calldatas, descrHash);
  189. execute(e, targets, values, calldatas, descrHash);
  190. // no side-effect
  191. assert state(e, otherId) != otherStateBefore => otherId == pId;
  192. }
  193. /*
  194. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  195. โ”‚ Rule: cancel (public) effect and liveness. โ”‚
  196. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  197. */
  198. rule cancel_liveness(uint256 pId, env e) {
  199. require nonpayable(e);
  200. require clockSanity(e);
  201. uint8 stateBefore = state(e, pId);
  202. address[] targets; uint256[] values; bytes[] calldatas; bytes32 descrHash;
  203. require pId == hashProposal(targets, values, calldatas, descrHash);
  204. cancel@withrevert(e, targets, values, calldatas, descrHash);
  205. // liveness
  206. assert !lastReverted <=> (
  207. stateBefore == PENDING() &&
  208. e.msg.sender == proposalProposer(pId)
  209. );
  210. }
  211. rule cancel_effect(uint256 pId, env e) {
  212. require nonpayable(e);
  213. require clockSanity(e);
  214. address[] targets; uint256[] values; bytes[] calldatas; bytes32 descrHash;
  215. require pId == hashProposal(targets, values, calldatas, descrHash);
  216. cancel(e, targets, values, calldatas, descrHash);
  217. // effect
  218. assert state(e, pId) == CANCELED();
  219. assert !isQueued(pId); // cancel resets timelockId
  220. }
  221. rule cancel_sideeffect(uint256 pId, env e) {
  222. require nonpayable(e);
  223. require clockSanity(e);
  224. uint256 otherId;
  225. uint8 otherStateBefore = state(e, otherId);
  226. bool otherQueuedBefore = isQueued(otherId);
  227. address[] targets; uint256[] values; bytes[] calldatas; bytes32 descrHash;
  228. require pId == hashProposal(targets, values, calldatas, descrHash);
  229. cancel(e, targets, values, calldatas, descrHash);
  230. // no side-effect
  231. assert state(e, otherId) != otherStateBefore => otherId == pId;
  232. assert isQueued(otherId) != otherQueuedBefore => otherId == pId;
  233. }