GovernorPreventLateQuorum.spec 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. //////////////////////////////////////////////////////////////////////////////
  2. ///////////////////// Governor.sol base definitions //////////////////////////
  3. //////////////////////////////////////////////////////////////////////////////
  4. using ERC721VotesHarness as erc721votes
  5. using ERC20VotesHarness as erc20votes
  6. methods {
  7. proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart
  8. proposalDeadline(uint256) returns uint256 envfree // matches proposalVoteEnd
  9. hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree
  10. isExecuted(uint256) returns bool envfree
  11. isCanceled(uint256) returns bool envfree
  12. execute(address[], uint256[], bytes[], bytes32) returns uint256
  13. hasVoted(uint256, address) returns bool
  14. castVote(uint256, uint8) returns uint256
  15. updateQuorumNumerator(uint256)
  16. queue(address[], uint256[], bytes[], bytes32) returns uint256
  17. quorumNumerator() returns uint256 envfree
  18. quorumDenominator() returns uint256 envfree
  19. votingPeriod() returns uint256 envfree
  20. lateQuorumVoteExtension() returns uint64 envfree
  21. propose(address[], uint256[], bytes[], string)
  22. // harness
  23. getExtendedDeadlineIsUnset(uint256) returns bool envfree
  24. getExtendedDeadline(uint256) returns uint64 envfree
  25. quorumReached(uint256) returns bool envfree
  26. voteSucceeded(uint256) returns bool envfree
  27. quorum(uint256) returns uint256
  28. latestCastVoteCall() returns uint256 envfree // more robust check than f.selector == _castVote(...).selector
  29. // function summarization
  30. proposalThreshold() returns uint256 envfree
  31. // erc20votes dispatch
  32. getVotes(address, uint256) returns uint256 => DISPATCHER(true)
  33. // erc721votes/Votes dispatch
  34. getPastTotalSupply(uint256) returns uint256 => DISPATCHER(true)
  35. getPastVotes(address, uint256) returns uint256 => DISPATCHER(true)
  36. // timelock dispatch
  37. getMinDelay() returns uint256 => DISPATCHER(true)
  38. hashOperationBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true)
  39. executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => CONSTANT
  40. scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256) => CONSTANT
  41. }
  42. //////////////////////////////////////////////////////////////////////////////
  43. //////////////////////////////// Definitions /////////////////////////////////
  44. //////////////////////////////////////////////////////////////////////////////
  45. // where can invariants help?
  46. // can I replace definitions with invariants?
  47. // create definition for extended
  48. definition deadlineCanBeExtended(uint256 pId) returns bool =
  49. getExtendedDeadlineIsUnset(pId) &&
  50. !quorumReached(pId);
  51. definition deadlineHasBeenExtended(uint256 pId) returns bool =
  52. !getExtendedDeadlineIsUnset(pId) &&
  53. quorumReached(pId);
  54. definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0;
  55. //////////////////////////////////////////////////////////////////////////////
  56. ////////////////////////////////// Rules /////////////////////////////////////
  57. //////////////////////////////////////////////////////////////////////////////
  58. // RULE deadline can only be extended only once PASSING but vacuous
  59. // 1. if deadline changes then we have state transition to deadlineHasBeenExtended RULE PASSING; ADV SANITY PASSING
  60. rule deadlineChangeEffects(method f)
  61. filtered {
  62. f -> !f.isView
  63. } {
  64. env e; calldataarg args; uint256 pId;
  65. require (proposalCreated(pId));
  66. uint256 deadlineBefore = proposalDeadline(pId);
  67. f(e, args);
  68. uint256 deadlineAfter = proposalDeadline(pId);
  69. assert(deadlineAfter > deadlineBefore => latestCastVoteCall() == e.block.number && deadlineHasBeenExtended(pId));
  70. }
  71. // 2. cant unextend RULE PASSING*; ADV SANITY PASSING
  72. rule deadlineCantBeUnextended(method f)
  73. filtered {
  74. f -> !f.isView
  75. && f.selector != updateQuorumNumerator(uint256).selector // * fails for this function
  76. } {
  77. env e; calldataarg args; uint256 pId;
  78. require(deadlineHasBeenExtended(pId));
  79. require(proposalCreated(pId));
  80. f(e, args);
  81. assert(deadlineHasBeenExtended(pId));
  82. }
  83. // 3. extended => can't change deadline RULE PASSING; ADV SANITY PASSING
  84. //@note if deadline changed, then it wasnt extended and castvote was called
  85. rule canExtendDeadlineOnce(method f)
  86. filtered {
  87. f -> !f.isView
  88. } {
  89. env e; calldataarg args; uint256 pId;
  90. require(deadlineHasBeenExtended(pId));
  91. require(proposalCreated(pId));
  92. uint256 deadlineBefore = proposalDeadline(pId);
  93. f(e, args);
  94. uint256 deadlineAfter = proposalDeadline(pId);
  95. assert(deadlineBefore == deadlineAfter, "deadline can not be extended twice");
  96. }