Skip to content

Commit

Permalink
Merge pull request lidofinance#126 from lidofinance/docs/update-to-ma…
Browse files Browse the repository at this point in the history
…tch-code

Docs update
  • Loading branch information
Psirex authored Sep 12, 2024
2 parents ba63b4a + 2f4fa3b commit a142c49
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 263 deletions.
49 changes: 31 additions & 18 deletions docs/mechanism.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ Additionally, there is a Gate Seal emergency committee that allows pausing certa

The Dual governance mechanism (DG) is an iteration on the protocol governance that gives stakers a say by allowing them to block DAO decisions and providing a negotiation device between stakers and the DAO.

Another way of looking at dual governance is that it implements 1) a dynamic user-extensible timelock on DAO decisions and 2) a rage quit mechanism for stakers taking into account the specifics of how Ethereum withdrawals work.
Another way of looking at dual governance is that it implements:
1) a dynamic user-extensible timelock on DAO decisions
2) a rage quit mechanism for stakers taking into account the specifics of how Ethereum withdrawals work.


## Navigation
Expand Down Expand Up @@ -220,11 +222,17 @@ The sub-state's purpose is to allow all stakers to observe the Veto Signalling b
**Transition to the parent state**. If, while the sub-state is active, the following condition becomes true:

```math
\big( t - t^S_{act} \leq \, T_{lock}(R) \big) \,\lor\, \big( R > R_2 \big)
t - t^S_{act} \leq \, T_{lock}(R)
```

then the Deactivation sub-state is exited so only the parent Veto Signalling state remains active.

**Transition to Rage Quit**. If, while the sub-state is active, the following condition becomes true:
```math
\big( t - t^S_{act} > L_{max} \big) \, \land \, \big( R > R_2 \big)
```
then the Deactivation sub-state is exited along with its parent Veto Signalling state and the Rage Quit state is entered.

**Transition to Veto Cooldown**. If, while the sub-state is active, the following condition becomes true:

