Prechádzať zdrojové kódy

final govPreventLateQ

teryanarmen 3 rokov pred
rodič
commit
b2cdcc38d4

+ 54 - 42
certora/applyHarness.patch

@@ -1,6 +1,12 @@
+diff -ruN .gitignore .gitignore
+--- .gitignore	1969-12-31 16:00:00.000000000 -0800
++++ .gitignore	2022-05-27 01:31:23.000000000 -0700
+@@ -0,0 +1,2 @@
++*
++!.gitignore
 diff -ruN access/AccessControl.sol access/AccessControl.sol
---- access/AccessControl.sol	2022-05-25 15:36:58.849363940 -0400
-+++ access/AccessControl.sol	2022-05-25 16:07:31.897814961 -0400
+--- access/AccessControl.sol	2022-05-25 09:38:35.000000000 -0700
++++ access/AccessControl.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -93,7 +93,7 @@
       *
       * _Available since v4.6._
@@ -10,29 +16,9 @@ diff -ruN access/AccessControl.sol access/AccessControl.sol
          _checkRole(role, _msgSender());
      }
  
-diff -ruN .gitignore .gitignore
---- .gitignore	1969-12-31 19:00:00.000000000 -0500
-+++ .gitignore	2022-05-25 16:07:31.901816357 -0400
-@@ -0,0 +1,2 @@
-+*
-+!.gitignore
-diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol
---- governance/extensions/GovernorPreventLateQuorum.sol	2022-05-25 15:36:58.849363940 -0400
-+++ governance/extensions/GovernorPreventLateQuorum.sol	2022-05-25 16:07:31.901816357 -0400
-@@ -21,8 +21,8 @@
-     using SafeCast for uint256;
-     using Timers for Timers.BlockNumber;
- 
--    uint64 private _voteExtension;
--    mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines;
-+    uint64 internal _voteExtension;
-+    mapping(uint256 => Timers.BlockNumber) internal _extendedDeadlines;
- 
-     /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period.
-     event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline);
 diff -ruN governance/Governor.sol governance/Governor.sol
---- governance/Governor.sol	2022-05-25 15:36:58.849363940 -0400
-+++ governance/Governor.sol	2022-05-25 16:07:31.901816357 -0400
+--- governance/Governor.sol	2022-05-25 09:38:35.000000000 -0700
++++ governance/Governor.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -44,7 +44,7 @@
  
      string private _name;
@@ -43,8 +29,8 @@ diff -ruN governance/Governor.sol governance/Governor.sol
      // This queue keeps track of the governor operating on itself. Calls to functions protected by the
      // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute},
 diff -ruN governance/TimelockController.sol governance/TimelockController.sol
---- governance/TimelockController.sol	2022-05-25 15:36:58.849363940 -0400
-+++ governance/TimelockController.sol	2022-05-25 16:07:31.901816357 -0400
+--- governance/TimelockController.sol	2022-05-25 09:38:35.000000000 -0700
++++ governance/TimelockController.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -28,10 +28,10 @@
      bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
      bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
