GovernorFunctions.spec 12 KB

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