```math
Expand Down Expand Up @@ -276,13 +284,13 @@ Upon entry into the Rage Quit state, three things happen:

In this state, the DAO is allowed to submit proposals to the DG but cannot execute any pending proposals. Stakers are not allowed to lock (w)stETH or withdrawal NFTs into the rage quit escrow so joining the ongoing rage quit is not possible. However, they can lock their tokens that are not part of the ongoing rage quit process to the newly-deployed veto signalling escrow to potentially trigger a new rage quit later.

The state lasts until the withdrawal started in 2) is complete, i.e. until all batch withdrawal NFTs generated from (w)stETH that was locked in the escrow are fulfilled and claimed, plus `RageQuitExtensionDelay` days.
The state lasts until the withdrawal started in 2) is complete, i.e. until all batch withdrawal NFTs generated from (w)stETH that was locked in the escrow are fulfilled and claimed, plus `RageQuitExtensionPeriodDuration` days.

If a staker locks a withdrawal NFT into the signalling escrow before the Rage Quit state is entered, this NFT remains locked in the rage quit escrow. When such an NFT becomes fulfilled, the staker is allowed to burn this NFT and convert it to plain ETH, although still locked in the escrow. This allows stakers to derisk their ETH as early as possible by removing any dependence on the DAO-controlled code (remember that the withdrawal NFT contract is potentially upgradeable by the DAO but the rage quit escrow is immutable).

Since batch withdrawal NFTs are generated after the NFTs that were locked by stakers into the escrow directly, the withdrawal queue mechanism (external to the DG) guarantees that by the time batch NFTs are fulfilled, all individually locked NFTs are fulfilled as well and can be claimed. Together with the extension delay, this guarantees that any staker having a withdrawal NFT locked in the rage quit escrow has at least `RageQuitExtensionDelay` days to convert it to escrow-locked ETH before the DAO execution is unblocked.
Since batch withdrawal NFTs are generated after the NFTs that were locked by stakers into the escrow directly, the withdrawal queue mechanism (external to the DG) guarantees that by the time batch NFTs are fulfilled, all individually locked NFTs are fulfilled as well and can be claimed. Together with the extension period, this guarantees that any staker having a withdrawal NFT locked in the rage quit escrow has at least `RageQuitExtensionPeriodDuration` days to convert it to escrow-locked ETH before the DAO execution is unblocked.

When the withdrawal is complete and the extension delay elapses, two things happen simultaneously:
When the withdrawal is complete and the extension period elapses, two things happen simultaneously:

1. A timelock lasting $W(i)$ days is started, during which the withdrawn ETH remains locked in the rage quit escrow. After the timelock elapses, stakers who participated in the rage quit can obtain their ETH from the rage quit escrow.
2. The Rage Quit state is exited.
Expand All @@ -291,26 +299,22 @@ When the withdrawal is complete and the extension delay elapses, two things happ

**Transition to Veto Cooldown**. If, at the moment of the Rage Quit state exit, $R(t) \leq R_1$, the Veto Cooldown state is entered.

The duration of the ETH withdraw timelock $W(i)$ is a non-linear function that depends on the rage quit sequence number $i$ (see below):
The duration of the ETH withdraw timelock $W(i)$ is a linear function that depends on the rage quit sequence number $i$ (see below):

```math
W(i) = W_{min} +
\left\{ \begin{array}{lc}
0, & \text{if } i \lt i_{min} \\
g_W(i - i_{min}), & \text{otherwise}
\end{array} \right.
W(i) = \min \left\{ W_{min} + i * W_{growth} \,,\, W_{max} \right\}
```

where $W_{min}$ is `RageQuitEthWithdrawalsMinTimelock`, $i_{min}$ is `RageQuitEthWithdrawalsTimelockGrowthStartSeqNumber`, and $g_W(x)$ is a quadratic polynomial function with coefficients `RageQuitEthWithdrawalsTimelockGrowthCoeffs` (a list of length 3).
where $W_{min}$ is `RageQuitEthWithdrawalsMinDelay`, $W_{max}$ is `RageQuitEthWithdrawalsMaxDelay`, $W_{growth}$ is `rageQuitEthWithdrawalsDelayGrowth`.

The rage quit sequence number is calculated as follows: each time the Normal state is entered, the sequence number is set to 0; each time the Rage Quit state is entered, the number is incremented by 1.

```env
# Proposed values, to be modeled and refined
RageQuitExtensionDelay = 7 days
RageQuitEthWithdrawalsMinTimelock = 60 days
RageQuitEthWithdrawalsTimelockGrowthStartSeqNumber = 2
RageQuitEthWithdrawalsTimelockGrowthCoeffs = (0, TODO, TODO)
RageQuitExtensionPeriodDuration = 7 days
RageQuitEthWithdrawalsMinDelay = 60 days
RageQuitEthWithdrawalsMaxDelay = 180 days
rageQuitEthWithdrawalsDelayGrowth = 15 days
```


Expand Down Expand Up @@ -356,10 +360,10 @@ To resolve the potential deadlock, the mechanism contains a third-party arbiter
* Execute any pending proposal submitted by the DAO to DG (i.e. bypass the DG dynamic timelock).
* Unpause any of the paused protocol contracts.

The Tiebreaker committee can perform the above actions, subject to a timelock of `TiebreakerExecutionTimelock` days, iff any of the following two conditions is true:
The Tiebreaker committee can perform the above actions, subject to a timelock of `TiebreakerExecutionTimelock` days, if any of the following two conditions is true:

* **Tiebreaker Condition A**: (governance state is Rage Quit) $\land$ (protocol withdrawals are paused for a duration exceeding `TiebreakerActivationTimeout`).
* **Tiebreaker Condition B**: (governance state is Rage Quit) $\land$ (last time governance exited Normal or Veto Cooldown state was more than `TiebreakerActivationTimeout` days ago).
* **Tiebreaker Condition B**: the last time governance exited Normal or Veto Cooldown state was more than `TiebreakerActivationTimeout` days ago.

The Tiebreaker committee should be composed of multiple sub-committees covering different interest groups within the Ethereum community (e.g. largest DAOs, EF, L2s, node operators, OGs) and should require approval from a supermajority of sub-committees to execute a pending proposal. The approval by each sub-committee should require the majority support within the sub-committee. No sub-committee should contain more than $1/4$ of the members that are also members of the Reseal committee.

Expand Down Expand Up @@ -395,6 +399,15 @@ Dual governance should not cover:

## Changelog

### 2024-09-12
- Explicitly described the `VetoSignallingDeactivation` -> `RageQuit` state transition.
- Renamed `RageQuitExtensionDelay` to `RageQuitExtensionPeriodDuration`.
- Replaced the quadratic function for the ETH withdrawal timelock $W(i)$ with a linear function.
- Renamed `RageQuitEthWithdrawalsMinTimelock` to `RageQuitEthWithdrawalsMinDelay`.
- Removed the `RageQuitEthWithdrawalsTimelockGrowthStartSeqNumber` and `RageQuitEthWithdrawalsTimelockGrowthCoeffs` parameters.
- Introduced the `RageQuitEthWithdrawalsMaxDelay` and `RageQuitEthWithdrawalsDelayGrowth` parameters to calculate the $W(i)$ duration.
- Removed the requirement **"governance state is Rage Quit"** from **Tiebreaker Condition B**.

### 2024-06-25
- Instead of using the "wNFT" shortcut for the "Lido: stETH Withdrawal NFT" token, the official symbol "unstETH" is now used.
- For the consistency with the codebase, the `RageQuitEthClaimMinTimelock`, `RageQuitEthClaimTimelockGrowthStartSeqNumber`, `RageQuitEthClaimTimelockGrowthCoeffs` parameters were renamed into `RageQuitEthWithdrawalsMinTimelock`, `RageQuitEthWithdrawalsTimelockGrowthStartSeqNumber`, `RageQuitEthWithdrawalsTimelockGrowthCoeffs`.
Expand Down
48 changes: 25 additions & 23 deletions docs/plan-b.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,29 @@ Timelocked Governance (TG) is a governance subsystem positioned between the Lido
* [Proposal flow](#proposal-flow)
* [Proposal execution](#proposal-execution)
* [Common types](#common-types)
* [Contract: `TimelockedGovernance`](#contract-timelockedgovernance)
* [Contract: `EmergencyProtectedTimelock`](#contract-emergencyprotectedtimelock)
* [Contract: `Executor`](#contract-executor)
* [Contract: `Configuration`](#contract-configuration)
* [Contract: `ProposalsList`](#contract-proposalslist)
* [Contract: `HashConsensus`](#contract-hashconsensus)
* [Contract: `EmergencyActivationCommittee`](#contract-emergencyactivationcommittee)
* [Contract: `EmergencyExecutionCommittee`](#contract-emergencyexecutioncommittee)
* Core Contracts:
* [Contract: `TimelockedGovernance`](#contract-timelockedgovernance)
* [Contract: `EmergencyProtectedTimelock`](#contract-emergencyprotectedtimelock)
* [Contract: `Executor`](#contract-executor)
* Committees:
* [Contract: `ProposalsList`](#contract-proposalslist)
* [Contract: `HashConsensus`](#contract-hashconsensus)
* [Contract: `EmergencyActivationCommittee`](#contract-emergencyactivationcommittee)
* [Contract: `EmergencyExecutionCommittee`](#contract-emergencyexecutioncommittee)

## System Overview

<img width="1289" alt="image" src="https://github.com/lidofinance/dual-governance/assets/14151334/905bac24-dfb2-4eca-a113-1b82ead93752"/>

The system comprises the following primary contracts:
- **`TimelockedGovernance.sol`**: A singleton contract that serves as the interface for submitting and scheduling the execution of governance proposals.
- **[`EmergencyProtectedTimelock.sol`]**: A singleton contract responsible for storing submitted proposals and providing an interface for their execution. It offers protection against malicious proposals submitted by the DAO, implemented as a timelock on proposal execution. This protection is enforced through the cooperation of two emergency committees that can suspend proposal execution.
- [`EmergencyProtectedTimelock.sol`]() A singleton contract that stores submitted proposals and provides an execution interface. In addition, it implements an optional protection from a malicious proposals submitted by the DAO. The protection is implemented as a timelock on proposal execution combined with two emergency committees that have the right to cooperate and suspend the execution of the proposals.
- **[`Executor.sol`]**: A contract instance responsible for executing calls resulting from governance proposals. All protocol permissions or roles protected by TG, as well as the authority to manage these roles/permissions, should be assigned exclusively to instance of this contract, rather than being assigned directly to the DAO voting system.
- **[`EmergencyActivationCommittee`]**: A contract with the authority to activate Emergency Mode. Activation requires a quorum from committee members.
- **[`EmergencyExecutionCommittee`]**: A contract that enables the execution of proposals during Emergency Mode by obtaining a quorum of committee members.
- [**`TimelockedGovernance.sol`**](#contract-timelockedgovernance): A singleton contract that serves as the interface for submitting and scheduling the execution of governance proposals.
- [**`EmergencyProtectedTimelock.sol`**](#contract-emergencyprotectedtimelock): A singleton contract that stores submitted proposals and provides an execution interface. In addition, it implements an optional protection from a malicious proposals submitted by the DAO. The protection is implemented as a timelock on proposal execution combined with two emergency committees that have the right to cooperate and suspend the execution of the proposals.
- [**`Executor.sol`**](#contract-executor): A contract instance responsible for executing calls resulting from governance proposals. All protocol permissions or roles protected by TG, as well as the authority to manage these roles/permissions, should be assigned exclusively to instance of this contract, rather than being assigned directly to the DAO voting system.

Additionally, the system uses several committee contracts that allow members to execute, acquiring quorum, a narrow set of actions:

- [**`EmergencyActivationCommittee`**](#contract-emergencyactivationcommittee): A contract with the authority to activate Emergency Mode. Activation requires a quorum from committee members.
- [**`EmergencyExecutionCommittee`**](#contract-emergencyexecutioncommittee): A contract that enables the execution of proposals during Emergency Mode by obtaining a quorum of committee members.

## Proposal flow
<img width="567" alt="image" src="https://github.com/lidofinance/dual-governance/assets/14151334/f6f2efc1-7bd7-4e03-9c8b-6cd12cdfede8"/>
Expand All @@ -52,10 +55,10 @@ If emergency protection is enabled on the `EmergencyProtectedTimelock` instance,

## Common types

### Struct: ExecutorCall
### Struct: ExternalCall

```solidity
struct ExecutorCall {
struct ExternalCall {
address target;
uint96 value;
bytes payload;
Expand All @@ -70,11 +73,11 @@ The main entry point to the timelocked governance system, which provides an inte

### Function: `TimelockedGovernance.submitProposal`
```solidity
function submitProposal(ExecutorCall[] calls)
function submitProposal(ExecutorCall[] calls, string metadata)
returns (uint256 proposalId)
```

Instructs the [`EmergencyProtectedTimelock`](#Contract-EmergencyProtectedTimelocksol) singleton instance to register a new governance proposal composed of one or more external `calls` to be made by an admin executor contract. Initiates a timelock on scheduling the proposal for execution.
Instructs the [`EmergencyProtectedTimelock`](#Contract-EmergencyProtectedTimelocksol) singleton instance to register a new governance proposal composed of one or more external `calls`, along with the attached metadata text, to be made by an admin executor contract. Initiates a timelock on scheduling the proposal for execution.

See: [`EmergencyProtectedTimelock.submit`](#)
#### Returns
Expand Down Expand Up @@ -118,9 +121,9 @@ See: [`EmergencyProtectedTimelock.cancelAllNonExecutedProposals`](#)

For a proposal to be executed, the following steps have to be performed in order:
1. The proposal must be submitted using the `EmergencyProtectedTimelock.submit()` function.
2. The configured post-submit timelock (`Configuration.AFTER_SUBMIT_DELAY()`) must elapse.
2. The configured post-submit timelock (`EmergencyProtectedTimelock.getAfterSubmitDelay()`) must elapse.
3. The proposal must be scheduled using the `EmergencyProtectedTimelock.schedule()` function.
4. The configured emergency protection delay (`Configuration.AFTER_SCHEDULE_DELAY()`) must elapse (can be zero, see below).
4. The configured emergency protection delay (`Configuration.getAfterScheduleDelay()`) must elapse (can be zero, see below).
5. The proposal must be executed using the `EmergencyProtectedTimelock.execute()` function.

The contract only allows proposal submission and scheduling by the `governance` address. Normally, this address points to the [`TimelockedGovernance`](#Contract-TimelockedGovernancesol) singleton instance. Proposal execution is permissionless, unless Emergency Mode is activated.
Expand All @@ -132,7 +135,7 @@ If the Emergency Committees are set up and active, the governance proposal under
While active, the Emergency Activation Committee can enable Emergency Mode. This mode prohibits anyone but the Emergency Execution Committee from executing proposals. Once the **Emergency Duration** has ended, the Emergency Execution Committee or anyone else may disable the emergency mode, canceling all pending proposals. After the emergency mode is deactivated or the Emergency Period has elapsed, the Emergency Committees lose their power.
### Function: `EmergencyProtectedTimelock.submit`
```solidity
function submit(address executor, ExecutorCall[] calls)
function submit(address executor, ExecutorCall[] calls, string metadata)
returns (uint256 proposalId)
```
Registers a new governance proposal composed of one or more external `calls` to be made by the `executor` contract. Initiates the `AfterSubmitDelay`.
Expand Down Expand Up @@ -204,7 +207,7 @@ Resets the `governance` address to the `EMERGENCY_GOVERNANCE` value defined in t
* MUST be called by the Emergency Execution Committee address.

### Admin functions
The contract includes functions for managing emergency protection configuration (`setEmergencyProtection`) and general system wiring (`transferExecutorOwnership`, `setGovernance`). These functions MUST be called by the [Admin Executor](#) address.
The contract has the interface for managing the configuration related to emergency protection (`setEmergencyProtectionActivationCommittee`, `setEmergencyProtectionExecutionCommittee`, `setEmergencyProtectionEndDate`, `setEmergencyModeDuration`, `setEmergencyGovernance`) and general system wiring (`transferExecutorOwnership`, `setGovernance`, `setupDelays`). These functions MUST be called by the [Admin Executor](#) address.

## Contract: `Executor`
Executes calls resulting from governance proposals' execution. Every protocol permission or role protected by the TG, as well as the permission to manage these roles/permissions, should be assigned exclusively to instances of this contract.
Expand Down Expand Up @@ -431,4 +434,3 @@ Executes the governance reset by calling the `emergencyReset` function on the `E
#### Preconditions
- The governance reset proposal MUST have reached quorum and passed the timelock duration.


Loading

0 comments on commit a142c49

Please sign in to comment.