fix GovernorPreventLateQuorum.spec

This commit is contained in:
Hadrien Croubois
2023-02-28 18:35:03 +01:00
parent 6c4ffe783f
commit bf73fb4013
2 changed files with 24 additions and 29 deletions

View File

@ -8,6 +8,5 @@ certoraRun \
--link GovernorFullHarness:token=ERC20VotesHarness \ --link GovernorFullHarness:token=ERC20VotesHarness \
--optimistic_loop \ --optimistic_loop \
--loop_iter 1 \ --loop_iter 1 \
--send_only \
--rules deadlineNeverReduced againstVotesDontCount hasVotedCorrelationNonzero canExtendDeadlineOnce deadlineChangeEffects quorumReachedCantChange quorumLengthGt0 cantExtendWhenQuorumUnreached quorumNumerLTEDenom deprecatedQuorumStateIsUninitialized \ --rules deadlineNeverReduced againstVotesDontCount hasVotedCorrelationNonzero canExtendDeadlineOnce deadlineChangeEffects quorumReachedCantChange quorumLengthGt0 cantExtendWhenQuorumUnreached quorumNumerLTEDenom deprecatedQuorumStateIsUninitialized \
$@ $@

View File

@ -10,9 +10,9 @@ feature of giving voters more time to vote in the case that a proposal reaches
quorum with less than `voteExtension` amount of time left to vote. quorum with less than `voteExtension` amount of time left to vote.
### Assumptions and Simplifications ### Assumptions and Simplifications
None None
#### Harnessing #### Harnessing
- The contract that the specification was verified against is - The contract that the specification was verified against is
`GovernorPreventLateQuorumHarness`, which inherits from all of the Governor `GovernorPreventLateQuorumHarness`, which inherits from all of the Governor
@ -25,7 +25,7 @@ None
version. This flag stores the `block.number` in a variable version. This flag stores the `block.number` in a variable
`latestCastVoteCall` and is used as a way to check when any of variations of `latestCastVoteCall` and is used as a way to check when any of variations of
`castVote` are called. `castVote` are called.
#### Munging #### Munging
- Various variables' visibility was changed from private to internal or from - Various variables' visibility was changed from private to internal or from
@ -44,13 +44,12 @@ methods {
// summarized // summarized
hashProposal(address[],uint256[],bytes[],bytes32) returns (uint256) => NONDET hashProposal(address[],uint256[],bytes[],bytes32) returns (uint256) => NONDET
_hashTypedDataV4(bytes32) returns (bytes32) _hashTypedDataV4(bytes32) returns (bytes32)
// envfree // envfree
quorumNumerator(uint256) returns uint256 quorumNumerator(uint256) returns uint256
quorumDenominator() returns uint256 envfree quorumDenominator() returns uint256 envfree
votingPeriod() returns uint256 envfree votingPeriod() returns uint256 envfree
lateQuorumVoteExtension() returns uint64 envfree lateQuorumVoteExtension() returns uint64 envfree
propose(address[], uint256[], bytes[], string)
// harness // harness
getDeprecatedQuorumNumerator() returns uint256 envfree getDeprecatedQuorumNumerator() returns uint256 envfree
@ -59,9 +58,6 @@ methods {
getExtendedDeadlineIsUnset(uint256) returns bool envfree getExtendedDeadlineIsUnset(uint256) returns bool envfree
getExtendedDeadlineIsStarted(uint256) returns bool envfree getExtendedDeadlineIsStarted(uint256) returns bool envfree
getExtendedDeadline(uint256) returns uint64 envfree getExtendedDeadline(uint256) returns uint64 envfree
getAgainstVotes(uint256) returns uint256 envfree
getAbstainVotes(uint256) returns uint256 envfree
getForVotes(uint256) returns uint256 envfree
getPastTotalSupply(uint256) returns (uint256) envfree getPastTotalSupply(uint256) returns (uint256) envfree
// more robust check than f.selector == _castVote(...).selector // more robust check than f.selector == _castVote(...).selector
@ -94,7 +90,7 @@ function helperFunctionsWithRevertOnlyCastVote(uint256 proposalId, method f, env
function setup(env e1, env e2) { function setup(env e1, env e2) {
require getQuorumNumeratorLength() + 1 < max_uint; require getQuorumNumeratorLength() + 1 < max_uint;
require e2.block.number >= e1.block.number; require e2.block.number >= e1.block.number;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -120,8 +116,8 @@ definition proposalNotCreated(env e, uint256 pId) returns bool =
&& getForVotes(pId) == 0; && getForVotes(pId) == 0;
/// Method f is a version of `castVote` whose state changing effects are covered by `castVoteBySig`. /// Method f is a version of `castVote` whose state changing effects are covered by `castVoteBySig`.
/// @dev castVoteBySig allows anyone to cast a vote for anyone else if they can supply the signature. Specifically, /// @dev castVoteBySig allows anyone to cast a vote for anyone else if they can supply the signature. Specifically,
/// it covers the case where the msg.sender supplies a signature for themselves which is normally done using the normal /// it covers the case where the msg.sender supplies a signature for themselves which is normally done using the normal
/// `castVote`. /// `castVote`.
definition castVoteSubset(method f) returns bool = definition castVoteSubset(method f) returns bool =
f.selector == castVote(uint256, uint8).selector || f.selector == castVote(uint256, uint8).selector ||
@ -146,18 +142,18 @@ definition castVoteSubset(method f) returns bool =
invariant proposalInOneState(env e1, uint256 pId) invariant proposalInOneState(env e1, uint256 pId)
getPastTotalSupply(0) > 0 => (proposalNotCreated(e1, pId) || deadlineExtendable(e1, pId) || deadlineExtended(e1, pId)) getPastTotalSupply(0) > 0 => (proposalNotCreated(e1, pId) || deadlineExtendable(e1, pId) || deadlineExtended(e1, pId))
filtered { f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector } filtered { f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector }
{ {
preserved with (env e2) { preserved with (env e2) {
setup(e1, e2); setup(e1, e2);
} }
} }
/** /**
* The quorum numerator is always less than or equal to the quorum denominator. * The quorum numerator is always less than or equal to the quorum denominator.
*/ */
invariant quorumNumerLTEDenom(env e1, uint256 blockNumber) invariant quorumNumerLTEDenom(env e1, uint256 blockNumber)
quorumNumerator(e1, blockNumber) <= quorumDenominator() quorumNumerator(e1, blockNumber) <= quorumDenominator()
{ {
preserved with (env e2) { preserved with (env e2) {
setup(e1, e2); setup(e1, e2);
} }
@ -211,8 +207,8 @@ invariant quorumReachedEffect(env e1, uint256 pId)
* `updateQuorumNumerator` can only change quorum requirements for future proposals. * `updateQuorumNumerator` can only change quorum requirements for future proposals.
* @dev In the case that the array containing past quorum numerators overflows, this rule will fail. * @dev In the case that the array containing past quorum numerators overflows, this rule will fail.
*/ */
rule quorumReachedCantChange(method f) filtered { rule quorumReachedCantChange(method f) filtered {
f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector
} { } {
env e1; uint256 pId; env e1; uint256 pId;
bool _quorumReached = quorumReached(e1, pId); bool _quorumReached = quorumReached(e1, pId);
@ -241,7 +237,7 @@ rule hasVotedCorrelationNonzero(uint256 pId, method f, env e) filtered {
uint256 forBefore = votesFor(); uint256 forBefore = votesFor();
uint256 abstainBefore = votesAbstain(); uint256 abstainBefore = votesAbstain();
bool hasVotedBefore = hasVoted(e, pId, acc); bool hasVotedBefore = hasVoted(pId, acc);
helperFunctionsWithRevertOnlyCastVote(pId, f, e); // should be f(e, args) helperFunctionsWithRevertOnlyCastVote(pId, f, e); // should be f(e, args)
@ -249,7 +245,7 @@ rule hasVotedCorrelationNonzero(uint256 pId, method f, env e) filtered {
uint256 forAfter = votesFor(); uint256 forAfter = votesFor();
uint256 abstainAfter = votesAbstain(); uint256 abstainAfter = votesAbstain();
bool hasVotedAfter = hasVoted(e, pId, acc); bool hasVotedAfter = hasVoted(pId, acc);
// want all vote categories to not decrease and at least one category to increase // want all vote categories to not decrease and at least one category to increase
assert assert
@ -265,8 +261,8 @@ rule hasVotedCorrelationNonzero(uint256 pId, method f, env e) filtered {
/** /**
* Voting against a proposal does not count towards quorum. * Voting against a proposal does not count towards quorum.
*/ */
rule againstVotesDontCount(method f) filtered { rule againstVotesDontCount(method f) filtered {
f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector
} { } {
env e; calldataarg args; uint256 pId; env e; calldataarg args; uint256 pId;
address acc = e.msg.sender; address acc = e.msg.sender;
@ -285,8 +281,8 @@ rule againstVotesDontCount(method f) filtered {
/** /**
* Deadline can never be reduced. * Deadline can never be reduced.
*/ */
rule deadlineNeverReduced(method f) filtered { rule deadlineNeverReduced(method f) filtered {
f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector
} { } {
env e1; env e2; calldataarg args; uint256 pId; env e1; env e2; calldataarg args; uint256 pId;
@ -307,9 +303,9 @@ rule deadlineNeverReduced(method f) filtered {
/** /**
* If deadline increases then we are in `deadlineExtended` state and `castVote` * If deadline increases then we are in `deadlineExtended` state and `castVote`
* was called. * was called.
*/ */
rule deadlineChangeEffects(method f) filtered { rule deadlineChangeEffects(method f) filtered {
f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector
} { } {
env e; calldataarg args; uint256 pId; env e; calldataarg args; uint256 pId;
@ -325,7 +321,7 @@ rule deadlineChangeEffects(method f) filtered {
/** /**
* @title Deadline can't be unextended * @title Deadline can't be unextended
* @notice A proposal can't leave `deadlineExtended` state. * @notice A proposal can't leave `deadlineExtended` state.
*/ */
rule deadlineCantBeUnextended(method f) filtered { rule deadlineCantBeUnextended(method f) filtered {
f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector
} { } {
@ -342,7 +338,7 @@ rule deadlineCantBeUnextended(method f) filtered {
/** /**
* A proposal's deadline can't change in `deadlineExtended` state. * A proposal's deadline can't change in `deadlineExtended` state.
*/ */
rule canExtendDeadlineOnce(method f) filtered { rule canExtendDeadlineOnce(method f) filtered {
f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector f -> !f.isFallback && !f.isView && !castVoteSubset(f) && f.selector != relay(address,uint256,bytes).selector
} { } {
@ -351,7 +347,7 @@ rule canExtendDeadlineOnce(method f) filtered {
require(deadlineExtended(e1, pId)); require(deadlineExtended(e1, pId));
require(proposalSnapshot(pId) > 0); require(proposalSnapshot(pId) > 0);
requireInvariant quorumReachedEffect(e1, pId); requireInvariant quorumReachedEffect(e1, pId);
setup(e1, e2); setup(e1, e2);
uint256 deadlineBefore = proposalDeadline(pId); uint256 deadlineBefore = proposalDeadline(pId);
f(e2, args); f(e2, args);