Skip to content

Commit

Permalink
Merge pull request #64 from lidofinance/develop
Browse files Browse the repository at this point in the history
Pre-Alfa Release
  • Loading branch information
Psirex authored Jul 22, 2024
2 parents 795c43e + c222802 commit 7248030
Show file tree
Hide file tree
Showing 88 changed files with 8,411 additions and 1,807 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/lido-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
name: "Test Proofs"
on:
push:
branches:
- develop
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}

- name: Run Proofs in KaaS
run: |
sha=$(git rev-parse HEAD)
branch_name=$(git rev-parse --abbrev-ref HEAD)
response=$(curl -X POST \
-w "%{http_code}" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.RV_COMPUTE_TOKEN }}" \
https://api.github.com/repos/runtimeverification/_kaas_lidofinance_dual-governance/actions/workflows/lido-ci.yml/dispatches \
-d '{
"ref": "develop",
"inputs": {
"branch_name": "'"${{ github.event.pull_request.head.sha || github.sha }}"'",
"extra_args": "script",
"statuses_sha": "'$sha'",
"org": "${{ github.repository_owner }}",
"repository": "${{ github.event.repository.name }}",
"auth_token": "'"${{ secrets.LIDO_STATUS_TOKEN }}"'"
}
}')
if [ "$response" -ge 200 ] && [ "$response" -lt 300 ]; then
echo "The request was successful"
elif [ "$response" -ge 400 ] && [ "$response" -lt 500 ]; then
echo "There was a client error: $response"
exit 1
else
echo "There was a server error: $response"
exit 1
fi
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/kontrol-cheatcodes"]
path = lib/kontrol-cheatcodes
url = https://github.com/runtimeverification/kontrol-cheatcodes
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
87 changes: 59 additions & 28 deletions contracts/Configuration.sol
Original file line number Diff line number Diff line change
@@ -1,38 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.26;

import {IConfiguration} from "./interfaces/IConfiguration.sol";
import {Durations, Duration} from "./types/Duration.sol";
import {IConfiguration, DualGovernanceConfig} from "./interfaces/IConfiguration.sol";

uint256 constant PERCENT = 10 ** 16;

