GovernorStates.spec 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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. rule stateConsistency(env e, uint256 pId) {
  12. uint8 result = state(e, pId);
  13. assert (
  14. result == PENDING() ||
  15. result == ACTIVE() ||
  16. result == CANCELED() ||
  17. result == DEFEATED() ||
  18. result == SUCCEEDED() ||
  19. result == QUEUED() ||
  20. result == EXECUTED()
  21. );
  22. }
  23. /*
  24. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  25. โ”‚ Rule: State transitions caused by function calls โ”‚
  26. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  27. */
  28. rule stateTransitionFn(uint256 pId, env e, method f, calldataarg args)
  29. filtered { f -> !assumedSafe(f)
  30. && f.selector != castVoteBySig(uint256,uint8,uint8,bytes32,bytes32).selector
  31. && f.selector != castVoteWithReasonAndParams(uint256,uint8,string,bytes).selector
  32. && f.selector != castVoteWithReason(uint256,uint8,string).selector
  33. && f.selector != castVoteWithReasonAndParamsBySig(uint256,uint8,string,bytes,uint8,bytes32,bytes32).selector
  34. }
  35. {
  36. require clockSanity(e);
  37. require quorumNumeratorLength() < max_uint256; // sanity
  38. uint8 stateBefore = state(e, pId);
  39. f(e, args);
  40. uint8 stateAfter = state(e, pId);
  41. assert (stateBefore != stateAfter) => (
  42. (stateBefore == UNSET() && stateAfter == PENDING() && f.selector == propose(address[],uint256[],bytes[],string).selector ) ||
  43. (stateBefore == PENDING() && stateAfter == CANCELED() && f.selector == cancel(address[],uint256[],bytes[],bytes32).selector ) ||
  44. (stateBefore == SUCCEEDED() && stateAfter == QUEUED() && f.selector == queue(address[],uint256[],bytes[],bytes32).selector ) ||
  45. (stateBefore == SUCCEEDED() && stateAfter == EXECUTED() && f.selector == execute(address[],uint256[],bytes[],bytes32).selector) ||
  46. (stateBefore == QUEUED() && stateAfter == EXECUTED() && f.selector == execute(address[],uint256[],bytes[],bytes32).selector)
  47. );
  48. }
  49. /*
  50. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  51. โ”‚ Rule: State transitions caused by time passing โ”‚
  52. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  53. */
  54. rule stateTransitionWait(uint256 pId, env e1, env e2) {
  55. require clockSanity(e1);
  56. require clockSanity(e2);
  57. require clock(e2) > clock(e1);
  58. // Force the state to be consistent with e1 (before). We want the storage related to `pId` to match what is
  59. // possible before the time passes. We don't want the state transition include elements that cannot have happened
  60. // before e1. This ensure that the e1 โ†’ e2 state transition is purelly a consequence of time passing.
  61. requireInvariant votesImplySnapshotPassed(e1, pId);
  62. uint8 stateBefore = state(e1, pId);
  63. uint8 stateAfter = state(e2, pId);
  64. assert (stateBefore != stateAfter) => (
  65. (stateBefore == PENDING() && stateAfter == ACTIVE() ) ||
  66. (stateBefore == PENDING() && stateAfter == DEFEATED() ) ||
  67. (stateBefore == ACTIVE() && stateAfter == SUCCEEDED()) ||
  68. (stateBefore == ACTIVE() && stateAfter == DEFEATED() ) ||
  69. // Strange consequence of the timelock binding:
  70. // When transitioning from ACTIVE to SUCCEEDED (because of the clock moving forward) the proposal state in
  71. // the timelock is suddenly considered. Prior state set in the timelock can cause the proposal to already be
  72. // queued, executed or canceled.
  73. (stateBefore == ACTIVE() && stateAfter == CANCELED()) ||
  74. (stateBefore == ACTIVE() && stateAfter == EXECUTED()) ||
  75. (stateBefore == ACTIVE() && stateAfter == QUEUED())
  76. );
  77. }
  78. /*
  79. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  80. โ”‚ Rule: State corresponds to the vote timing and results โ”‚
  81. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  82. */
  83. rule stateIsConsistentWithVotes(uint256 pId, env e) {
  84. require clockSanity(e);
  85. requireInvariant proposalStateConsistency(pId);
  86. uint48 currentClock = clock(e);
  87. uint8 currentState = state(e, pId);
  88. uint256 snapshot = proposalSnapshot(pId);
  89. uint256 deadline = proposalDeadline(pId);
  90. bool quorumSuccess = quorumReached(pId);
  91. bool voteSuccess = voteSucceeded(pId);
  92. // Pending: before vote starts
  93. assert currentState == PENDING() => (
  94. snapshot >= currentClock
  95. );
  96. // Active: after vote starts & before vote ends
  97. assert currentState == ACTIVE() => (
  98. snapshot < currentClock &&
  99. deadline >= currentClock
  100. );
  101. // Succeeded: after vote end, with vote successful and quorum reached
  102. assert currentState == SUCCEEDED() => (
  103. deadline < currentClock &&
  104. (
  105. quorumSuccess &&
  106. voteSuccess
  107. )
  108. );
  109. // Defeated: after vote end, with vote not successful or quorum not reached
  110. assert currentState == DEFEATED() => (
  111. deadline < currentClock &&
  112. (
  113. !quorumSuccess ||
  114. !voteSuccess
  115. )
  116. );
  117. }
  118. /*
  119. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  120. โ”‚ [NEED WORK] Rule: `updateQuorumNumerator` cannot cause quorumReached to change. โ”‚
  121. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  122. */
  123. //// This would be nice, but its way to slow to run because "quorumReached" is a FV nightmare
  124. //// Also, for it to work we need to prove that the checkpoints have (strictly) increasing keys.
  125. // rule onlyVoteCanChangeQuorumReached(uint256 pId, env e, method f, calldataarg args)
  126. // filtered { f -> !assumedSafe(f) }
  127. // {
  128. // require clockSanity(e);
  129. // require clock(e) > proposalSnapshot(pId); // vote has started
  130. // require quorumNumeratorLength() < max_uint256; // sanity
  131. //
  132. // bool quorumReachedBefore = quorumReached(pId);
  133. //
  134. // uint256 snapshot = proposalSnapshot(pId);
  135. // uint256 totalSupply = token_getPastTotalSupply(snapshot);
  136. //
  137. // f(e, args);
  138. //
  139. // // Needed because the prover doesn't understand the checkpoint properties of the voting token.
  140. // require clock(e) > snapshot => token_getPastTotalSupply(snapshot) == totalSupply;
  141. //
  142. // assert quorumReached(pId) != quorumReachedBefore => (
  143. // !quorumReachedBefore &&
  144. // votingAll(f)
  145. // );
  146. // }
  147. //// To prove that, we need to prove that the checkpoints have (strictly) increasing keys.
  148. //// otherwise it gives us counter example where the checkpoint history has keys:
  149. //// [ 12,12,13,13,12] and the lookup obviously fail to get the correct value
  150. // rule quorumUpdateDoesntAffectPastProposals(uint256 pId, env e) {
  151. // require clockSanity(e);
  152. // require clock(e) > proposalSnapshot(pId); // vote has started
  153. // require quorumNumeratorLength() < max_uint256; // sanity
  154. //
  155. // bool quorumReachedBefore = quorumReached(pId);
  156. //
  157. // uint256 newQuorumNumerator;
  158. // updateQuorumNumerator(e, newQuorumNumerator);
  159. //
  160. // assert quorumReached(pId) == quorumReachedBefore;
  161. // }