Files
openzeppelin-contracts/certora/specs/GovernorInvariants.spec
2023-05-03 17:52:59 +02:00

154 lines
11 KiB
Ruby

import "helpers/helpers.spec"
import "helpers/Governor.helpers.spec"
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Invariant: clock is consistent between the goernor and the token │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule clockMode(env e) {
require clockSanity(e);
assert clock(e) == e.block.number || clock(e) == e.block.timestamp;
assert clock(e) == token_clock(e);
/// This causes a failure in the prover
// assert CLOCK_MODE(e) == token_CLOCK_MODE(e);
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Invariant: Votes start and end are either initialized (non zero) or uninitialized (zero) simultaneously │
│ │
│ This invariant assumes that the block number cannot be 0 at any stage of the contract cycle │
│ This is very safe assumption as usually the 0 block is genesis block which is uploaded with data │
│ by the developers and will not be valid to raise proposals (at the current way that block chain is functioning) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
invariant proposalStateConsistency(uint256 pId)
(proposalProposer(pId) == 0 <=> proposalSnapshot(pId) == 0) &&
(proposalProposer(pId) == 0 <=> proposalDeadline(pId) == 0)
{
preserved with (env e) {
require clockSanity(e);
}
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Invariant: votes recorded => created │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
invariant votesImplyCreated(uint256 pId)
(
getAgainstVotes(pId) > 0 ||
getForVotes(pId) > 0 ||
getAbstainVotes(pId) > 0
) => proposalCreated(pId)
{
preserved with (env e) {
require clockSanity(e);
requireInvariant proposalStateConsistency(pId);
}
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Invariant: cancel => created │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
invariant canceledImplyCreated(uint pId)
isCanceled(pId) => proposalCreated(pId)
{
preserved with (env e) {
require clockSanity(e);
requireInvariant proposalStateConsistency(pId);
}
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Invariant: executed => created │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
invariant executedImplyCreated(uint pId)
isExecuted(pId) => proposalCreated(pId)
{
preserved with (env e) {
require clockSanity(e);
requireInvariant proposalStateConsistency(pId);
}
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Invariant: queued => created │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
invariant queuedImplyCreated(uint pId)
isQueued(pId) => proposalCreated(pId)
{
preserved with (env e) {
require clockSanity(e);
requireInvariant proposalStateConsistency(pId);
}
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Invariant: timings │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
invariant votesImplySnapshotPassed(env e, uint256 pId)
(
getAgainstVotes(pId) > 0 ||
getForVotes(pId) > 0 ||
getAbstainVotes(pId) > 0
) => proposalSnapshot(pId) < clock(e)
{
preserved with (env e2) {
// In this invariant, `env e` is representing the present. And `clock(e)` the current timestamp.
// It should hold for any transitions in the pasts
require clock(e2) <= clock(e);
}
}
invariant queuedImplyDeadlineOver(env e, uint pId)
isQueued(pId) => proposalDeadline(pId) < clock(e)
{
preserved {
require clockSanity(e);
requireInvariant proposalStateConsistency(pId);
}
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Invariant: Votes start before it ends │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
invariant voteStartBeforeVoteEnd(uint256 pId)
proposalSnapshot(pId) <= proposalDeadline(pId)
{
preserved {
requireInvariant proposalStateConsistency(pId);
}
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Invariant: A proposal cannot be both executed and canceled simultaneously │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
invariant noBothExecutedAndCanceled(uint256 pId)
!isExecuted(pId) || !isCanceled(pId)
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Invariant: the "governance call" dequeue is empty │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
invariant governanceCallLength()
governanceCallLength() == 0