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