@@ -58,9 +44,35 @@ diff -ruN governance/TimelockController.sol governance/TimelockController.sol
  
      /**
       * @dev Emitted when a call is scheduled as part of operation `id`.
+diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol
+--- governance/extensions/GovernorCountingSimple.sol	2022-05-25 09:38:35.000000000 -0700
++++ governance/extensions/GovernorCountingSimple.sol	2022-05-27 03:15:40.000000000 -0700
+@@ -27,7 +27,7 @@
+         mapping(address => bool) hasVoted;
+     }
+ 
+-    mapping(uint256 => ProposalVote) private _proposalVotes;
++    mapping(uint256 => ProposalVote) internal _proposalVotes;
+ 
+     /**
+      * @dev See {IGovernor-COUNTING_MODE}.
+diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol
+--- governance/extensions/GovernorPreventLateQuorum.sol	2022-05-25 09:38:35.000000000 -0700
++++ governance/extensions/GovernorPreventLateQuorum.sol	2022-05-27 01:31:23.000000000 -0700
+@@ -21,8 +21,8 @@
+     using SafeCast for uint256;
+     using Timers for Timers.BlockNumber;
+ 
+-    uint64 private _voteExtension;
+-    mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines;
++    uint64 internal _voteExtension;
++    mapping(uint256 => Timers.BlockNumber) internal _extendedDeadlines;
+ 
+     /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period.
+     event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline);
 diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol
---- governance/utils/Votes.sol	2022-05-25 15:36:58.849363940 -0400
-+++ governance/utils/Votes.sol	2022-05-25 16:07:31.901816357 -0400
+--- governance/utils/Votes.sol	2022-05-25 09:38:35.000000000 -0700
++++ governance/utils/Votes.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -35,7 +35,25 @@
      bytes32 private constant _DELEGATION_TYPEHASH =
          keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
@@ -134,8 +146,8 @@ diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol
 +    function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public
  }
 diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol
---- token/ERC1155/ERC1155.sol	2022-05-25 15:36:58.853363841 -0400
-+++ token/ERC1155/ERC1155.sol	2022-05-25 16:07:31.901816357 -0400
+--- token/ERC1155/ERC1155.sol	2022-05-25 09:38:35.000000000 -0700
++++ token/ERC1155/ERC1155.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -268,7 +268,7 @@
          uint256 id,
          uint256 amount,
@@ -191,8 +203,8 @@ diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol
              try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                  bytes4 response
 diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol
---- token/ERC20/ERC20.sol	2022-05-25 15:36:58.853363841 -0400
-+++ token/ERC20/ERC20.sol	2022-05-25 16:19:02.895813907 -0400
+--- token/ERC20/ERC20.sol	2022-05-25 09:38:35.000000000 -0700
++++ token/ERC20/ERC20.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -277,7 +277,7 @@
       * - `account` cannot be the zero address.
       * - `account` must have at least `amount` tokens.
@@ -203,8 +215,8 @@ diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol
  
          _beforeTokenTransfer(account, address(0), amount);
 diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol
---- token/ERC20/extensions/ERC20FlashMint.sol	2022-05-25 15:36:58.853363841 -0400
-+++ token/ERC20/extensions/ERC20FlashMint.sol	2022-05-25 16:07:31.901816357 -0400
+--- token/ERC20/extensions/ERC20FlashMint.sol	2022-05-25 09:38:35.000000000 -0700
++++ token/ERC20/extensions/ERC20FlashMint.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -40,9 +40,11 @@
          require(token == address(this), "ERC20FlashMint: wrong token");
          // silence warning about unused variable without the addition of bytecode.
@@ -219,8 +231,8 @@ diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20
       * @dev Returns the receiver address of the flash fee. By default this
       * implementation returns the address(0) which means the fee amount will be burnt.
 diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol
---- token/ERC20/extensions/ERC20Votes.sol	2022-03-02 13:56:14.831535075 -0500
-+++ token/ERC20/extensions/ERC20Votes.sol	2022-05-25 16:14:01.137717224 -0400
+--- token/ERC20/extensions/ERC20Votes.sol	2022-05-06 13:43:21.000000000 -0700
++++ token/ERC20/extensions/ERC20Votes.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -33,8 +33,8 @@
      bytes32 private constant _DELEGATION_TYPEHASH =
          keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
@@ -242,8 +254,8 @@ diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Vote
  
          _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount);
 diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol
---- token/ERC20/extensions/ERC20Wrapper.sol	2022-05-25 15:36:58.853363841 -0400
-+++ token/ERC20/extensions/ERC20Wrapper.sol	2022-05-25 16:07:31.909819153 -0400
+--- token/ERC20/extensions/ERC20Wrapper.sol	2022-05-25 09:38:35.000000000 -0700
++++ token/ERC20/extensions/ERC20Wrapper.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -55,7 +55,7 @@
       * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal
       * function that can be exposed with access control if desired.
@@ -254,8 +266,8 @@ diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wr
          _mint(account, value);
          return value;
 diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol
---- token/ERC721/extensions/draft-ERC721Votes.sol	2022-05-25 15:36:58.853363841 -0400
-+++ token/ERC721/extensions/draft-ERC721Votes.sol	2022-05-25 16:07:31.909819153 -0400
+--- token/ERC721/extensions/draft-ERC721Votes.sol	2022-05-25 09:38:35.000000000 -0700
++++ token/ERC721/extensions/draft-ERC721Votes.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -34,7 +34,7 @@
      /**
       * @dev Returns the balance of `account`.
@@ -266,8 +278,8 @@ diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/
      }
  }
 diff -ruN utils/Address.sol utils/Address.sol
---- utils/Address.sol	2022-05-25 15:36:58.853363841 -0400
-+++ utils/Address.sol	2022-05-25 16:07:31.909819153 -0400
+--- utils/Address.sol	2022-05-25 09:38:35.000000000 -0700
++++ utils/Address.sol	2022-05-27 01:31:23.000000000 -0700
 @@ -131,6 +131,7 @@
          uint256 value,
          string memory errorMessage

+ 21 - 6
certora/harnesses/GovernorPreventLateQuorumHarness.sol

@@ -31,19 +31,34 @@ contract GovernorPreventLateQuorumHarness is Governor, GovernorCountingSimple, G
         return _voteExtension;
     }
 
-    function getExtendedDeadlineIsUnset(uint256 id) public view returns(bool) {
-        return _extendedDeadlines[id].isUnset();
+    function getExtendedDeadlineIsUnset(uint256 proposalId) public view returns(bool) {
+        return _extendedDeadlines[proposalId].isUnset();
     }
 
-    function getExtendedDeadlineIsStarted(uint256 id) public view returns(bool) {
-        return _extendedDeadlines[id].isStarted();
+    function getExtendedDeadlineIsStarted(uint256 proposalId) public view returns(bool) {
+        return _extendedDeadlines[proposalId].isStarted();
     }
 
-    function getExtendedDeadline(uint256 id) public view returns(uint64) {
-        return _extendedDeadlines[id].getDeadline();
+    function getExtendedDeadline(uint256 proposalId) public view returns(uint64) {
+        return _extendedDeadlines[proposalId].getDeadline();
     }
 
     // Harness from GovernorCountingSimple // 
+
+    function getAgainstVotes(uint256 proposalId) public view returns(uint256) {
+        ProposalVote storage proposalvote = _proposalVotes[proposalId];
+        return proposalvote.againstVotes;
+    }
+
+    function getAbstainVotes(uint256 proposalId) public view returns(uint256) {
+        ProposalVote storage proposalvote = _proposalVotes[proposalId];
+        return proposalvote.abstainVotes;
+    }
+
+    function getForVotes(uint256 proposalId) public view returns(uint256) {
+        ProposalVote storage proposalvote = _proposalVotes[proposalId];
+        return proposalvote.forVotes;
+    }
     
     function quorumReached(uint256 proposalId) public view returns(bool) {
         return _quorumReached(proposalId);

+ 35 - 0
certora/harnesses/InitializableBasicHarness.sol

@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.2;
+
+import "../munged/proxy/utils/Initializable4.6.sol";
+
+contract InitializableBasicHarness is Initializable {
+
+    uint256 public unchangeable;
+    
+    modifier version1() {
+        require(_initialized == 1);
+        _;
+    }
+
+    modifier version2() {
+        require(_initialized == 2);
+        _;
+    }
+
+    function initialize(uint256 val) public initializer {
+        unchangeable = val;
+    }
+
+    function reinitialize(uint256 val) public reinitializer(2) {
+        unchangeable = val;
+    }
+
+    function returnsV1() public view version1 returns(uint256) {
+        return unchangeable/2;
+    }
+
+    function returnsV2() public view version2 returns(uint256) {
+        return unchangeable/3;
+    }
+}

+ 18 - 0
certora/harnesses/InitializablrComplexHarness.sol

@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.2;
+
+import "../munged/proxy/utils/Initializable4.6.sol";
+
+contract InitializableBasicHarness is Initializable {
+
+    uint256 public unchangeable;
+
+    function initialize(uint256 _val) public initializer {
+        unchangeable = _val;
+    }
+    
+    function reinitialize(uint256 _val) public reinitializer(2) {
+        unchangeable = _val;
+    }
+    
+}

+ 3 - 2
certora/scripts/verifyGovernorPreventLateQuorum.sh

@@ -1,12 +1,13 @@
 certoraRun \
     certora/harnesses/ERC721VotesHarness.sol certora/munged/governance/TimelockController.sol certora/harnesses/GovernorPreventLateQuorumHarness.sol \
     --verify GovernorPreventLateQuorumHarness:certora/specs/GovernorPreventLateQuorum.spec \
-    --solc solc8.13 \
+    --solc solc \
     --optimistic_loop \
-    --disable_auto_cache_key_gen \
     --staging \
+    --settings -optimisticFallback=true \
     --send_only \
     --loop_iter 1 \
+    --rule $1 \
     --msg "$1" \
 
 

+ 3 - 1
certora/specs/GovernorBase.spec

@@ -38,7 +38,9 @@ methods {
 
 
 // proposal was created - relation proved in noStartBeforeCreation
-definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0;
+definition proposalCreated(uint256 pId) returns bool = 
+    proposalSnapshot(pId) > 0
+    && proposalDeadline(pId) > 0;
 
 
 //////////////////////////////////////////////////////////////////////////////

+ 62 - 46
certora/specs/GovernorPreventLateQuorum.spec

@@ -12,6 +12,9 @@ methods {
     getExtendedDeadlineIsUnset(uint256) returns bool envfree
     getExtendedDeadlineIsStarted(uint256) returns bool envfree
     getExtendedDeadline(uint256) returns uint64 envfree
+    getAgainstVotes(uint256) returns uint256 envfree
+    getAbstainVotes(uint256) returns uint256 envfree
+    getForVotes(uint256) returns uint256 envfree
     
     // more robust check than f.selector == _castVote(...).selector
     latestCastVoteCall() returns uint256 envfree 
@@ -22,6 +25,8 @@ methods {
     hashOperationBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true)
     executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => CONSTANT
     scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256) => CONSTANT
+    // checkpoint length ERC721
+    numCheckpoints(address) returns uint32
 }
 
 
@@ -54,27 +59,55 @@ function helperFunctionsWithRevertOnlyCastVote(uint256 proposalId, method f, env
 
 // proposal deadline can be extended (but isn't)
 definition deadlineExtendable(env e, uint256 pId) returns bool = 
-    getExtendedDeadlineIsUnset(pId)
+    getExtendedDeadlineIsUnset(pId) // deadline == 0
     && !quorumReached(e, pId);
 
 // proposal deadline has been extended
 definition deadlineExtended(env e, uint256 pId) returns bool = 
-    getExtendedDeadlineIsStarted(pId)
+    getExtendedDeadlineIsStarted(pId) // deadline > 0
     && quorumReached(e, pId);
 
+definition proposalNotCreated(env e, uint256 pId) returns bool = 
+    proposalSnapshot(pId) == 0
+    && proposalDeadline(pId) == 0
+    && getExtendedDeadlineIsUnset(pId)
+    && getAgainstVotes(pId) == 0
+    && getAbstainVotes(pId) == 0
+    && getForVotes(pId) == 0
+    && !quorumReached(e, pId);
+
 
 //////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////// Invariants /////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
 /*
- * I1: A propsal must be in state deadlineExtendable or deadlineExtended.
- * --INVARIANT PASSING // fails for updateQuorumNumerator
- * --ADVANCED SANITY PASSING // can't sanity test failing rules, not sure how it works for invariants
+ * I1: If a proposal has reached quorum then the proposal snapshot (start block.number) must be non-zero
+ * INVARIANT NOT PASSING // fails for updateQuorumNumerator and in the initial state when voting token total supply is 0 (causes quoromReached to return true)
+ * ADVANCED SANITY NOT RAN
+ */ 
+invariant quorumReachedEffect(env e, uint256 pId)
+    quorumReached(e, pId) => proposalCreated(pId) // bug: 0 supply 0 votes => quorumReached
+    // filtered { f -> f.selector != updateQuorumNumerator(uint256).selector } // * fails for this function
+
+/*
+ * I2: A non-existant proposal must meet the definition of one.
+ * INVARIANT NOT PASSING // fails for updateQuorumNumerator and in the initial state when voting token total supply is 0 (causes quoromReached to return true)
+ * ADVANCED SANITY NOT RAN
  */
