GovernorPreventLateQuorum.spec 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. //////////////////////////////////////////////////////////////////////////////
  2. ///////////////////// Governor.sol base definitions //////////////////////////
  3. //////////////////////////////////////////////////////////////////////////////
  4. using ERC721VotesHarness as erc721votes
  5. methods {
  6. proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart
  7. proposalDeadline(uint256) returns uint256 envfree // matches proposalVoteEnd
  8. hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree
  9. isExecuted(uint256) returns bool envfree
  10. isCanceled(uint256) returns bool envfree
  11. execute(address[], uint256[], bytes[], bytes32) returns uint256
  12. hasVoted(uint256, address) returns bool
  13. castVote(uint256, uint8) returns uint256
  14. updateQuorumNumerator(uint256)
  15. queue(address[], uint256[], bytes[], bytes32) returns uint256
  16. quorumNumerator() returns uint256 envfree
  17. quorumDenominator() returns uint256 envfree
  18. votingPeriod() returns uint256 envfree
  19. lateQuorumVoteExtension() returns uint64 envfree
  20. // harness
  21. getExtendedDeadlineIsUnset(uint256) returns bool envfree
  22. getExtendedDeadline(uint256) returns uint64 envfree
  23. quorumReached(uint256) returns bool envfree
  24. latestCastVoteCall() returns uint256 envfree // more robust check than f.selector == _castVote(...).selector
  25. // function summarization
  26. proposalThreshold() returns uint256 envfree
  27. getVotes(address, uint256) returns uint256 => DISPATCHER(true)
  28. getPastTotalSupply(uint256 t) returns uint256 => PER_CALLEE_CONSTANT
  29. getPastVotes(address a, uint256 t) returns uint256 => PER_CALLEE_CONSTANT
  30. }
  31. //////////////////////////////////////////////////////////////////////////////
  32. //////////////////////////////// Definitions /////////////////////////////////
  33. //////////////////////////////////////////////////////////////////////////////
  34. // create definition for extended
  35. definition deadlineCanBeExtended(uint256 id) returns bool =
  36. getExtendedDeadlineIsUnset(id) &&
  37. getExtendedDeadline(id) == 0 &&
  38. !quorumReached(id);
  39. definition deadlineHasBeenExtended(uint256 id) returns bool =
  40. !getExtendedDeadlineIsUnset(id) &&
  41. getExtendedDeadline(id) > 0 &&
  42. quorumReached(id);
  43. // RULE deadline can only be extended once
  44. // 1. if deadline changes then we have state transition from deadlineCanBeExtended to deadlineHasBeenExtended
  45. rule deadlineChangeEffects(method f) filtered {f -> !f.isView /* bottleneck, restrict for faster testing && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } {
  46. env e; calldataarg args; uint256 id;
  47. require (latestCastVoteCall() < e.block.number);
  48. require (quorumNumerator() <= quorumDenominator());
  49. require deadlineCanBeExtended(id);
  50. require (proposalDeadline(id) > e.block.number
  51. && proposalDeadline(id) >= proposalSnapshot(id) + votingPeriod()
  52. && proposalSnapshot(id) < e.block.number);
  53. uint256 deadlineBefore = proposalDeadline(id);
  54. f(e, args);
  55. uint256 deadlineAfter = proposalDeadline(id);
  56. assert(deadlineAfter > deadlineBefore => latestCastVoteCall() == e.block.number && deadlineHasBeenExtended(id));
  57. }
  58. // 2. cant unextend
  59. rule deadlineCantBeUnextended(method f) filtered {f -> !f.isView /* && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } {
  60. env e; calldataarg args; uint256 id;
  61. require(deadlineHasBeenExtended(id));
  62. f(e, args);
  63. assert(deadlineHasBeenExtended(id));
  64. }
  65. // 3. extended => can't change deadline
  66. //@note if deadline changed, then it wasnt extended and castvote was called
  67. rule canExtendDeadlineOnce(method f) filtered {f -> !f.isView /* && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } {
  68. env e; calldataarg args;
  69. uint256 id;
  70. require(deadlineHasBeenExtended(id)); // stays true
  71. require (proposalDeadline(id) > e.block.number
  72. && proposalDeadline(id) >= proposalSnapshot(id) + votingPeriod()
  73. && proposalSnapshot(id) < e.block.number);
  74. uint256 deadlineBefore = proposalDeadline(id);
  75. f(e, args);
  76. uint256 deadlineAfter = proposalDeadline(id);
  77. assert(deadlineBefore == deadlineAfter, "deadline can not be extended twice");
  78. }
  79. // RULE deadline can only extended if quorum reached w/ <= timeOfExtension left to vote
  80. // 3 rules
  81. // 1. voting increases total votes
  82. // 2. number of votes > quorum => quorum reached
  83. // 3. deadline can only extended if quorum reached w/ <= timeOfExtension left to vote
  84. rule deadlineCanOnlyBeExtenededIfQuorumReached() {
  85. env e; method f; calldataarg args;
  86. uint256 id;
  87. require(getExtendedDeadlineIsUnset(id));
  88. f(e, args);
  89. assert(false);
  90. }
  91. // RULE extendedDeadline is used iff quorum is reached w/ <= extensionTime left to vote
  92. // RULE extendedDeadlineField is set iff quroum is reached
  93. // RULE if the deadline/extendedDeadline has not been reached, you can still vote (base)