GovernorStates.spec 12 KB

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