parent
0874adbd1f
commit
4ea73a8c05
@ -0,0 +1,14 @@ |
|||||||
|
--- governance/extensions/GovernorPreventLateQuorum.sol 2023-03-07 10:48:47.733488857 +0100
|
||||||
|
+++ governance/extensions/GovernorPreventLateQuorum.sol 2023-03-15 14:14:59.121060484 +0100
|
||||||
|
@@ -84,6 +84,11 @@
|
||||||
|
return _voteExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ // FV
|
||||||
|
+ function _getExtendedDeadline(uint256 proposalId) internal view returns (uint64) {
|
||||||
|
+ return _extendedDeadlines[proposalId];
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* @dev Changes the {lateQuorumVoteExtension}. This operation can only be performed by the governance executor,
|
||||||
|
* generally through a governance proposal.
|
@ -0,0 +1,176 @@ |
|||||||
|
// SPDX-License-Identifier: MIT |
||||||
|
pragma solidity ^0.8.2; |
||||||
|
|
||||||
|
import "../patched/governance/Governor.sol"; |
||||||
|
import "../patched/governance/extensions/GovernorCountingSimple.sol"; |
||||||
|
import "../patched/governance/extensions/GovernorPreventLateQuorum.sol"; |
||||||
|
import "../patched/governance/extensions/GovernorTimelockControl.sol"; |
||||||
|
import "../patched/governance/extensions/GovernorVotes.sol"; |
||||||
|
import "../patched/governance/extensions/GovernorVotesQuorumFraction.sol"; |
||||||
|
import "../patched/token/ERC20/extensions/ERC20Votes.sol"; |
||||||
|
|
||||||
|
contract GovernorPreventLateHarness is |
||||||
|
Governor, |
||||||
|
GovernorCountingSimple, |
||||||
|
GovernorPreventLateQuorum, |
||||||
|
GovernorTimelockControl, |
||||||
|
GovernorVotes, |
||||||
|
GovernorVotesQuorumFraction |
||||||
|
{ |
||||||
|
constructor(IVotes _token, TimelockController _timelock, uint256 _quorumNumeratorValue, uint64 _initialVoteExtension) |
||||||
|
Governor("Harness") |
||||||
|
GovernorPreventLateQuorum(_initialVoteExtension) |
||||||
|
GovernorTimelockControl(_timelock) |
||||||
|
GovernorVotes(_token) |
||||||
|
GovernorVotesQuorumFraction(_quorumNumeratorValue) |
||||||
|
{} |
||||||
|
|
||||||
|
// Harness from Votes |
||||||
|
function token_getPastTotalSupply(uint256 blockNumber) public view returns(uint256) { |
||||||
|
return token.getPastTotalSupply(blockNumber); |
||||||
|
} |
||||||
|
|
||||||
|
function token_getPastVotes(address account, uint256 blockNumber) public view returns(uint256) { |
||||||
|
return token.getPastVotes(account, blockNumber); |
||||||
|
} |
||||||
|
|
||||||
|
function token_clock() public view returns (uint48) { |
||||||
|
return token.clock(); |
||||||
|
} |
||||||
|
|
||||||
|
function token_CLOCK_MODE() public view returns (string memory) { |
||||||
|
return token.CLOCK_MODE(); |
||||||
|
} |
||||||
|
|
||||||
|
// Harness from Governor |
||||||
|
function getExecutor() public view returns (address) { |
||||||
|
return _executor(); |
||||||
|
} |
||||||
|
|
||||||
|
function proposalProposer(uint256 proposalId) public view returns (address) { |
||||||
|
return _proposalProposer(proposalId); |
||||||
|
} |
||||||
|
|
||||||
|
function quorumReached(uint256 proposalId) public view returns (bool) { |
||||||
|
return _quorumReached(proposalId); |
||||||
|
} |
||||||
|
|
||||||
|
function voteSucceeded(uint256 proposalId) public view returns (bool) { |
||||||
|
return _voteSucceeded(proposalId); |
||||||
|
} |
||||||
|
|
||||||
|
function isExecuted(uint256 proposalId) public view returns (bool) { |
||||||
|
return _isExecuted(proposalId); |
||||||
|
} |
||||||
|
|
||||||
|
function isCanceled(uint256 proposalId) public view returns (bool) { |
||||||
|
return _isCanceled(proposalId); |
||||||
|
} |
||||||
|
|
||||||
|
function isQueued(uint256 proposalId) public view returns (bool) { |
||||||
|
return _proposalQueueId(proposalId) != bytes32(0); |
||||||
|
} |
||||||
|
|
||||||
|
function governanceCallLength() public view returns (uint256) { |
||||||
|
return _governanceCallLength(); |
||||||
|
} |
||||||
|
|
||||||
|
// Harness from GovernorPreventLateQuorum |
||||||
|
function getExtendedDeadline(uint256 proposalId) public view returns (uint64) { |
||||||
|
return _getExtendedDeadline(proposalId); |
||||||
|
} |
||||||
|
|
||||||
|
// Harness from GovernorCountingSimple |
||||||
|
function getAgainstVotes(uint256 proposalId) public view returns (uint256) { |
||||||
|
(uint256 againstVotes,,) = proposalVotes(proposalId); |
||||||
|
return againstVotes; |
||||||
|
} |
||||||
|
|
||||||
|
function getForVotes(uint256 proposalId) public view returns (uint256) { |
||||||
|
(,uint256 forVotes,) = proposalVotes(proposalId); |
||||||
|
return forVotes; |
||||||
|
} |
||||||
|
|
||||||
|
function getAbstainVotes(uint256 proposalId) public view returns (uint256) { |
||||||
|
(,,uint256 abstainVotes) = proposalVotes(proposalId); |
||||||
|
return abstainVotes; |
||||||
|
} |
||||||
|
|
||||||
|
// The following functions are overrides required by Solidity added by OZ Wizard. |
||||||
|
function votingDelay() public pure override returns (uint256) { |
||||||
|
return 1; // 1 block |
||||||
|
} |
||||||
|
|
||||||
|
function votingPeriod() public pure override returns (uint256) { |
||||||
|
return 45818; // 1 week |
||||||
|
} |
||||||
|
|
||||||
|
function quorum(uint256 blockNumber) |
||||||
|
public |
||||||
|
view |
||||||
|
override(IGovernor, GovernorVotesQuorumFraction) |
||||||
|
returns (uint256) |
||||||
|
{ |
||||||
|
return super.quorum(blockNumber); |
||||||
|
} |
||||||
|
|
||||||
|
function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { |
||||||
|
return super.state(proposalId); |
||||||
|
} |
||||||
|
|
||||||
|
function proposalDeadline(uint256 proposalId) public view override(IGovernor, Governor, GovernorPreventLateQuorum) returns (uint256) { |
||||||
|
return super.proposalDeadline(proposalId); |
||||||
|
} |
||||||
|
|
||||||
|
function propose( |
||||||
|
address[] memory targets, |
||||||
|
uint256[] memory values, |
||||||
|
bytes[] memory calldatas, |
||||||
|
string memory description |
||||||
|
) public override(Governor, IGovernor) returns (uint256) { |
||||||
|
return super.propose(targets, values, calldatas, description); |
||||||
|
} |
||||||
|
|
||||||
|
function _execute( |
||||||
|
uint256 proposalId, |
||||||
|
address[] memory targets, |
||||||
|
uint256[] memory values, |
||||||
|
bytes[] memory calldatas, |
||||||
|
bytes32 descriptionHash |
||||||
|
) internal override(Governor, GovernorTimelockControl) { |
||||||
|
super._execute(proposalId, targets, values, calldatas, descriptionHash); |
||||||
|
} |
||||||
|
|
||||||
|
function _cancel( |
||||||
|
address[] memory targets, |
||||||
|
uint256[] memory values, |
||||||
|
bytes[] memory calldatas, |
||||||
|
bytes32 descriptionHash |
||||||
|
) internal override(Governor, GovernorTimelockControl) returns (uint256) { |
||||||
|
return super._cancel(targets, values, calldatas, descriptionHash); |
||||||
|
} |
||||||
|
|
||||||
|
function _castVote( |
||||||
|
uint256 proposalId, |
||||||
|
address account, |
||||||
|
uint8 support, |
||||||
|
string memory reason, |
||||||
|
bytes memory params |
||||||
|
) internal override(Governor, GovernorPreventLateQuorum) returns (uint256) { |
||||||
|
return super._castVote(proposalId, account, support, reason, params); |
||||||
|
} |
||||||
|
|
||||||
|
function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { |
||||||
|
return super._executor(); |
||||||
|
} |
||||||
|
|
||||||
|
function supportsInterface(bytes4 interfaceId) |
||||||
|
public |
||||||
|
view |
||||||
|
virtual |
||||||
|
override(Governor, GovernorTimelockControl) |
||||||
|
returns (bool) |
||||||
|
{ |
||||||
|
return super.supportsInterface(interfaceId); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
import "helpers.spec" |
||||||
|
import "methods/IGovernor.spec" |
||||||
|
import "Governor.helpers.spec" |
||||||
|
import "GovernorInvariants.spec" |
||||||
|
|
||||||
|
methods { |
||||||
|
lateQuorumVoteExtension() returns uint64 envfree |
||||||
|
getExtendedDeadline(uint256) returns uint64 envfree |
||||||
|
} |
||||||
|
|
||||||
|
use invariant proposalStateConsistency |
||||||
|
use invariant votesImplySnapshotPassed |
||||||
|
|
||||||
|
/* |
||||||
|
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ |
||||||
|
│ Rule: │ |
||||||
|
│ * Deadline can never be reduced │ |
||||||
|
│ * If deadline increases then we are in `deadlineExtended` state and `castVote` was called. │ |
||||||
|
│ * A proposal's deadline can't change in `deadlineExtended` state. │ |
||||||
|
│ * A proposal's deadline can't be unextended. │ |
||||||
|
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ |
||||||
|
*/ |
||||||
|
rule deadlineChangeToPreventLateQuorum(uint256 pId, env e, method f, calldataarg args) |
||||||
|
filtered { f -> !skip(f) } |
||||||
|
{ |
||||||
|
requireInvariant proposalStateConsistency(pId); |
||||||
|
requireInvariant votesImplySnapshotPassed(pId); |
||||||
|
|
||||||
|
// This should be a direct consequence of the invariant: `getExtendedDeadline(pId) > 0 => quorumReached(pId)` |
||||||
|
// But this is not (easily) provable because the prover think `_totalSupplyCheckpoints` can arbitrarily change, |
||||||
|
// which causes the quorum() to change. Not sure how to fix that. |
||||||
|
require !quorumReached(pId) => getExtendedDeadline(pId) == 0; |
||||||
|
|
||||||
|
uint256 deadlineBefore = proposalDeadline(pId); |
||||||
|
bool deadlineExtendedBefore = getExtendedDeadline(pId) > 0; |
||||||
|
bool quorumReachedBefore = quorumReached(pId); |
||||||
|
|
||||||
|
f(e, args); |
||||||
|
|
||||||
|
uint256 deadlineAfter = proposalDeadline(pId); |
||||||
|
bool deadlineExtendedAfter = getExtendedDeadline(pId) > 0; |
||||||
|
bool quorumReachedAfter = quorumReached(pId); |
||||||
|
|
||||||
|
// deadline can never be reduced |
||||||
|
assert deadlineBefore <= proposalDeadline(pId); |
||||||
|
|
||||||
|
// deadline can only be extended in proposal or on cast vote |
||||||
|
assert deadlineAfter != deadlineBefore => ( |
||||||
|
( |
||||||
|
!deadlineExtendedBefore && |
||||||
|
!deadlineExtendedAfter && |
||||||
|
f.selector == propose(address[], uint256[], bytes[], string).selector |
||||||
|
) || ( |
||||||
|
!deadlineExtendedBefore && |
||||||
|
deadlineExtendedAfter && |
||||||
|
!quorumReachedBefore && |
||||||
|
quorumReachedAfter && |
||||||
|
deadlineAfter == clock(e) + lateQuorumVoteExtension() && |
||||||
|
votingAll(f) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
// a deadline can only be extended once |
||||||
|
assert deadlineExtendedBefore => deadlineBefore == deadlineAfter; |
||||||
|
|
||||||
|
// a deadline cannot be un-extended |
||||||
|
assert deadlineExtendedBefore => deadlineExtendedAfter; |
||||||
|
} |
Loading…
Reference in new issue