-invariant proposalInOneState(env e, uint256 pId)
-    deadlineExtendable(e, pId) || deadlineExtended(e, pId)
-    { preserved { require proposalCreated(pId); } }
+invariant proposalNotCreatedEffects(env e, uint256 pId)
+    !proposalCreated(pId) => proposalNotCreated(e, pId)
+    // filtered { f -> f.selector != updateQuorumNumerator(uint256).selector } // * fails for this function
+
+/*
+ * I3: A created propsal must be in state deadlineExtendable or deadlineExtended.
+ * INVARIANT NOT PASSING // fails for updateQuorumNumerator and in the initial state when voting token total supply is 0 (causes quoromReached to return true)
+ * ADVANCED SANITY NOT RAN 
+ */
+invariant proposalInOneState(env e, uint256 pId) 
+    proposalNotCreated(e, pId) || deadlineExtendable(e, pId) || deadlineExtended(e, pId)
+    // filtered { f -> f.selector != updateQuorumNumerator(uint256).selector } // * fails for this function
+    { preserved { requireInvariant proposalNotCreatedEffects(e, pId); }}
 
 
 //////////////////////////////////////////////////////////////////////////////
@@ -92,7 +125,7 @@ invariant proposalInOneState(env e, uint256 pId)
 rule deadlineChangeEffects(method f) filtered {f -> !f.isView} {
     env e; calldataarg args; uint256 pId;
 
-    require (proposalCreated(pId));
+    requireInvariant quorumReachedEffect(e, pId);
     
     uint256 deadlineBefore = proposalDeadline(pId);
     f(e, args);
@@ -110,12 +143,12 @@ rule deadlineChangeEffects(method f) filtered {f -> !f.isView} {
 rule deadlineCantBeUnextended(method f) 
     filtered {
         f -> !f.isView
-        && f.selector != updateQuorumNumerator(uint256).selector // * fails for this function
+        // && f.selector != updateQuorumNumerator(uint256).selector // * fails for this function
     } {
     env e; calldataarg args; uint256 pId;
 
     require(deadlineExtended(e, pId));
-    require(proposalCreated(pId));
+    requireInvariant quorumReachedEffect(e, pId);
     
     f(e, args);
     
@@ -125,14 +158,14 @@ rule deadlineCantBeUnextended(method f)
 
 /*
  * R3: A proposal's deadline can't change in deadlineExtended state.
- * RULE PASSING*
+ * RULE PASSING
  * ADVANCED SANITY PASSING 
  */ 
 rule canExtendDeadlineOnce(method f) filtered {f -> !f.isView} {
     env e; calldataarg args; uint256 pId;
 
     require(deadlineExtended(e, pId));
-    require(proposalCreated(pId)); 
+    requireInvariant quorumReachedEffect(e, pId); 
     
     uint256 deadlineBefore = proposalDeadline(pId);
     f(e, args);
@@ -145,7 +178,7 @@ rule canExtendDeadlineOnce(method f) filtered {f -> !f.isView} {
 //////////////////////////// second set of rules ////////////////////////////
 
 // HIGH LEVEL RULE R6: deadline can only extended if quorum reached w/ <= timeOfExtension left to vote
-// I1, R4 and R5 are assumed in R6 so we prove them first
+// I3, R4 and R5 are assumed in R6 so we prove them first
 
 /*
  * R4: A change in hasVoted must be correlated with an increasing of the vote supports, i.e. casting a vote increases the total number of votes.
@@ -163,7 +196,7 @@ rule hasVotedCorrelationNonzero(uint256 pId, method f, env e) filtered {f -> !f.
 
     bool hasVotedBefore = hasVoted(e, pId, acc);
 
-    helperFunctionsWithRevertOnlyCastVote(pId, f, e);
+    helperFunctionsWithRevertOnlyCastVote(pId, f, e); // should be f(e, args)
 
     uint256 againstAfter = votesAgainst();
     uint256 forAfter = votesFor();
@@ -175,11 +208,11 @@ rule hasVotedCorrelationNonzero(uint256 pId, method f, env e) filtered {f -> !f.
     assert 
         (!hasVotedBefore && hasVotedAfter) => 
         (againstBefore <= againstAfter && forBefore <= forAfter && abstainBefore <= abstainAfter), 
-        "no correlation: some category decreased"; // currently vacous but keeping for CI tests
+        "after a vote is cast, the number of votes for each category must not decrease"; // currently vacous but keeping for CI tests
     assert 
         (!hasVotedBefore && hasVotedAfter) => 
         (againstBefore < againstAfter || forBefore < forAfter || abstainBefore < abstainAfter),
-        "no correlation: no category increased";
+        "after a vote is cast, the number of votes of at least one category must increase";
 }
 
 
@@ -200,7 +233,7 @@ rule againstVotesDontCount(method f) filtered {f -> !f.isView} {
     bool quorumAfter = quorumReached(e, pId);
     uint256 againstAfter = votesAgainst();
 
-    assert (againstBefore < againstAfter) => quorumBefore == quorumAfter, "quorum reached with against vote"; 
+    assert (againstBefore < againstAfter) => quorumBefore == quorumAfter, "quorum must not be reached with an against vote"; 
 }
 
 /*
@@ -211,9 +244,9 @@ rule againstVotesDontCount(method f) filtered {f -> !f.isView} {
 rule deadlineExtenededIfQuorumReached(method f) filtered {f -> !f.isView} {
     env e; calldataarg args; uint256 pId;
 
-    // need invariant that proves that a propsal must be in state deadlineExtendable or deadlineExtended
-    require(deadlineExtended(e, pId) || deadlineExtendable(e, pId));
-    require(proposalCreated(pId));
+    requireInvariant proposalInOneState(e, pId);
+    requireInvariant quorumReachedEffect(e, pId); 
+    requireInvariant proposalNotCreatedEffects(e, pId);
 
     bool wasDeadlineExtendable = deadlineExtendable(e, pId);
     uint64 extension = lateQuorumVoteExtension();
@@ -221,8 +254,8 @@ rule deadlineExtenededIfQuorumReached(method f) filtered {f -> !f.isView} {
     f(e, args);
     uint256 deadlineAfter = proposalDeadline(pId);
     
-    assert(deadlineAfter > deadlineBefore => wasDeadlineExtendable, "deadline was not extendable");
-    assert(deadlineAfter > deadlineBefore => deadlineBefore - e.block.number <= extension, "deadline extension should not be used");
+    assert deadlineAfter > deadlineBefore => wasDeadlineExtendable, "deadline must have been extendable for the deadline to be extended";
+    assert deadlineAfter > deadlineBefore => deadlineBefore - e.block.number <= extension, "deadline extension should not be used";
 }
 
 /*
@@ -232,7 +265,8 @@ rule deadlineExtenededIfQuorumReached(method f) filtered {f -> !f.isView} {
  */
 rule extendedDeadlineValueSetIfQuorumReached(method f) filtered {f -> !f.isView} {
     env e; calldataarg args; uint256 pId;
-    require(deadlineExtended(e, pId) || deadlineExtendable(e, pId));
+
+    requireInvariant proposalInOneState(e, pId);
     
     bool extendedBefore = deadlineExtended(e, pId);
     f(e, args);
@@ -247,33 +281,15 @@ rule extendedDeadlineValueSetIfQuorumReached(method f) filtered {f -> !f.isView}
 }
 
 /*
-* R8: If the deadline for a proposal has not been reached, users can still vote.
-* --RULE PASSING
-* --ADVANCED SANITY PASSING
-*/
-rule canVote(method f) filtered {f -> !f.isView} {
-    env e; calldataarg args; uint256 pId;
-    address acc = e.msg.sender;
-    uint256 deadline = proposalDeadline(pId);
-    bool votedBefore = hasVoted(e, pId, acc);
-    
-    require(proposalCreated(pId));
-    require(deadline >= e.block.number);
-    // last error? 
-    helperFunctionsWithRevertOnlyCastVote(pId, f, e);
-    bool votedAfter = hasVoted(e, pId, acc);
-    
-    assert !votedBefore && votedAfter => deadline >= e.block.number;
-}
-
-/*
- * R9: Deadline can never be reduced.
+ * R8: Deadline can never be reduced.
  * RULE PASSING
  * ADVANCED SANITY PASSING
  */
 rule deadlineNeverReduced(method f) filtered {f -> !f.isView} {
     env e; calldataarg args; uint256 pId;
-    require(proposalCreated(pId));
+
+    requireInvariant quorumReachedEffect(e, pId);
+    requireInvariant proposalNotCreatedEffects(e, pId);
 
     uint256 deadlineBefore = proposalDeadline(pId);
     f(e, args);

+ 3 - 0
certora/specs/Initializable.spec

@@ -0,0 +1,3 @@
+methods {
+    
+}