Skip to content

Commit

Permalink
Update EIP-7002: Move to Last Call (#9328)
Browse files Browse the repository at this point in the history
* Update 7002

* Move to last call

* Fix link to assets
  • Loading branch information
mkalinin authored Feb 13, 2025
1 parent ffb1c70 commit 974da53
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 6 deletions.
21 changes: 15 additions & 6 deletions EIPS/eip-7002.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ title: Execution layer triggerable withdrawals
description: Allow validators to trigger exits and partial withdrawals via their execution layer (0x01) withdrawal credentials
author: Danny Ryan (@djrtwo), Mikhail Kalinin (@mkalinin), Ansgar Dietrichs (@adietrichs), Hsiao-Wei Wang (@hwwhww), lightclient (@lightclient)
discussions-to: https://ethereum-magicians.org/t/eip-7002-execution-layer-triggerable-exits/14195
status: Review
status: Last Call
last-call-deadline: 2025-02-21
type: Standards Track
category: Core
created: 2023-05-09
Expand Down Expand Up @@ -606,10 +607,11 @@ Sketch of spec:
Multiple validators can utilize the same execution layer withdrawal credential, thus the `validator_pubkey` field is utilized to disambiguate which validator is being exited.

Note, `validator_index` also disambiguates validators.
The problem is that smart contracts of some staking pools are not aware of the indices, because the index becomes known only after validator has been created on the beacon chain, while the pubkey is available in advance.

### Message queue

The contract maintains and in-state queue of withdrawal request messages to be dequeued each block into the block and thus into the execution layer.
The contract maintains an in-state queue of withdrawal request messages to be dequeued each block into the block and thus into the execution layer.

The number of withdrawal requests that can be passed into the consensus layer are bound by `MAX_WITHDRAWAL_REQUESTS_PER_BLOCK` to bound the load both on the block size as well as on the consensus layer processing. `16` has been chosen for `MAX_WITHDRAWAL_REQUESTS_PER_BLOCK` to be in line with the bounds of similar operations on the beacon chain -- e.g. `VoluntaryExit` and `Deposit`.

Expand All @@ -629,7 +631,7 @@ Method (b) has been utilized in this EIP to eliminate additional EIP requirement

### `TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK` configuration value

`TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK` has been selected as `2` such that even if all ETH is staked (~120M ETH -> 3.75M validators), the 64 validator per epoch target (`2 * 32 slots`) still exceeds the per-epoch exit churn limit on the consensus layer (defined by `get_validator_churn_limit()`) at such values -- 57 validators per epoch (`3.75M // 65536`).
`TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK` has been selected as `2` such that the growth of the partial withdrawal queue in the beacon state is negligible under extreme scenarios of the exit churn congestion.

### Fee update rule

Expand All @@ -639,13 +641,20 @@ Like EIP-1559, it’s a self-correcting formula: as the excess goes higher, the

The block-by-block behavior is roughly as follows. If block `N` processes `X` requests, then at the end of block `N` `excess` increases by `X - TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK`, and so the `fee` in block `N+1` increases by a factor of `e**((X - TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK) / WITHDRAWAL_REQUEST_FEE_UPDATE_FRACTION)`. Hence, it has a similar effect to the existing EIP-1559, but is more "stable" in the sense that it responds in the same way to the same total withdrawal requests regardless of how they are distributed over time.

The parameter `WITHDRAWAL_REQUEST_FEE_UPDATE_FRACTION` controls the maximum downwards rate of change of the blob gas price. It is chosen to target a maximum downwards change rate of `e(TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK / WITHDRAWAL_REQUEST_FEE_UPDATE_FRACTION) ≈ 1.125` per block. The maximum upwards change per block is `e((MAX_WITHDRAWAL_REQUESTS_PER_BLOCK - TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK) / WITHDRAWAL_REQUEST_FEE_UPDATE_FRACTION) ≈ 2.279`.
The parameter `WITHDRAWAL_REQUEST_FEE_UPDATE_FRACTION` controls the maximum downwards rate of change of the blob gas price. It is chosen to target a maximum downwards change rate of `e(TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK / WITHDRAWAL_REQUEST_FEE_UPDATE_FRACTION) ≈ 1.125` per block.

More detailed analysis of the fee mechanism is available [here](../assets/eip-7002/fee_analysis.md).

### Withdrawal requests inside of the block

Withdrawal requests are placed into the actual body of the block (and execution payload in the consensus layer).
Withdrawal requests are placed into the actual body of the beacon block.

There is a strong design requirement that the consensus layer and execution layer can execute independently of each other. This means, in this case, that the consensus layer cannot rely upon a synchronous call to the execution layer to get the required withdrawal requests for the current block. Instead, the requests must be embedded in the beacon block such that if the execution layer is offline, the consensus layer still has the requisite data to fully execute the consensus portion of the state transition function.

### Submitting requests via execution layer

There is a strong design requirement that the consensus layer and execution layer can execute independently of each other. This means, in this case, that the consensus layer cannot rely upon a synchronous call to the execution layer to get the required withdrawal requests for the current block. Instead, the requests must be embedded in the shared data-structure of the execution payload such that if the execution layer is offline, the consensus layer still has the requisite data to fully execute the consensus portion of the state transition function.
Verifying `secp256k1` signatures from the consensus layer via Engine API is one of the alternatives to the proposed requests mechanism which engineering complexity is much lower.
However, this approach would limit usage of withdrawal requests to a large extent by making it impossible for smart contracts owning validator withdrawal credentials to benefit from this functionality.

## Backwards Compatibility

Expand Down
51 changes: 51 additions & 0 deletions assets/eip-7002/fee_analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Withdrawal requests fee analysis

Analysis of the following parameters of the Withdrawal request smart contract:
* `MAX_WITHDRAWAL_REQUESTS_PER_BLOCK = 16`
* `TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK = 2`

**TL; DR:** These parameters have reasonable values.

## Consensus layer with TARGET=2, MAX=16

Full withdrawal requests doesn't create additional data complexity on the CL side as the changes are applied to the list of validators.

Current exit churn size allows to initiate withdrawal for 256 ETH per epoch, if a number of request per epoch is at its level (`32 * MAX = 512` requests) then the average withdrawing amount should be no bigger than `0.5 ETH` to fit the churn. It is not unreasonable to assume that most of the time this limit will be satisfied.

Partial withdrawals has a separate list which processing depends on the exit churn. If there is enough churn then pending partial withdrawal request will be processed at least `MIN_VALIDATOR_WITHDRAWABILITY_DELAY = 256` epochs after it was queued on the CL. This delay sets a lower boundary on the number of pending withdrawals in the queue in a situation when each block has `MAX=16` requests, it is `256 * 32 * 16 = 131,072`, which is `3 MB` of data. In an extreme case scenario when e.g. ~10% of validators are exiting this number can grow up to `192 MB`.

With `TARGET=2` requests per each block the above numbers are reduced to `0.375 MB` and `24 MB` respectively. Average amount for a partial withdrawal increases up to `4.0 ETH`.

## Attack via prohibitive fee

Full withdrawal requests are a security mechanism and an attacker can potentially benefit from block this functionality by keep the request fee at a prohibitive level. This section explores such attack with more details.

The cost of attack includes two parts. First part is to raise the fee to a certain level (the base cost) and the second part is to keep the fee at that level (per block cost).

The fee is updated at the end of the block processing, so it remains the same regardless of a number of requests submitted within one block. The lowest fee is `1 Wei` which makes the base cost of this attack near to `0`. The cost per block is computed as `prohibitive_fee * TARGET`.

There are two ways to increase the cost:
* Raise fee upon each request -- affects mostly the base cost, has a slight effect on the cost per block
* Increase `TARGET` -- affects the cost per block

The cost of attack with the status quo and above improvements are provided in the table below. The second cost is the cost per hour of such attack, i.e. cost per block times `300` (number of slots per hour).

| Fee | TARGET=2 | TARGET=2 + fine-grained fee | TARGET=8 |
|-|-|-|-|
| 0.0001 ETH | 0.06 ETH | 0.07 ETH | 0.25 ETH |
| 0.01 ETH | 6.25 ETH | 6.61 ETH | 25.00 ETH |
| 0.1 ETH | 61.98 ETH | 65.56 ETH | 247.92 ETH |
| 0.5 ETH | 303.39 ETH | 320.93 ETH | 1213.58 ETH |
| 1 ETH | 614.59 ETH | 650.10 ETH | 2458.37 ETH |
| 2 ETH | 1244.95 ETH | 1316.90 ETH | 4979.79 ETH |
| 4 ETH | 2521.76 ETH | 2667.53 ETH | 10087.02 ETH |

To summarize:
* raising fee upon each requests increases the cost of an hour of attack by _~6%_, while `TARGET=8` makes the 4 times increase
* with the status quo the attack is not sustainable long term

## UX

Assuming `1 Gwei` to be a reasonable fee per request, it will take 2.75 hours for the fee to reach this level if someone submits 2,000 requests in one block (the max possible number to fit in 30M gas block).

From the UX standpoint it might seem quite long, but `TARGET=2`, comparing to e.g. `TARGET=8`, reduces the strain on the protocol by more efficiently rate limiting the growth of the EL and CL queues.

0 comments on commit 974da53

Please sign in to comment.