fix some specs

formal-verification
Hadrien Croubois 2 years ago
parent bf73fb4013
commit 7dc201fce9
  1. 12
      certora/harnesses/GovernorFullHarness.sol
  2. 4
      certora/harnesses/GovernorHarness.sol
  3. 3
      certora/scripts/passes/verifyGovernorPreventLateQuorum.sh
  4. 69
      certora/specs/GovernorBase.spec

@ -62,14 +62,6 @@ contract GovernorFullHarness is
return _voteExtension; return _voteExtension;
} }
function getExtendedDeadlineIsUnset(uint256 proposalId) public view returns (bool) {
return _extendedDeadlines[proposalId] == 0;
}
function getExtendedDeadlineIsStarted(uint256 proposalId) public view returns (bool) {
return _extendedDeadlines[proposalId] > 0;
}
function getExtendedDeadline(uint256 proposalId) public view returns (uint64) { function getExtendedDeadline(uint256 proposalId) public view returns (uint64) {
return _extendedDeadlines[proposalId]; return _extendedDeadlines[proposalId];
} }
@ -105,6 +97,10 @@ contract GovernorFullHarness is
return _executor(); return _executor();
} }
function proposalProposer(uint256 proposalId) public view returns (address) {
return _proposalProposer(proposalId);
}
function isExecuted(uint256 proposalId) public view returns (bool) { function isExecuted(uint256 proposalId) public view returns (bool) {
return _proposals[proposalId].executed; return _proposals[proposalId].executed;
} }

@ -63,6 +63,10 @@ contract GovernorHarness is
return _executor(); return _executor();
} }
function proposalProposer(uint256 proposalId) public view returns (address) {
return _proposalProposer(proposalId);
}
function isExecuted(uint256 proposalId) public view returns (bool) { function isExecuted(uint256 proposalId) public view returns (bool) {
return _proposals[proposalId].executed; return _proposals[proposalId].executed;
} }

@ -7,6 +7,5 @@ certoraRun \
--verify GovernorFullHarness:certora/specs/GovernorPreventLateQuorum.spec \ --verify GovernorFullHarness:certora/specs/GovernorPreventLateQuorum.spec \
--link GovernorFullHarness:token=ERC20VotesHarness \ --link GovernorFullHarness:token=ERC20VotesHarness \
--optimistic_loop \ --optimistic_loop \
--loop_iter 1 \ --optimistic_hashing \
--rules deadlineNeverReduced againstVotesDontCount hasVotedCorrelationNonzero canExtendDeadlineOnce deadlineChangeEffects quorumReachedCantChange quorumLengthGt0 cantExtendWhenQuorumUnreached quorumNumerLTEDenom deprecatedQuorumStateIsUninitialized \
$@ $@

