|
|
|
@ -3,6 +3,7 @@ |
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
|
|
|
|
|
|
using ERC721VotesHarness as erc721votes |
|
|
|
|
using ERC20VotesHarness as erc20votes |
|
|
|
|
|
|
|
|
|
methods { |
|
|
|
|
proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart |
|
|
|
@ -19,97 +20,101 @@ methods { |
|
|
|
|
quorumDenominator() returns uint256 envfree |
|
|
|
|
votingPeriod() returns uint256 envfree |
|
|
|
|
lateQuorumVoteExtension() returns uint64 envfree |
|
|
|
|
propose(address[], uint256[], bytes[], string) |
|
|
|
|
|
|
|
|
|
// harness |
|
|
|
|
getExtendedDeadlineIsUnset(uint256) returns bool envfree |
|
|
|
|
getExtendedDeadline(uint256) returns uint64 envfree |
|
|
|
|
quorumReached(uint256) returns bool envfree |
|
|
|
|
voteSucceeded(uint256) returns bool envfree |
|
|
|
|
quorum(uint256) returns uint256 |
|
|
|
|
latestCastVoteCall() returns uint256 envfree // more robust check than f.selector == _castVote(...).selector |
|
|
|
|
|
|
|
|
|
// function summarization |
|
|
|
|
proposalThreshold() returns uint256 envfree |
|
|
|
|
|
|
|
|
|
// erc20votes dispatch |
|
|
|
|
getVotes(address, uint256) returns uint256 => DISPATCHER(true) |
|
|
|
|
|
|
|
|
|
getPastTotalSupply(uint256 t) returns uint256 => PER_CALLEE_CONSTANT |
|
|
|
|
getPastVotes(address a, uint256 t) returns uint256 => PER_CALLEE_CONSTANT |
|
|
|
|
// erc721votes/Votes dispatch |
|
|
|
|
getPastTotalSupply(uint256) returns uint256 => DISPATCHER(true) |
|
|
|
|
getPastVotes(address, uint256) returns uint256 => DISPATCHER(true) |
|
|
|
|
// timelock dispatch |
|
|
|
|
getMinDelay() returns uint256 => DISPATCHER(true) |
|
|
|
|
|
|
|
|
|
hashOperationBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) |
|
|
|
|
executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => CONSTANT |
|
|
|
|
scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256) => CONSTANT |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
|
//////////////////////////////// Definitions ///////////////////////////////// |
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
|
|
|
|
|
|
// where can invariants help? |
|
|
|
|
// can I replace definitions with invariants? |
|
|
|
|
|
|
|
|
|
// create definition for extended |
|
|
|
|
definition deadlineCanBeExtended(uint256 id) returns bool = |
|
|
|
|
getExtendedDeadlineIsUnset(id) && |
|
|
|
|
getExtendedDeadline(id) == 0 && |
|
|
|
|
!quorumReached(id); |
|
|
|
|
definition deadlineCanBeExtended(uint256 pId) returns bool = |
|
|
|
|
getExtendedDeadlineIsUnset(pId) && |
|
|
|
|
!quorumReached(pId); |
|
|
|
|
|
|
|
|
|
definition deadlineHasBeenExtended(uint256 id) returns bool = |
|
|
|
|
!getExtendedDeadlineIsUnset(id) && |
|
|
|
|
getExtendedDeadline(id) > 0 && |
|
|
|
|
quorumReached(id); |
|
|
|
|
definition deadlineHasBeenExtended(uint256 pId) returns bool = |
|
|
|
|
!getExtendedDeadlineIsUnset(pId) && |
|
|
|
|
quorumReached(pId); |
|
|
|
|
|
|
|
|
|
definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0; |
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
|
////////////////////////////////// Rules ///////////////////////////////////// |
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
|
|
|
|
|
|
// RULE deadline can only be extended once |
|
|
|
|
// 1. if deadline changes then we have state transition from deadlineCanBeExtended to deadlineHasBeenExtended |
|
|
|
|
rule deadlineChangeEffects(method f) filtered {f -> !f.isView /* bottleneck, restrict for faster testing && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } { |
|
|
|
|
env e; calldataarg args; uint256 id; |
|
|
|
|
// RULE deadline can only be extended only once PASSING but vacuous |
|
|
|
|
// 1. if deadline changes then we have state transition to deadlineHasBeenExtended RULE PASSING; ADV SANITY PASSING |
|
|
|
|
rule deadlineChangeEffects(method f) |
|
|
|
|
filtered { |
|
|
|
|
f -> !f.isView |
|
|
|
|
} { |
|
|
|
|
env e; calldataarg args; uint256 pId; |
|
|
|
|
|
|
|
|
|
require (latestCastVoteCall() < e.block.number); |
|
|
|
|
require (quorumNumerator() <= quorumDenominator()); |
|
|
|
|
require deadlineCanBeExtended(id); |
|
|
|
|
require (proposalDeadline(id) > e.block.number |
|
|
|
|
&& proposalDeadline(id) >= proposalSnapshot(id) + votingPeriod() |
|
|
|
|
&& proposalSnapshot(id) < e.block.number); |
|
|
|
|
require (proposalCreated(pId)); |
|
|
|
|
|
|
|
|
|
uint256 deadlineBefore = proposalDeadline(id); |
|
|
|
|
uint256 deadlineBefore = proposalDeadline(pId); |
|
|
|
|
f(e, args); |
|
|
|
|
uint256 deadlineAfter = proposalDeadline(id); |
|
|
|
|
uint256 deadlineAfter = proposalDeadline(pId); |
|
|
|
|
|
|
|
|
|
assert(deadlineAfter > deadlineBefore => latestCastVoteCall() == e.block.number && deadlineHasBeenExtended(id)); |
|
|
|
|
assert(deadlineAfter > deadlineBefore => latestCastVoteCall() == e.block.number && deadlineHasBeenExtended(pId)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 2. cant unextend |
|
|
|
|
rule deadlineCantBeUnextended(method f) filtered {f -> !f.isView /* && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } { |
|
|
|
|
env e; calldataarg args; uint256 id; |
|
|
|
|
require(deadlineHasBeenExtended(id)); |
|
|
|
|
// 2. cant unextend RULE PASSING*; ADV SANITY PASSING |
|
|
|
|
rule deadlineCantBeUnextended(method f) |
|
|
|
|
filtered { |
|
|
|
|
f -> !f.isView |
|
|
|
|
&& f.selector != updateQuorumNumerator(uint256).selector // * fails for this function |
|
|
|
|
} { |
|
|
|
|
env e; calldataarg args; uint256 pId; |
|
|
|
|
|
|
|
|
|
require(deadlineHasBeenExtended(pId)); |
|
|
|
|
require(proposalCreated(pId)); |
|
|
|
|
|
|
|
|
|
f(e, args); |
|
|
|
|
assert(deadlineHasBeenExtended(id)); |
|
|
|
|
|
|
|
|
|
assert(deadlineHasBeenExtended(pId)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 3. extended => can't change deadline |
|
|
|
|
// 3. extended => can't change deadline RULE PASSING; ADV SANITY PASSING |
|
|
|
|
//@note if deadline changed, then it wasnt extended and castvote was called |
|
|
|
|
rule canExtendDeadlineOnce(method f) filtered {f -> !f.isView /* && f.selector != propose(address[], uint256[], bytes[], string).selector*/ } { |
|
|
|
|
env e; calldataarg args; |
|
|
|
|
uint256 id; |
|
|
|
|
require(deadlineHasBeenExtended(id)); // stays true |
|
|
|
|
require (proposalDeadline(id) > e.block.number |
|
|
|
|
&& proposalDeadline(id) >= proposalSnapshot(id) + votingPeriod() |
|
|
|
|
&& proposalSnapshot(id) < e.block.number); |
|
|
|
|
uint256 deadlineBefore = proposalDeadline(id); |
|
|
|
|
rule canExtendDeadlineOnce(method f) |
|
|
|
|
filtered { |
|
|
|
|
f -> !f.isView |
|
|
|
|
} { |
|
|
|
|
env e; calldataarg args; uint256 pId; |
|
|
|
|
|
|
|
|
|
require(deadlineHasBeenExtended(pId)); |
|
|
|
|
require(proposalCreated(pId)); |
|
|
|
|
|
|
|
|
|
uint256 deadlineBefore = proposalDeadline(pId); |
|
|
|
|
f(e, args); |
|
|
|
|
uint256 deadlineAfter = proposalDeadline(id); |
|
|
|
|
uint256 deadlineAfter = proposalDeadline(pId); |
|
|
|
|
|
|
|
|
|
assert(deadlineBefore == deadlineAfter, "deadline can not be extended twice"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// RULE deadline can only extended if quorum reached w/ <= timeOfExtension left to vote |
|
|
|
|
// 3 rules |
|
|
|
|
// 1. voting increases total votes |
|
|
|
|
// 2. number of votes > quorum => quorum reached |
|
|
|
|
// 3. deadline can only extended if quorum reached w/ <= timeOfExtension left to vote |
|
|
|
|
rule deadlineCanOnlyBeExtenededIfQuorumReached() { |
|
|
|
|
env e; method f; calldataarg args; |
|
|
|
|
uint256 id; |
|
|
|
|
require(getExtendedDeadlineIsUnset(id)); |
|
|
|
|
f(e, args); |
|
|
|
|
assert(false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// RULE extendedDeadline is used iff quorum is reached w/ <= extensionTime left to vote |
|
|
|
|
|
|
|
|
|
// RULE extendedDeadlineField is set iff quroum is reached |
|
|
|
|
|
|
|
|
|
// RULE if the deadline/extendedDeadline has not been reached, you can still vote (base) |