contract Configuration is IConfiguration {
error MaxSealablesLimitOverflow(uint256 count, uint256 limit);

address public immutable ADMIN_EXECUTOR;
address public immutable EMERGENCY_GOVERNANCE;
uint256 public immutable MIN_WITHDRAWALS_BATCH_SIZE = 8;
uint256 public immutable MAX_WITHDRAWALS_BATCH_SIZE = 128;

// ---
// Dual Governance State Properties
// ---
uint256 public immutable FIRST_SEAL_RAGE_QUIT_SUPPORT = 3 * PERCENT;
uint256 public immutable SECOND_SEAL_RAGE_QUIT_SUPPORT = 15 * PERCENT;

uint256 public immutable AFTER_SUBMIT_DELAY = 3 days;
uint256 public immutable AFTER_SCHEDULE_DELAY = 2 days;
Duration public immutable DYNAMIC_TIMELOCK_MIN_DURATION = Durations.from(3 days);
Duration public immutable DYNAMIC_TIMELOCK_MAX_DURATION = Durations.from(30 days);

uint256 public immutable RAGE_QUIT_ETH_WITHDRAWAL_TIMELOCK = 30 days;
Duration public immutable VETO_SIGNALLING_MIN_ACTIVE_DURATION = Durations.from(5 hours);
Duration public immutable VETO_SIGNALLING_DEACTIVATION_MAX_DURATION = Durations.from(5 days);
Duration public immutable RAGE_QUIT_ACCUMULATION_MAX_DURATION = Durations.from(3 days);

uint256 public immutable SIGNALLING_COOLDOWN_DURATION = 4 days;
uint256 public immutable SIGNALLING_DEACTIVATION_DURATION = 5 days;
uint256 public immutable SIGNALLING_MIN_PROPOSAL_REVIEW_DURATION = 30 days;
Duration public immutable VETO_COOLDOWN_DURATION = Durations.from(4 days);

uint256 public immutable SIGNALLING_MIN_DURATION = 3 days;
uint256 public immutable SIGNALLING_MAX_DURATION = 30 days;
Duration public immutable RAGE_QUIT_EXTENSION_DELAY = Durations.from(7 days);
Duration public immutable RAGE_QUIT_ETH_WITHDRAWALS_MIN_TIMELOCK = Durations.from(60 days);
uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_START_SEQ_NUMBER = 2;

uint256 public immutable FIRST_SEAL_THRESHOLD = 3 * PERCENT;
uint256 public immutable SECOND_SEAL_THRESHOLD = 15 * PERCENT;
uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_A = 0;
uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_B = 0;
uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_C = 0;
// ---

uint256 public immutable TIE_BREAK_ACTIVATION_TIMEOUT = 365 days;
address public immutable ADMIN_EXECUTOR;
address public immutable EMERGENCY_GOVERNANCE;

uint256 public immutable RAGE_QUIT_EXTRA_TIMELOCK = 14 days;
uint256 public immutable RAGE_QUIT_EXTENSION_DELAY = 7 days;
uint256 public immutable RAGE_QUIT_ETH_CLAIM_MIN_TIMELOCK = 60 days;
uint256 public immutable MIN_STATE_DURATION = 5 hours;
uint256 public immutable ESCROW_ASSETS_UNLOCK_DELAY = 5 hours;
Duration public immutable AFTER_SUBMIT_DELAY = Durations.from(3 days);
Duration public immutable AFTER_SCHEDULE_DELAY = Durations.from(2 days);

Duration public immutable SIGNALLING_ESCROW_MIN_LOCK_TIME = Durations.from(5 hours);

Duration public immutable TIE_BREAK_ACTIVATION_TIMEOUT = Durations.from(365 days);

// Sealables Array Representation
uint256 private immutable MAX_SELABLES_COUNT = 5;
Expand Down Expand Up @@ -74,15 +86,34 @@ contract Configuration is IConfiguration {
external
view
returns (
uint256 firstSealThreshold,
uint256 secondSealThreshold,
uint256 signallingMinDuration,
uint256 signallingMaxDuration
uint256 firstSealRageQuitSupport,
uint256 secondSealRageQuitSupport,
Duration dynamicTimelockMinDuration,
Duration dynamicTimelockMaxDuration
)
{
firstSealThreshold = FIRST_SEAL_THRESHOLD;
secondSealThreshold = SECOND_SEAL_THRESHOLD;
signallingMinDuration = SIGNALLING_MIN_DURATION;
signallingMaxDuration = SIGNALLING_MAX_DURATION;
firstSealRageQuitSupport = FIRST_SEAL_RAGE_QUIT_SUPPORT;
secondSealRageQuitSupport = SECOND_SEAL_RAGE_QUIT_SUPPORT;
dynamicTimelockMinDuration = DYNAMIC_TIMELOCK_MIN_DURATION;
dynamicTimelockMaxDuration = DYNAMIC_TIMELOCK_MAX_DURATION;
}

function getDualGovernanceConfig() external view returns (DualGovernanceConfig memory config) {
config.firstSealRageQuitSupport = FIRST_SEAL_RAGE_QUIT_SUPPORT;
config.secondSealRageQuitSupport = SECOND_SEAL_RAGE_QUIT_SUPPORT;
config.dynamicTimelockMinDuration = DYNAMIC_TIMELOCK_MIN_DURATION;
config.dynamicTimelockMaxDuration = DYNAMIC_TIMELOCK_MAX_DURATION;
config.vetoSignallingMinActiveDuration = VETO_SIGNALLING_MIN_ACTIVE_DURATION;
config.vetoSignallingDeactivationMaxDuration = VETO_SIGNALLING_DEACTIVATION_MAX_DURATION;
config.vetoCooldownDuration = VETO_COOLDOWN_DURATION;
config.rageQuitExtensionDelay = RAGE_QUIT_EXTENSION_DELAY;
config.rageQuitEthWithdrawalsMinTimelock = RAGE_QUIT_ETH_WITHDRAWALS_MIN_TIMELOCK;
config.rageQuitEthWithdrawalsTimelockGrowthStartSeqNumber =
RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_START_SEQ_NUMBER;
config.rageQuitEthWithdrawalsTimelockGrowthCoeffs = [
RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_A,
RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_B,
RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_C
];
}
}
2 changes: 1 addition & 1 deletion contracts/ConfigurationProvider.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.26;

import {IConfiguration} from "./interfaces/IConfiguration.sol";

Expand Down
95 changes: 56 additions & 39 deletions contracts/DualGovernance.sol
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.26;

import {Duration} from "./types/Duration.sol";
import {Timestamp} from "./types/Timestamp.sol";
import {ITimelock, IGovernance} from "./interfaces/ITimelock.sol";
import {ISealable} from "./interfaces/ISealable.sol";
import {IResealManager} from "./interfaces/IResealManager.sol";

import {ConfigurationProvider} from "./ConfigurationProvider.sol";
import {Proposers, Proposer} from "./libraries/Proposers.sol";
import {ExecutorCall} from "./libraries/Proposals.sol";
import {EmergencyProtection} from "./libraries/EmergencyProtection.sol";
import {DualGovernanceState, State as GovernanceState} from "./libraries/DualGovernanceState.sol";
import {State, DualGovernanceState} from "./libraries/DualGovernanceState.sol";
import {TiebreakerProtection} from "./libraries/TiebreakerProtection.sol";

contract DualGovernance is IGovernance, ConfigurationProvider {
using Proposers for Proposers.State;
using DualGovernanceState for DualGovernanceState.Store;
using TiebreakerProtection for TiebreakerProtection.Tiebreaker;

event TiebreakerSet(address tiebreakCommittee);
event ProposalScheduled(uint256 proposalId);

error ProposalNotExecutable(uint256 proposalId);
error NotTiebreaker(address account, address tiebreakCommittee);
error NotResealCommitttee(address account);

ITimelock public immutable TIMELOCK;

address internal _tiebreaker;

TiebreakerProtection.Tiebreaker internal _tiebreaker;
Proposers.State internal _proposers;
DualGovernanceState.Store internal _dgState;
EmergencyProtection.State internal _emergencyProtection;
mapping(uint256 proposalId => uint256 executableAfter) internal _scheduledProposals;
address internal _resealCommittee;
IResealManager internal _resealManager;

constructor(
address config,
Expand All @@ -40,33 +44,36 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
_proposers.register(adminProposer, CONFIG.ADMIN_EXECUTOR());
}

function submit(ExecutorCall[] calldata calls) external returns (uint256 proposalId) {
function submitProposal(ExecutorCall[] calldata calls) external returns (uint256 proposalId) {
_proposers.checkProposer(msg.sender);
_dgState.activateNextState(CONFIG);
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
_dgState.checkProposalsCreationAllowed();
_dgState.setLastProposalCreationTimestamp();
Proposer memory proposer = _proposers.get(msg.sender);
proposalId = TIMELOCK.submit(proposer.executor, calls);
}

function schedule(uint256 proposalId) external {
_dgState.activateNextState(CONFIG);
_dgState.checkProposalsAdoptionAllowed();
function scheduleProposal(uint256 proposalId) external {
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());

Timestamp proposalSubmissionTime = TIMELOCK.getProposalSubmissionTime(proposalId);
_dgState.checkCanScheduleProposal(proposalSubmissionTime);

TIMELOCK.schedule(proposalId);

emit ProposalScheduled(proposalId);
}

function cancelAll() external {
function cancelAllPendingProposals() external {
_proposers.checkAdminProposer(CONFIG, msg.sender);
TIMELOCK.cancelAll();
TIMELOCK.cancelAllNonExecutedProposals();
}

function signallingEscrow() external view returns (address) {
function getVetoSignallingEscrow() external view returns (address) {
return address(_dgState.signallingEscrow);
}

function isScheduled(uint256 proposalId) external view returns (bool) {
return _scheduledProposals[proposalId] != 0;
function getRageQuitEscrow() external view returns (address) {
return address(_dgState.rageQuitEscrow);
}

function canSchedule(uint256 proposalId) external view returns (bool) {
Expand All @@ -78,31 +85,31 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
// ---

function activateNextState() external {
_dgState.activateNextState(CONFIG);
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
}

function currentState() external view returns (GovernanceState) {
function getCurrentState() external view returns (State) {
return _dgState.currentState();
}

function getVetoSignallingState()
external
view
returns (bool isActive, uint256 duration, uint256 activatedAt, uint256 enteredAt)
returns (bool isActive, Duration duration, Timestamp activatedAt, Timestamp enteredAt)
{
(isActive, duration, activatedAt, enteredAt) = _dgState.getVetoSignallingState(CONFIG);
(isActive, duration, activatedAt, enteredAt) = _dgState.getVetoSignallingState(CONFIG.getDualGovernanceConfig());
}

function getVetoSignallingDeactivationState()
external
view
returns (bool isActive, uint256 duration, uint256 enteredAt)
returns (bool isActive, Duration duration, Timestamp enteredAt)
{
(isActive, duration, enteredAt) = _dgState.getVetoSignallingDeactivationState(CONFIG);
(isActive, duration, enteredAt) = _dgState.getVetoSignallingDeactivationState(CONFIG.getDualGovernanceConfig());
}

function getVetoSignallingDuration() external view returns (uint256) {
return _dgState.getVetoSignallingDuration(CONFIG);
function getVetoSignallingDuration() external view returns (Duration) {
return _dgState.getVetoSignallingDuration(CONFIG.getDualGovernanceConfig());
}

function isSchedulingEnabled() external view returns (bool) {
Expand Down Expand Up @@ -143,28 +150,38 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
// Tiebreaker Protection
// ---

function tiebreakerSchedule(uint256 proposalId) external {
_checkTiebreakerCommittee(msg.sender);
function tiebreakerResumeSealable(address sealable) external {
_tiebreaker.checkTiebreakerCommittee(msg.sender);
_dgState.checkTiebreak(CONFIG);
_tiebreaker.resumeSealable(sealable);
}

function tiebreakerScheduleProposal(uint256 proposalId) external {
_tiebreaker.checkTiebreakerCommittee(msg.sender);
_dgState.checkTiebreak(CONFIG);
TIMELOCK.schedule(proposalId);
}

function setTiebreakerProtection(address newTiebreaker) external {
function setTiebreakerProtection(address newTiebreaker, address resealManager) external {
_checkAdminExecutor(msg.sender);
address oldTiebreaker = _tiebreaker;
if (newTiebreaker != oldTiebreaker) {
_tiebreaker = newTiebreaker;
emit TiebreakerSet(newTiebreaker);
}
_tiebreaker.setTiebreaker(newTiebreaker, resealManager);
}

// ---
// Internal Helper Methods
// Reseal executor
// ---

function _checkTiebreakerCommittee(address account) internal view {
if (account != _tiebreaker) {
revert NotTiebreaker(account, _tiebreaker);
function resealSealables(address[] memory sealables) external {
if (msg.sender != _resealCommittee) {
revert NotResealCommitttee(msg.sender);
}
_dgState.checkResealState();
_resealManager.reseal(sealables);
}

function setReseal(address resealManager, address resealCommittee) external {
_checkAdminExecutor(msg.sender);
_resealCommittee = resealCommittee;
_resealManager = IResealManager(resealManager);
}
}
Loading

0 comments on commit 7248030

Please sign in to comment.