@ -8,6 +8,7 @@ methods {
proposalThreshold() returns uint256 envfree proposalThreshold() returns uint256 envfree
proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart
proposalDeadline(uint256) returns uint256 envfree // matches proposalVoteEnd proposalDeadline(uint256) returns uint256 envfree // matches proposalVoteEnd
proposalProposer(uint256) returns address envfree
propose(address[], uint256[], bytes[], string) returns uint256 propose(address[], uint256[], bytes[], string) returns uint256
execute(address[], uint256[], bytes[], bytes32) returns uint256 execute(address[], uint256[], bytes[], bytes32) returns uint256
@ -50,7 +51,7 @@ methods {
*/ */
definition proposalCreated(uint256 pId) returns bool = definition proposalCreated(uint256 pId) returns bool =
proposalSnapshot(pId) > 0 && proposalDeadline(pId) > 0; proposalSnapshot(pId) > 0 && proposalDeadline(pId) > 0 && proposalProposer(pId) != 0;
/* /*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
@ -64,22 +65,22 @@ function helperFunctionsWithRevert(uint256 proposalId, method f, env e) {
if (f.selector == propose(address[], uint256[], bytes[], string).selector) if (f.selector == propose(address[], uint256[], bytes[], string).selector)
{ {
uint256 result = propose@withrevert(e, targets, values, calldatas, reason); uint256 result = propose@withrevert(e, targets, values, calldatas, reason);
require(result == proposalId); require result == proposalId;
} }
else if (f.selector == execute(address[], uint256[], bytes[], bytes32).selector) else if (f.selector == execute(address[], uint256[], bytes[], bytes32).selector)
{ {
uint256 result = execute@withrevert(e, targets, values, calldatas, descriptionHash); uint256 result = execute@withrevert(e, targets, values, calldatas, descriptionHash);
require(result == proposalId); require result == proposalId;
} }
else if (f.selector == queue(address[], uint256[], bytes[], bytes32).selector) else if (f.selector == queue(address[], uint256[], bytes[], bytes32).selector)
{ {
uint256 result = queue@withrevert(e, targets, values, calldatas, descriptionHash); uint256 result = queue@withrevert(e, targets, values, calldatas, descriptionHash);
require(result == proposalId); require result == proposalId;
} }
else if (f.selector == cancel(address[], uint256[], bytes[], bytes32).selector) else if (f.selector == cancel(address[], uint256[], bytes[], bytes32).selector)
{ {
uint256 result = cancel@withrevert(e, targets, values, calldatas, descriptionHash); uint256 result = cancel@withrevert(e, targets, values, calldatas, descriptionHash);
require(result == proposalId); require result == proposalId;
} }
else if (f.selector == castVote(uint256, uint8).selector) else if (f.selector == castVote(uint256, uint8).selector)
{ {
@ -117,8 +118,8 @@ function helperFunctionsWithRevert(uint256 proposalId, method f, env e) {
│ by the developers and will not be valid to raise proposals (at the current way that block chain is functioning) │ │ by the developers and will not be valid to raise proposals (at the current way that block chain is functioning) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
invariant startAndEndDatesNonZero(uint256 pId) invariant proposalStateConsistency(uint256 pId)
proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0 (proposalProposer(pId) != 0 <=> proposalSnapshot(pId) != 0) && (proposalProposer(pId) != 0 <=> proposalDeadline(pId) != 0)
{ {
preserved with (env e) { preserved with (env e) {
require e.block.number > 0; require e.block.number > 0;
@ -134,7 +135,7 @@ invariant canceledImplyCreated(uint pId)
isCanceled(pId) => proposalCreated(pId) isCanceled(pId) => proposalCreated(pId)
{ {
preserved with (env e) { preserved with (env e) {
requireInvariant startAndEndDatesNonZero(pId); requireInvariant proposalStateConsistency(pId);
require e.block.number > 0; require e.block.number > 0;
} }
} }
@ -148,7 +149,7 @@ invariant executedImplyCreated(uint pId)
isExecuted(pId) => proposalCreated(pId) isExecuted(pId) => proposalCreated(pId)
{ {
preserved with (env e) { preserved with (env e) {
requireInvariant startAndEndDatesNonZero(pId); requireInvariant proposalStateConsistency(pId);
require e.block.number > 0; require e.block.number > 0;
} }
} }
@ -159,11 +160,10 @@ invariant executedImplyCreated(uint pId)
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
invariant voteStartBeforeVoteEnd(uint256 pId) invariant voteStartBeforeVoteEnd(uint256 pId)
//proposalCreated(pId) => proposalSnapshot(pId) <= proposalDeadline(pId)
proposalSnapshot(pId) <= proposalDeadline(pId) proposalSnapshot(pId) <= proposalDeadline(pId)
{ {
preserved { preserved {
requireInvariant startAndEndDatesNonZero(pId); requireInvariant proposalStateConsistency(pId);
} }
} }
@ -177,18 +177,34 @@ invariant noBothExecutedAndCanceled(uint256 pId)
/* /*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: A proposal could be executed only if quorum was reached and vote succeeded │ │ Rule: No double proposition │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
rule canProposeOnlyOnce(uint256 pId, env e) {
require proposalCreated(pId);
address[] targets; uint256[] values; bytes[] calldatas; string reason;
uint256 result = propose(e, targets, values, calldatas, reason);
assert result != pId, "double proposal";
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: Once a proposal is created, voteStart, voteEnd and proposer are immutable │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f, calldataarg args) { rule immutableFieldsAfterProposalCreation(uint256 pId, env e, method f, calldataarg arg) {
require(!isExecuted(pId)); require proposalCreated(pId);
bool quorumReachedBefore = quorumReached(e, pId); uint256 voteStart = proposalSnapshot(pId);
bool voteSucceededBefore = voteSucceeded(pId); uint256 voteEnd = proposalDeadline(pId);
address proposer = proposalProposer(pId);
f(e, args); f(e, arg);
assert isExecuted(pId) => (quorumReachedBefore && voteSucceededBefore), "quorum was changed"; assert voteStart == proposalSnapshot(pId), "Start date was changed";
assert voteEnd == proposalDeadline(pId), "End date was changed";
assert proposer == proposalProposer(pId), "Proposer was changed";
} }
/* /*
@ -213,19 +229,18 @@ rule noDoubleVoting(uint256 pId, env e, uint8 sup) {
/* /*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: Once a proposal is created, voteStart and voteEnd are immutable │ Rule: A proposal could be executed only if quorum was reached and vote succeeded
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
rule immutableFieldsAfterProposalCreation(uint256 pId, env e, method f, calldataarg arg) { rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f, calldataarg args) {
require proposalCreated(pId); require !isExecuted(pId);
uint256 voteStart = proposalSnapshot(pId); bool quorumReachedBefore = quorumReached(e, pId);
uint256 voteEnd = proposalDeadline(pId); bool voteSucceededBefore = voteSucceeded(pId);
f(e, arg); f(e, args);
assert voteStart == proposalSnapshot(pId), "Start date was changed"; assert isExecuted(pId) => (quorumReachedBefore && voteSucceededBefore), "quorum was changed";
assert voteEnd == proposalDeadline(pId), "End date was changed";
} }
/* /*
@ -311,7 +326,7 @@ rule allFunctionsRevertIfCanceled(uint256 pId, env e, method f, calldataarg args
│ Rule: Proposal can be switched state only by specific functions │ │ Rule: Proposal can be switched state only by specific functions │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
rule proposedOnlyAfterProposeFunc(uint256 pId, env e, method f) { rule stateOnlyAfterFunc(uint256 pId, env e, method f) {
bool createdBefore = proposalCreated(pId); bool createdBefore = proposalCreated(pId);
bool executedBefore = isExecuted(pId); bool executedBefore = isExecuted(pId);
bool canceledBefore = isCanceled(pId); bool canceledBefore = isCanceled(pId);

Loading…
Cancel
Save