start work on governor
This commit is contained in:
29
certora/harnesses/GovernorCountingSimpleHarness.sol
Normal file
29
certora/harnesses/GovernorCountingSimpleHarness.sol
Normal file
@ -0,0 +1,29 @@
|
||||
import "../../contracts/governance/extensions/GovernorCountingSimple.sol";
|
||||
|
||||
contract GovernorCountingSimpleHarness is GovernorCountingSimple {
|
||||
|
||||
mapping(uint256 => uint256) _quorum;
|
||||
|
||||
function quorum(uint256 blockNumber) public view override virtual returns (uint256) {
|
||||
return _quorum[blockNumber];
|
||||
}
|
||||
|
||||
mapping (address => mapping (uint256 => uint256)) _getVotes;
|
||||
|
||||
function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) {
|
||||
return _getVotes[account][blockNumber];
|
||||
}
|
||||
|
||||
uint256 _votingDelay;
|
||||
function votingDelay() public view override virtual returns (uint256) {
|
||||
return _votingDelay;
|
||||
}
|
||||
|
||||
uint256 _votingPeriod;
|
||||
function votingPeriod() public view override virtual returns (uint256) {
|
||||
return _votingPeriod;
|
||||
}
|
||||
|
||||
constructor(string memory name) Governor(name) {}
|
||||
|
||||
}
|
||||
58
certora/harnesses/GovernorHarness.sol
Normal file
58
certora/harnesses/GovernorHarness.sol
Normal file
@ -0,0 +1,58 @@
|
||||
import "../../contracts/governance/Governor.sol";
|
||||
|
||||
contract GovernorHarness is Governor {
|
||||
|
||||
mapping(uint256 => uint256) _quorum;
|
||||
|
||||
function quorum(uint256 blockNumber) public view override virtual returns (uint256) {
|
||||
return _quorum[blockNumber];
|
||||
}
|
||||
|
||||
mapping (address => mapping (uint256 => uint256)) _getVotes;
|
||||
|
||||
function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) {
|
||||
return _getVotes[account][blockNumber];
|
||||
}
|
||||
|
||||
mapping (uint256 => bool) __quoromReached;
|
||||
function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) {
|
||||
return __quoromReached[proposalId];
|
||||
}
|
||||
|
||||
mapping (uint256 => bool) __voteSucceeded;
|
||||
function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) {
|
||||
return __voteSucceeded[proposalId];
|
||||
}
|
||||
|
||||
//string _COUNTING_MODE;
|
||||
function COUNTING_MODE() public pure override virtual returns (string memory) {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
mapping(uint256 => mapping(address => bool)) _hasVoted;
|
||||
function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) {
|
||||
return _hasVoted[proposalId][account];
|
||||
}
|
||||
|
||||
uint256 _votingDelay;
|
||||
function votingDelay() public view override virtual returns (uint256) {
|
||||
return _votingDelay;
|
||||
}
|
||||
|
||||
uint256 _votingPeriod;
|
||||
function votingPeriod() public view override virtual returns (uint256) {
|
||||
return _votingPeriod;
|
||||
}
|
||||
|
||||
function _countVote(
|
||||
uint256 proposalId,
|
||||
address account,
|
||||
uint8 support,
|
||||
uint256 weight
|
||||
) internal override virtual {
|
||||
// havoc something
|
||||
}
|
||||
|
||||
constructor(string memory name) Governor(name) {}
|
||||
|
||||
}
|
||||
58
certora/harnesses/GovernorProposalThresholdHarness.sol
Normal file
58
certora/harnesses/GovernorProposalThresholdHarness.sol
Normal file
@ -0,0 +1,58 @@
|
||||
import "../../contracts/governance/extensions/GovernorProposalThreshold.sol";
|
||||
|
||||
contract GovernorProposalThresholdHarness is GovernorProposalThreshold {
|
||||
|
||||
mapping(uint256 => uint256) _quorum;
|
||||
|
||||
function quorum(uint256 blockNumber) public view override virtual returns (uint256) {
|
||||
return _quorum[blockNumber];
|
||||
}
|
||||
|
||||
mapping (address => mapping (uint256 => uint256)) _getVotes;
|
||||
|
||||
function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) {
|
||||
return _getVotes[account][blockNumber];
|
||||
}
|
||||
|
||||
mapping (uint256 => bool) __quoromReached;
|
||||
function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) {
|
||||
return __quoromReached[proposalId];
|
||||
}
|
||||
|
||||
mapping (uint256 => bool) __voteSucceeded;
|
||||
function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) {
|
||||
return __voteSucceeded[proposalId];
|
||||
}
|
||||
|
||||
//string _COUNTING_MODE;
|
||||
function COUNTING_MODE() public pure override virtual returns (string memory) {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
mapping(uint256 => mapping(address => bool)) _hasVoted;
|
||||
function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) {
|
||||
return _hasVoted[proposalId][account];
|
||||
}
|
||||
|
||||
uint256 _votingDelay;
|
||||
function votingDelay() public view override virtual returns (uint256) {
|
||||
return _votingDelay;
|
||||
}
|
||||
|
||||
uint256 _votingPeriod;
|
||||
function votingPeriod() public view override virtual returns (uint256) {
|
||||
return _votingPeriod;
|
||||
}
|
||||
|
||||
function _countVote(
|
||||
uint256 proposalId,
|
||||
address account,
|
||||
uint8 support,
|
||||
uint256 weight
|
||||
) internal override virtual {
|
||||
// havoc something
|
||||
}
|
||||
|
||||
constructor(string memory name) Governor(name) {}
|
||||
|
||||
}
|
||||
58
certora/harnesses/GovernorTimelockCompoundHarness.sol
Normal file
58
certora/harnesses/GovernorTimelockCompoundHarness.sol
Normal file
@ -0,0 +1,58 @@
|
||||
import "../../contracts/governance/extensions/GovernorTimelockCompound.sol";
|
||||
|
||||
contract GovernorTimelockCompoundHarness is GovernorTimelockCompound {
|
||||
|
||||
mapping(uint256 => uint256) _quorum;
|
||||
|
||||
function quorum(uint256 blockNumber) public view override virtual returns (uint256) {
|
||||
return _quorum[blockNumber];
|
||||
}
|
||||
|
||||
mapping (address => mapping (uint256 => uint256)) _getVotes;
|
||||
|
||||
function getVotes(address account, uint256 blockNumber) public view override virtual returns (uint256) {
|
||||
return _getVotes[account][blockNumber];
|
||||
}
|
||||
|
||||
mapping (uint256 => bool) __quoromReached;
|
||||
function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) {
|
||||
return __quoromReached[proposalId];
|
||||
}
|
||||
|
||||
mapping (uint256 => bool) __voteSucceeded;
|
||||
function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) {
|
||||
return __voteSucceeded[proposalId];
|
||||
}
|
||||
|
||||
//string _COUNTING_MODE;
|
||||
function COUNTING_MODE() public pure override virtual returns (string memory) {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
mapping(uint256 => mapping(address => bool)) _hasVoted;
|
||||
function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) {
|
||||
return _hasVoted[proposalId][account];
|
||||
}
|
||||
|
||||
uint256 _votingDelay;
|
||||
function votingDelay() public view override virtual returns (uint256) {
|
||||
return _votingDelay;
|
||||
}
|
||||
|
||||
uint256 _votingPeriod;
|
||||
function votingPeriod() public view override virtual returns (uint256) {
|
||||
return _votingPeriod;
|
||||
}
|
||||
|
||||
function _countVote(
|
||||
uint256 proposalId,
|
||||
address account,
|
||||
uint8 support,
|
||||
uint256 weight
|
||||
) internal override virtual {
|
||||
// havoc something
|
||||
}
|
||||
|
||||
constructor(string memory name, ICompoundTimelock timelock) Governor(name) GovernorTimelockCompound(timelock) {}
|
||||
|
||||
}
|
||||
52
certora/harnesses/GovernorVotesHarness.sol
Normal file
52
certora/harnesses/GovernorVotesHarness.sol
Normal file
@ -0,0 +1,52 @@
|
||||
import "../../contracts/governance/extensions/GovernorVotes.sol";
|
||||
|
||||
contract GovernorVotesHarness is GovernorVotes {
|
||||
|
||||
mapping(uint256 => uint256) _quorum;
|
||||
|
||||
function quorum(uint256 blockNumber) public view override virtual returns (uint256) {
|
||||
return _quorum[blockNumber];
|
||||
}
|
||||
|
||||
mapping (uint256 => bool) __quoromReached;
|
||||
function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) {
|
||||
return __quoromReached[proposalId];
|
||||
}
|
||||
|
||||
mapping (uint256 => bool) __voteSucceeded;
|
||||
function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) {
|
||||
return __voteSucceeded[proposalId];
|
||||
}
|
||||
|
||||
//string _COUNTING_MODE;
|
||||
function COUNTING_MODE() public pure override virtual returns (string memory) {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
mapping(uint256 => mapping(address => bool)) _hasVoted;
|
||||
function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) {
|
||||
return _hasVoted[proposalId][account];
|
||||
}
|
||||
|
||||
uint256 _votingDelay;
|
||||
function votingDelay() public view override virtual returns (uint256) {
|
||||
return _votingDelay;
|
||||
}
|
||||
|
||||
uint256 _votingPeriod;
|
||||
function votingPeriod() public view override virtual returns (uint256) {
|
||||
return _votingPeriod;
|
||||
}
|
||||
|
||||
function _countVote(
|
||||
uint256 proposalId,
|
||||
address account,
|
||||
uint8 support,
|
||||
uint256 weight
|
||||
) internal override virtual {
|
||||
// havoc something
|
||||
}
|
||||
|
||||
constructor(string memory name) Governor(name) {}
|
||||
|
||||
}
|
||||
46
certora/harnesses/GovernorVotesQuorumFractionHarness.sol
Normal file
46
certora/harnesses/GovernorVotesQuorumFractionHarness.sol
Normal file
@ -0,0 +1,46 @@
|
||||
import "../../contracts/governance/extensions/GovernorVotesQuorumFractionGovernor.sol";
|
||||
|
||||
contract GovernorVotesQuorumFractionHarness is GovernorVotesQuorumFraction {
|
||||
|
||||
mapping (uint256 => bool) __quoromReached;
|
||||
function _quorumReached(uint256 proposalId) internal view override virtual returns (bool) {
|
||||
return __quoromReached[proposalId];
|
||||
}
|
||||
|
||||
mapping (uint256 => bool) __voteSucceeded;
|
||||
function _voteSucceeded(uint256 proposalId) internal view override virtual returns (bool) {
|
||||
return __voteSucceeded[proposalId];
|
||||
}
|
||||
|
||||
//string _COUNTING_MODE;
|
||||
function COUNTING_MODE() public pure override virtual returns (string memory) {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
mapping(uint256 => mapping(address => bool)) _hasVoted;
|
||||
function hasVoted(uint256 proposalId, address account) public view override virtual returns (bool) {
|
||||
return _hasVoted[proposalId][account];
|
||||
}
|
||||
|
||||
uint256 _votingDelay;
|
||||
function votingDelay() public view override virtual returns (uint256) {
|
||||
return _votingDelay;
|
||||
}
|
||||
|
||||
uint256 _votingPeriod;
|
||||
function votingPeriod() public view override virtual returns (uint256) {
|
||||
return _votingPeriod;
|
||||
}
|
||||
|
||||
function _countVote(
|
||||
uint256 proposalId,
|
||||
address account,
|
||||
uint8 support,
|
||||
uint256 weight
|
||||
) internal override virtual {
|
||||
// havoc something
|
||||
}
|
||||
|
||||
constructor(string memory name) Governor(name) {}
|
||||
|
||||
}
|
||||
2
certora/scripts/Governor.sh
Executable file
2
certora/scripts/Governor.sh
Executable file
@ -0,0 +1,2 @@
|
||||
certoraRun certora/harnesses/GovernorHarness.sol \
|
||||
--verify GovernorHarness:certora/specs/Privileged.spec
|
||||
2
certora/scripts/GovernorCountingSimple.sh
Executable file
2
certora/scripts/GovernorCountingSimple.sh
Executable file
@ -0,0 +1,2 @@
|
||||
certoraRun certora/harnesses/GovernorCountingSimpleHarness.sol \
|
||||
--verify GovernorCountingSimpleHarness:certora/specs/Privileged.spec
|
||||
2
certora/scripts/GovernorProposalThreshold.sh
Executable file
2
certora/scripts/GovernorProposalThreshold.sh
Executable file
@ -0,0 +1,2 @@
|
||||
certoraRun certora/harnesses/GovernorProposalThresholdHarness.sol \
|
||||
--verify GovernorProposalThresholdHarness:certora/specs/Privileged.spec
|
||||
2
certora/scripts/GovernorTimelockCompound.sh
Executable file
2
certora/scripts/GovernorTimelockCompound.sh
Executable file
@ -0,0 +1,2 @@
|
||||
certoraRun certora/harnesses/GovernorTimelockCompoundHarness.sol \
|
||||
--verify GovernorTimelockCompoundHarness:certora/specs/Privileged.spec
|
||||
2
certora/scripts/GovernorVotes.sh
Executable file
2
certora/scripts/GovernorVotes.sh
Executable file
@ -0,0 +1,2 @@
|
||||
certoraRun certora/harnesses/GovernorVotesHarness.sol \
|
||||
--verify GovernorVotesHarness:certora/specs/Privileged.spec
|
||||
2
certora/scripts/GovernorVotesQuorumFractionHarness.sh
Executable file
2
certora/scripts/GovernorVotesQuorumFractionHarness.sh
Executable file
@ -0,0 +1,2 @@
|
||||
certoraRun certora/harnesses/GovernorVotesQuorumFractionHarness.sol \
|
||||
--verify GovernorVotesQuorumFractionHarness:certora/specs/Privileged.spec
|
||||
7
certora/scripts/check.sh
Executable file
7
certora/scripts/check.sh
Executable file
@ -0,0 +1,7 @@
|
||||
echo "Usage: Contract Spec"
|
||||
echo "e.g. GovernorVotes Privileged"
|
||||
Contract=$1
|
||||
Spec=$2
|
||||
shift 2
|
||||
certoraRun certora/harnesses/${Contract}Harness.sol \
|
||||
--verify ${Contract}Harness:certora/specs/${Spec}.spec "$@"
|
||||
79
certora/specs/GovernorBase.spec
Normal file
79
certora/specs/GovernorBase.spec
Normal file
@ -0,0 +1,79 @@
|
||||
// Governor.sol base definitions
|
||||
methods {
|
||||
proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart
|
||||
}
|
||||
ghost proposalVoteStart(uint256) returns uint64 {
|
||||
init_state axiom forall uint256 pId. proposalVoteStart(pId) == 0;
|
||||
}
|
||||
ghost proposalVoteEnd(uint256) returns uint64 {
|
||||
init_state axiom forall uint256 pId. proposalVoteEnd(pId) == 0;
|
||||
}
|
||||
ghost proposalExecuted(uint256) returns bool {
|
||||
init_state axiom forall uint256 pId. !proposalExecuted(pId);
|
||||
}
|
||||
ghost proposalCanceled(uint256) returns bool {
|
||||
init_state axiom forall uint256 pId. !proposalCanceled(pId);
|
||||
}
|
||||
|
||||
hook Sstore _proposals[KEY uint256 pId].(offset 0) uint64 newValue STORAGE {
|
||||
havoc proposalVoteStart assuming (
|
||||
proposalVoteStart@new(pId) == newValue
|
||||
&& (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2))
|
||||
);
|
||||
}
|
||||
|
||||
hook Sload uint64 value _proposals[KEY uint256 pId].(offset 0) STORAGE {
|
||||
require proposalVoteStart(pId) == value;
|
||||
}
|
||||
|
||||
|
||||
hook Sstore _proposals[KEY uint256 pId].(offset 32).(offset 0) uint64 newValue STORAGE {
|
||||
havoc proposalVoteEnd assuming (
|
||||
proposalVoteEnd@new(pId) == newValue
|
||||
&& (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2))
|
||||
);
|
||||
}
|
||||
|
||||
hook Sload uint64 value _proposals[KEY uint256 pId].(offset 32).(offset 0) STORAGE {
|
||||
require proposalVoteEnd(pId) == value;
|
||||
}
|
||||
|
||||
rule sanityCheckVoteStart(method f, uint256 pId) {
|
||||
uint64 preGhost = proposalVoteStart(pId);
|
||||
uint256 pre = proposalSnapshot(pId);
|
||||
|
||||
env e;
|
||||
calldataarg arg;
|
||||
f(e, arg);
|
||||
|
||||
uint64 postGhost = proposalVoteStart(pId);
|
||||
uint256 post = proposalSnapshot(pId);
|
||||
|
||||
assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes";
|
||||
assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end";
|
||||
}
|
||||
|
||||
rule sanityCheckVoteEnd(method f, uint256 pId) {
|
||||
uint64 preGhost = proposalVoteEnd(pId);
|
||||
uint256 pre = proposalSnapshot(pId);
|
||||
|
||||
env e;
|
||||
calldataarg arg;
|
||||
f(e, arg);
|
||||
|
||||
uint64 postGhost = proposalVoteEnd(pId);
|
||||
uint256 post = proposalSnapshot(pId);
|
||||
|
||||
assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes";
|
||||
assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end";
|
||||
}
|
||||
|
||||
/**
|
||||
* A proposal cannot end unless it started.
|
||||
*/
|
||||
invariant voteStartBeforeVoteEnd(uint256 pId) proposalVoteStart(pId) < proposalVoteEnd(pId)
|
||||
|
||||
/**
|
||||
* A proposal cannot be both executed and canceled.
|
||||
*/
|
||||
invariant noBothExecutedAndCanceled(uint256 pId) !proposalExecuted(pId) || !proposalCanceled(pId)
|
||||
31
certora/specs/Privileged.spec
Normal file
31
certora/specs/Privileged.spec
Normal file
@ -0,0 +1,31 @@
|
||||
definition knownAsNonPrivileged(method f) returns bool = false
|
||||
/* ( f.selector == isWhitelistedOtoken(address).selector ||
|
||||
f.selector == isWhitelistedProduct(address,address,address,bool).selector ||
|
||||
f.selector == owner().selector ||
|
||||
f.selector == isWhitelistedCallee(address).selector ||
|
||||
f.selector == whitelistOtoken(address).selector ||
|
||||
f.selector == addressBook().selector ||
|
||||
f.selector == isWhitelistedCollateral(address).selector )*/;
|
||||
|
||||
|
||||
|
||||
rule privilegedOperation(method f, address privileged)
|
||||
description "$f can be called by more than one user without reverting"
|
||||
{
|
||||
env e1;
|
||||
calldataarg arg;
|
||||
require !knownAsNonPrivileged(f);
|
||||
require e1.msg.sender == privileged;
|
||||
|
||||
storage initialStorage = lastStorage;
|
||||
invoke f(e1, arg); // privileged succeeds executing candidate privileged operation.
|
||||
bool firstSucceeded = !lastReverted;
|
||||
|
||||
env e2;
|
||||
calldataarg arg2;
|
||||
require e2.msg.sender != privileged;
|
||||
invoke f(e2, arg2) at initialStorage; // unprivileged
|
||||
bool secondSucceeded = !lastReverted;
|
||||
|
||||
assert !(firstSucceeded && secondSucceeded), "${f.selector} can be called by both ${e1.msg.sender} and ${e2.msg.sender}, so it is not privileged";
|
||||
}
|
||||
Reference in New Issue
Block a user