GovernorStates.spec 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import "helpers/helpers.spec"
  2. import "helpers/Governor.helpers.spec"
  3. import "GovernorInvariants.spec"
  4. use invariant proposalStateConsistency
  5. use invariant votesImplySnapshotPassed
  6. definition assumedSafeOrDuplicate(method f) returns bool =
  7. assumedSafe(f)
  8. || f.selector == castVoteWithReason(uint256,uint8,string).selector
  9. || f.selector == castVoteWithReasonAndParams(uint256,uint8,string,bytes).selector
  10. || f.selector == castVoteBySig(uint256,uint8,uint8,bytes32,bytes32).selector
  11. || f.selector == castVoteWithReasonAndParamsBySig(uint256,uint8,string,bytes,uint8,bytes32,bytes32).selector;
  12. /*
  13. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  14. โ”‚ Rule: state returns one of the value in the enumeration โ”‚
  15. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  16. */
  17. rule stateDomain(env e, uint256 pId) {
  18. uint8 result = safeState(e, pId);
  19. assert (
  20. result == UNSET() ||
  21. result == PENDING() ||
  22. result == ACTIVE() ||
  23. result == CANCELED() ||
  24. result == DEFEATED() ||
  25. result == SUCCEEDED() ||
  26. result == QUEUED() ||
  27. result == EXECUTED()
  28. );
  29. }
  30. /*
  31. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  32. โ”‚ [DISABLED] Rule: State transitions caused by function calls โ”‚
  33. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  34. */
  35. /// Previous version that results in the prover timing out.
  36. // rule stateTransitionFn(uint256 pId, env e, method f, calldataarg args)
  37. // filtered { f -> !assumedSafeOrDuplicate(f) }
  38. // {
  39. // require clockSanity(e);
  40. // require quorumNumeratorLength() < max_uint256; // sanity
  41. //
  42. // uint8 stateBefore = safeState(e, pId);
  43. // f(e, args);
  44. // uint8 stateAfter = safeState(e, pId);
  45. //
  46. // assert (stateBefore != stateAfter) => (
  47. // (stateBefore == UNSET() && stateAfter == PENDING() && f.selector == propose(address[],uint256[],bytes[],string).selector ) ||
  48. // (stateBefore == PENDING() && stateAfter == CANCELED() && f.selector == cancel(address[],uint256[],bytes[],bytes32).selector ) ||
  49. // (stateBefore == SUCCEEDED() && stateAfter == QUEUED() && f.selector == queue(address[],uint256[],bytes[],bytes32).selector ) ||
  50. // (stateBefore == SUCCEEDED() && stateAfter == EXECUTED() && f.selector == execute(address[],uint256[],bytes[],bytes32).selector) ||
  51. // (stateBefore == QUEUED() && stateAfter == EXECUTED() && f.selector == execute(address[],uint256[],bytes[],bytes32).selector)
  52. // );
  53. // }
  54. /// This version also causes a lot of timeouts so we comment it out of now
  55. // function stateTransitionFnHelper(method f, uint8 s) returns uint8 {
  56. // uint256 pId; env e; calldataarg args;
  57. //
  58. // require clockSanity(e);
  59. // require quorumNumeratorLength() < max_uint256; // sanity
  60. //
  61. // require safeState(e, pId) == s; // constrain state before
  62. // f(e, args);
  63. // require safeState(e, pId) != s; // constrain state after
  64. //
  65. // return safeState(e, pId);
  66. // }
  67. //
  68. // rule stateTransitionFn_UNSET(method f) filtered { f -> !assumedSafeOrDuplicate(f) } {
  69. // uint8 stateAfter = stateTransitionFnHelper(f, UNSET());
  70. // assert stateAfter == PENDING() && f.selector == propose(address[],uint256[],bytes[],string).selector;
  71. // }
  72. //
  73. // rule stateTransitionFn_PENDING(method f) filtered { f -> !assumedSafeOrDuplicate(f) } {
  74. // uint8 stateAfter = stateTransitionFnHelper(f, PENDING());
  75. // assert stateAfter == CANCELED() && f.selector == cancel(address[],uint256[],bytes[],bytes32).selector;
  76. // }
  77. //
  78. // rule stateTransitionFn_ACTIVE(method f) filtered { f -> !assumedSafeOrDuplicate(f) } {
  79. // uint8 stateAfter = stateTransitionFnHelper(f, ACTIVE());
  80. // assert false;
  81. // }
  82. //
  83. // rule stateTransitionFn_CANCELED(method f) filtered { f -> !assumedSafeOrDuplicate(f) } {
  84. // uint8 stateAfter = stateTransitionFnHelper(f, CANCELED());
  85. // assert false;
  86. // }
  87. //
  88. // rule stateTransitionFn_DEFEATED(method f) filtered { f -> !assumedSafeOrDuplicate(f) } {
  89. // uint8 stateAfter = stateTransitionFnHelper(f, DEFEATED());
  90. // assert false;
  91. // }
  92. //
  93. // rule stateTransitionFn_SUCCEEDED(method f) filtered { f -> !assumedSafeOrDuplicate(f) } {
  94. // uint8 stateAfter = stateTransitionFnHelper(f, SUCCEEDED());
  95. // assert (
  96. // (stateAfter == QUEUED() && f.selector == queue(address[],uint256[],bytes[],bytes32).selector) ||
  97. // (stateAfter == EXECUTED() && f.selector == execute(address[],uint256[],bytes[],bytes32).selector)
  98. // );
  99. // }
  100. //
  101. // rule stateTransitionFn_QUEUED(method f) filtered { f -> !assumedSafeOrDuplicate(f) } {
  102. // uint8 stateAfter = stateTransitionFnHelper(f, QUEUED());
  103. // assert stateAfter == EXECUTED() && f.selector == execute(address[],uint256[],bytes[],bytes32).selector;
  104. // }
  105. //
  106. // rule stateTransitionFn_EXECUTED(method f) filtered { f -> !assumedSafeOrDuplicate(f) } {
  107. // uint8 stateAfter = stateTransitionFnHelper(f, EXECUTED());
  108. // assert false;
  109. // }
  110. /*
  111. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  112. โ”‚ Rule: State transitions caused by time passing โ”‚
  113. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  114. */
  115. // The timelockId can be set in states QUEUED, EXECUTED and CANCELED. However, checking the full scope of this results
  116. // in a timeout. This is a weaker version that is still useful. Ideally we would check `safeState`.
  117. invariant noTimelockBeforeEndOfVote(env e, uint256 pId)
  118. state(e, pId) == ACTIVE() => timelockId(pId) == 0
  119. rule stateTransitionWait(uint256 pId, env e1, env e2) {
  120. require clockSanity(e1);
  121. require clockSanity(e2);
  122. require clock(e2) > clock(e1);
  123. // Force the state to be consistent with e1 (before). We want the storage related to `pId` to match what is
  124. // possible before the time passes. We don't want the state transition include elements that cannot have happened
  125. // before e1. This ensure that the e1 โ†’ e2 state transition is purelly a consequence of time passing.
  126. requireInvariant votesImplySnapshotPassed(e1, pId);
  127. requireInvariant noTimelockBeforeEndOfVote(e1, pId);
  128. uint8 stateBefore = state(e1, pId); // Ideally we would use "safeState(e1, pId)"
  129. uint8 stateAfter = state(e2, pId); // Ideally we would use "safeState(e2, pId)"
  130. assert (stateBefore != stateAfter) => (
  131. (stateBefore == PENDING() && stateAfter == ACTIVE() ) ||
  132. (stateBefore == PENDING() && stateAfter == DEFEATED() ) ||
  133. (stateBefore == ACTIVE() && stateAfter == SUCCEEDED()) ||
  134. (stateBefore == ACTIVE() && stateAfter == DEFEATED() )
  135. );
  136. }
  137. /*
  138. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  139. โ”‚ Rule: State corresponds to the vote timing and results โ”‚
  140. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  141. */
  142. rule stateIsConsistentWithVotes(uint256 pId, env e) {
  143. require clockSanity(e);
  144. requireInvariant proposalStateConsistency(pId);
  145. uint48 currentClock = clock(e);
  146. uint8 currentState = safeState(e, pId);
  147. uint256 snapshot = proposalSnapshot(pId);
  148. uint256 deadline = proposalDeadline(pId);
  149. bool quorumSuccess = quorumReached(pId);
  150. bool voteSuccess = voteSucceeded(pId);
  151. // Pending: before vote starts
  152. assert currentState == PENDING() => (
  153. snapshot >= currentClock
  154. );
  155. // Active: after vote starts & before vote ends
  156. assert currentState == ACTIVE() => (
  157. snapshot < currentClock &&
  158. deadline >= currentClock
  159. );
  160. // Succeeded: after vote end, with vote successful and quorum reached
  161. assert currentState == SUCCEEDED() => (
  162. deadline < currentClock &&
  163. (
  164. quorumSuccess &&
  165. voteSuccess
  166. )
  167. );
  168. // Defeated: after vote end, with vote not successful or quorum not reached
  169. assert currentState == DEFEATED() => (
  170. deadline < currentClock &&
  171. (
  172. !quorumSuccess ||
  173. !voteSuccess
  174. )
  175. );
  176. }
  177. /*
  178. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  179. โ”‚ [NEED WORK] Rule: `updateQuorumNumerator` cannot cause quorumReached to change. โ”‚
  180. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  181. */
  182. //// This would be nice, but its way to slow to run because "quorumReached" is a FV nightmare
  183. //// Also, for it to work we need to prove that the checkpoints have (strictly) increasing keys.
  184. // rule onlyVoteCanChangeQuorumReached(uint256 pId, env e, method f, calldataarg args)
  185. // filtered { f -> !assumedSafeOrDuplicate(f) }
  186. // {
  187. // require clockSanity(e);
  188. // require clock(e) > proposalSnapshot(pId); // vote has started
  189. // require quorumNumeratorLength() < max_uint256; // sanity
  190. //
  191. // bool quorumReachedBefore = quorumReached(pId);
  192. //
  193. // uint256 snapshot = proposalSnapshot(pId);
  194. // uint256 totalSupply = token_getPastTotalSupply(snapshot);
  195. //
  196. // f(e, args);
  197. //
  198. // // Needed because the prover doesn't understand the checkpoint properties of the voting token.
  199. // require clock(e) > snapshot => token_getPastTotalSupply(snapshot) == totalSupply;
  200. //
  201. // assert quorumReached(pId) != quorumReachedBefore => (
  202. // !quorumReachedBefore &&
  203. // votingAll(f)
  204. // );
  205. // }
  206. //// To prove that, we need to prove that the checkpoints have (strictly) increasing keys.
  207. //// otherwise it gives us counter example where the checkpoint history has keys:
  208. //// [ 12,12,13,13,12] and the lookup obviously fail to get the correct value
  209. // rule quorumUpdateDoesntAffectPastProposals(uint256 pId, env e) {
  210. // require clockSanity(e);
  211. // require clock(e) > proposalSnapshot(pId); // vote has started
  212. // require quorumNumeratorLength() < max_uint256; // sanity
  213. //
  214. // bool quorumReachedBefore = quorumReached(pId);
  215. //
  216. // uint256 newQuorumNumerator;
  217. // updateQuorumNumerator(e, newQuorumNumerator);
  218. //
  219. // assert quorumReached(pId) == quorumReachedBefore;
  220. // }