|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////// THIS SPEC IS A RESERVE FOR NOT IN PROGRESS //////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
import "GovernorBase.spec"
|
|
|
|
|
|
|
|
using ERC20VotesHarness as erc20votes
|
|
|
|
|
|
|
|
methods {
|
|
|
|
ghost_sum_vote_power_by_id(uint256) returns uint256 envfree
|
|
|
|
|
|
|
|
quorum(uint256) returns uint256
|
|
|
|
proposalVotes(uint256) returns (uint256, uint256, uint256) envfree
|
|
|
|
|
|
|
|
quorumNumerator() returns uint256
|
|
|
|
_executor() returns address
|
|
|
|
|
|
|
|
erc20votes._getPastVotes(address, uint256) returns uint256
|
|
|
|
|
|
|
|
getExecutor() returns address
|
|
|
|
|
|
|
|
timelock() returns address
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////// GHOSTS /////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
//////////// ghosts to keep track of votes counting ////////////
|
|
|
|
|
|
|
|
/*
|
|
|
|
* the sum of voting power of those who voted
|
|
|
|
*/
|
|
|
|
ghost sum_all_votes_power() returns uint256 {
|
|
|
|
init_state axiom sum_all_votes_power() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE {
|
|
|
|
havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sum of all votes casted per proposal
|
|
|
|
*/
|
|
|
|
ghost tracked_weight(uint256) returns uint256 {
|
|
|
|
init_state axiom forall uint256 p. tracked_weight(p) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sum of all votes casted
|
|
|
|
*/
|
|
|
|
ghost sum_tracked_weight() returns uint256 {
|
|
|
|
init_state axiom sum_tracked_weight() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* getter for _proposalVotes.againstVotes
|
|
|
|
*/
|
|
|
|
ghost votesAgainst() returns uint256 {
|
|
|
|
init_state axiom votesAgainst() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* getter for _proposalVotes.forVotes
|
|
|
|
*/
|
|
|
|
ghost votesFor() returns uint256 {
|
|
|
|
init_state axiom votesFor() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* getter for _proposalVotes.abstainVotes
|
|
|
|
*/
|
|
|
|
ghost votesAbstain() returns uint256 {
|
|
|
|
init_state axiom votesAbstain() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
hook Sstore _proposalVotes [KEY uint256 pId].againstVotes uint256 votes(uint256 old_votes) STORAGE {
|
|
|
|
havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) &&
|
|
|
|
(p != pId => tracked_weight@new(p) == tracked_weight@old(p));
|
|
|
|
havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes;
|
|
|
|
havoc votesAgainst assuming votesAgainst@new() == votesAgainst@old() - old_votes + votes;
|
|
|
|
}
|
|
|
|
|
|
|
|
hook Sstore _proposalVotes [KEY uint256 pId].forVotes uint256 votes(uint256 old_votes) STORAGE {
|
|
|
|
havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) &&
|
|
|
|
(p != pId => tracked_weight@new(p) == tracked_weight@old(p));
|
|
|
|
havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes;
|
|
|
|
havoc votesFor assuming votesFor@new() == votesFor@old() - old_votes + votes;
|
|
|
|
}
|
|
|
|
|
|
|
|
hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 old_votes) STORAGE {
|
|
|
|
havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) &&
|
|
|
|
(p != pId => tracked_weight@new(p) == tracked_weight@old(p));
|
|
|
|
havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes;
|
|
|
|
havoc votesAbstain assuming votesAbstain@new() == votesAbstain@old() - old_votes + votes;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////// INVARIANTS ////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////// RULES //////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
//NOT FINISHED
|
|
|
|
/*
|
|
|
|
* the sum of voting power of those who voted is less or equal to the maximum possible votes, per each proposal
|
|
|
|
*/
|
|
|
|
rule possibleTotalVotes(uint256 pId, uint8 sup, env e, method f) {
|
|
|
|
|
|
|
|
// add requireinvariant for all i, j. i = i - 1 && i < j => checkpointlookup[i] < checkpointlookup[j];
|
|
|
|
require tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId));
|
|
|
|
|
|
|
|
uint256 againstB;
|
|
|
|
uint256 forB;
|
|
|
|
uint256 absatinB;
|
|
|
|
againstB, forB, absatinB = proposalVotes(pId);
|
|
|
|
|
|
|
|
calldataarg args;
|
|
|
|
//f(e, args);
|
|
|
|
|
|
|
|
castVote(e, pId, sup);
|
|
|
|
|
|
|
|
uint256 against;
|
|
|
|
uint256 for;
|
|
|
|
uint256 absatin;
|
|
|
|
against, for, absatin = proposalVotes(pId);
|
|
|
|
|
|
|
|
uint256 ps = proposalSnapshot(pId);
|
|
|
|
|
|
|
|
assert tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)), "bla bla bla";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////// 2nd iteration with OZ //////////////////////////
|
|
|
|
|
|
|
|
function executionsCall(method f, env e, address target, uint256 value, bytes data,
|
|
|
|
bytes32 predecessor, bytes32 salt, uint256 delay,
|
|
|
|
address[] targets, uint256[] values, bytes[] datas) {
|
|
|
|
if (f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector) {
|
|
|
|
execute(e, target, value, data, predecessor, salt);
|
|
|
|
} else if (f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector) {
|
|
|
|
executeBatch(e, targets, values, datas, predecessor, salt);
|
|
|
|
} else {
|
|
|
|
calldataarg args;
|
|
|
|
f(e, args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// STATUS - in progress
|
|
|
|
// execute() is the only way to set timestamp to 1
|
|
|
|
rule getTimestampOnlyChange(method f, env e){
|
|
|
|
bytes32 id;
|
|
|
|
address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt; uint256 delay;
|
|
|
|
address[] targets; uint256[] values; bytes[] datas;
|
|
|
|
|
|
|
|
require (targets[0] == target && values[0] == value && datas[0] == data)
|
|
|
|
|| (targets[1] == target && values[1] == value && datas[1] == data)
|
|
|
|
|| (targets[2] == target && values[2] == value && datas[2] == data);
|
|
|
|
|
|
|
|
hashIdCorrelation(id, target, value, data, predecessor, salt);
|
|
|
|
|
|
|
|
executionsCall(f, e, target, value, data, predecessor, salt, delay, targets, values, datas);
|
|
|
|
|
|
|
|
assert getTimestamp(id) == 1 => f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector
|
|
|
|
|| f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector, "Did you find a way to break the system?";
|
|
|
|
}
|
|
|
|
|