[toc]
# EIP-6110
> [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110): Supply validator deposits on chain
>
> **Abstract**
>
> Appends validator deposits to the Execution Layer block structure. This shifts responsibility of deposit inclusion and validation to the Execution Layer and removes the need for deposit (or eth1data) voting from the Consensus Layer.
>
> Validator deposits list supplied in a block is obtained by parsing deposit contract log events emitted by each deposit transaction included in a given block.
# Summary
This document gives a summary of an [EIP-6110](http://eips.ethereum.org/EIPS/eip-6110) prototype project. Goals of the project can be roughly outlined as follows:
* prove the proposed design by implementation
* get an understanding on the engineering complexity introduced by the proposal
* incorporate a feedback received from the development process into the specification, potentially getting rid of blind spots and inconsistencies, and do polishing
* cover CL specification with tests
* implement the proposal in Ethereum clients running the Mainnet
* stress test to uncover potential performance issues
## Timeframes
The project started early March 2023 and lasted for 4 months.
The original timeframe was 2 months but because of various [challenges](#Challenges) it took twice as more time to accomplish the main milestones.
## Participants
* [Kevin Bogner](https://github.com/kevinbogner)
* [Navie Chan](https://github.com/naviechan)
* [Mikhail Kalinin](https://github.com/mkalinin)
With a lot of help from:
* [Besu team](https://github.com/hyperledger/besu)
* [Lighthouse team](https://github.com/sigp/lighthouse)
* [Pari](https://github.com/parithosh) and [EF DevOps team](https://github.com/ethpandaops)
# Accomplishments
* Lighthouse implementation [kevinbogner#3](https://github.com/kevinbogner/lighthouse/pull/3)
* Besu implementation [#5055](https://github.com/hyperledger/besu/pull/5055/), [#5295](https://github.com/hyperledger/besu/pull/5295/)
* Consensus spec tests [#3309](https://github.com/ethereum/consensus-specs/pull/3309)
* Specification updates
* EIP [#6778](https://github.com/ethereum/EIPs/pull/6778), [#7237](https://github.com/ethereum/EIPs/pull/7237)
* Engine API [#427](https://github.com/ethereum/execution-apis/pull/427)
* Several multi-peer devnets including Blockscout setup
* Stress testing
* [PEEPanEIP#112](https://youtu.be/tRTBgCN9VgY)
* [ACDC#114 presentation](https://www.youtube.com/live/WtOQPxa6jOE?feature=share&t=2908)
## Lighthouse
The Lighthouse prototype is based on commit [`ae77e49`](https://github.com/sigp/lighthouse/tree/7a4be59884bb1960529e0614f92b62ab0ae77e49) of the [`deneb-free-blobs`](https://github.com/sigp/lighthouse/tree/deneb-free-blobs) branch, which contains the latest Dencun changes. The two main additions of the [`consensus-specs`](https://github.com/ethereum/consensus-specs/tree/dev/specs/_features/eip6110) are:
- `DepositReceipt` container
```rust
pub struct DepositReceipt {
pub pubkey: PublicKeyBytes,
pub withdrawal_credentials: Hash256,
#[serde(with = "serde_utils::quoted_u64")]
pub amount: u64,
pub signature: SignatureBytes,
#[serde(with = "serde_utils::quoted_u64")]
pub index: u64,
}
```
- `process_deposit_receipt` function
```rust
pub fn process_deposit_receipt<T: EthSpec>(
state: &mut BeaconState<T>,
deposit_receipt: &DepositReceipt,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
match state {
BeaconState::Eip6110(_) => {
let start_index = state
.deposit_receipts_start_index()
.map_err(|_| BlockProcessingError::DepositReceiptError)?;
if start_index == UNSET_DEPOSIT_RECEIPTS_START_INDEX {
*state
.deposit_receipts_start_index_mut()
.map_err(|_| BlockProcessingError::DepositReceiptError)? =
deposit_receipt.index;
}
// `apply_deposit` logic from `process_deposit` function:
...
}
```
---
- [Consensus spec tests](https://github.com/ethereum/consensus-specs/pull/3309)
- [Docker image](https://hub.docker.com/layers/kevinbogner/6110-lh/v1.1.2debug/images/sha256-828e6082b3eaab7e4ecc22e318809721ec53f80d208ecb53c08f240549a163dc?context=explore)
## Besu
To accomplish in-protocol deposit, we have introduced the following changes to the EL client.
#### Execution Payload and Engine API
There is a single new field named `depositReceipts` appended towards the end of the execution payload. We are basing our version of execution payload `ExecutionPayloadV6110` on `ExecutionPayloadV3` from Cancun, hence `depositReceipts` should be right after `excessDataGas`.
We also created new methods `engine_newPayloadV6110` and `engine_getPayloadV6110` to have `ExecutionPayloadV6110` in the params and responses.
#### Changes to block structure
`depositsRoot` and `deposits` are added to the block header and block body respectively similar to `withdrawalsRoot` and `withdrawals` in Shanghai.
The `deposits` here and `depositReceipts` in the execution payload refer to the same object. We understand the naming confusion, but for the sake of simplicity in code, and the term `deposits` will be limited to EL internal processing only, we are in favour of this decision.
#### Block validation on deposits
To validate `deposits` in a block, we need to compare the deposits from the payload against the deposits we extract from log events emitted by deposit transactions included in the block. The deposits in the payload **must** be equal to the extracted deposits in every way including the values of each field in each deposit, as well as the order of the deposits.
To identify whether a log contains deposits, it must be emitted from the address of the deposit contract. The variable `depositContractAddress` is introduced to the genesis config file and is used to identify and extract deposit logs during validation.
ABI decoding mechanism is added to Besu since deposits from logs are ABI encoded.
#### Current state of implementation
Originally, EIP-6110 was written to be based on Shanghai. During the prototype project the specification was rebased to Cancun.
The **Shanghai** based EL implementation of EIP-6110 on Besu has already been merged to the main branch under the experiemental feature and can be activated using `experiementalEIPsTime` in the genesis. The related tests including unit testing and acceptance testing (Series of engine API calls to imitate calls from CL clients) for several valid and invalid scenarios are already written.
The next step is to rebase this implementation to **Cancun**. A follow-up PR will be created once 4844 is implemented on Besu.
## Devnet testing
### Local devnets
We used [Kurtosis](https://github.com/kurtosis-tech/eth2-package) to run local devnets with our implementation to ensure basic functionality.
### Multi-peer devnets
We used tools provided by [ethPandaOps](https://github.com/ethpandaops) for multi-peer devnets. These tools enabled us to [generate genesis configs](https://github.com/ethpandaops/ethereum-genesis-generator/tree/deposit-devnets) and manage the [deployment and termination](https://github.com/ethpandaops/deposit-devnets) of devnets.
We opted for Blockscout as the block explorer to visualize the chain activities due to it being open source and offers the option to self-deploy. ethPandaOps provided ansible deployment of Blockscout on one of the nodes to conveniently deploy and re-deploy.
![](https://hackmd.io/_uploads/rJZLiSC9h.png)
![](https://hackmd.io/_uploads/SJdphBR5n.png)
### Stress testing
For stress testing, we followed [Parithosh's guide](https://parithosh.com/2021/06/09/2021-09-06-eth2-deposits/) to make a large volume of deposits. This involved using [eth2-val-tools](https://github.com/protolambda/eth2-val-tools) for creating `deposit_data`, and [ethereal](https://github.com/wealdtech/ethereal) for executing the deposits.
With a [deposit-fuzzer](https://github.com/kevinbogner/deposit-fuzz), we processed approximately 100 deposits per slot. However, we hadn't fully exploited the `gas_limit` at this point. To utilize it more effectively, we deployed a [batch-deposit-contract](https://docs.kiln.fi/v1/tools/eth-batch-deposit-contract), which enabled us to maximize the `gas_limit`, handling up to 725 deposits per slot.
![](https://hackmd.io/_uploads/HywhlLA93.png)
---
**Besu message:**
2023-07-03 12:15:49.625+00:00 | vert.x-worker-thread-0 | INFO | AbstractEngineNewPayload | Imported #**411** / 1 tx / 0 ws / **725 ds** / base fee 93.22 kwei / 24,763,439 (97.4%) gas
**Lighthouse message:**
Jul 03 12:15:49.679 INFO Valid block from HTTP API slot: **411**, proposer_index: 80, root: 0x7d86…c511, **block_delay: 1.298732062s**
---
It took Lighthouse ~1.3s to import a block with 725 deposits vs several millis for an empty block.
Although, block_delay may inaccurately represent real block processing time it gives an estimation on the order of this value.
#### Besu
An analysis was made by [Ameziane](https://github.com/ahamlat) to visualize the performance impact of our EIP-6110 implementation. The key takeaway is the 6110 block processing logic under `engine_newPayloadV2` does not take up too much CPU time compare to other engine calls.
![Overview of CPU time consumption](https://hackmd.io/_uploads/B1GSR_yi3.jpg)
An interesting note is that block body validation takes up a reasonable part of block processing.
![Detailed view of block processing](https://hackmd.io/_uploads/rJvOZYJo3.jpg)
*Detailed view of block processing*
The following image provides a detailed breakdown of what constitutes the block body validation process.
![Detailed breakdown of block body validation](https://hackmd.io/_uploads/BkGqWK1j3.jpg)
*Detailed breakdown of block body validation*
Compared with the current mainnet profiling, some overhead is associated with the `validateDeposits` method noticed within block body validation. However, this increase isn't alarming. Block body validation is estimated to take about 2-3 times longer with **EIP-6110 deposits** than on the current mainnet, yet it's still a minor part of the overall block processing in Besu.
The image below provides a detailed view of block processing on the mainnet.
![Detailed view of block processing on mainnet](https://hackmd.io/_uploads/rJ7h-t1jh.jpg)
*Detailed view of block processing on mainnet*
Zooming into block body validation on the mainnet uncovers further insights.
![Detailed breakdown of block body validation on mainnet](https://hackmd.io/_uploads/HySpZYJjh.jpg)
*Detailed breakdown of block body validation on mainnet*
These findings present a favorable perspective on Besu's performance. Aspects like receipts root calculation and block body validation should remain unaffected across different networks, including the mainnet.
Through our assessment of block processing time and CPU frames, we've found that block body validation clocks in at around **40 ms on Besu** at present. Compared to Besu's established performance baselines, this performance level is quite satisfactory, leading us to conclude that Besu exhibits efficient performance under the current circumstances.
# Challenges
One of our biggest challenges was identifying compatible 4844 versions of Besu and Lighthouse to build our implementation. We initiated our devnets during the transition between [4844-devnet-4](https://github.com/ethpandaops/4844-testnet/tree/master/ansible/inventories/devnet-4) and [4844-devnet-5](https://github.com/ethpandaops/4844-testnet/tree/master/ansible/inventories/devnet-5). However, at this time, Besu and Lighthouse were not running stably together.
When we forked to 6110, Lighthouse encountered some peering issues, resulting in critical `InsufficientPeers` errors. Once we added 6110 to the [pubsub messages](https://github.com/kevinbogner/lighthouse/blob/eip6110-deneb/beacon_node/lighthouse_network/src/types/pubsub.rs), we were able to resolve the problem. Nevertheless, diagnosing and fixing this issue took a substantial amount of time.
We have faced troubles having Blockscout to play nicely with Besu. Blockscout offical repository provides a pre-defined set of environmental variables for Geth, Nethermind and Erigon but not Besu therefore we spent a good chunk of time fiddling with the parameters and environmental variables on both Besu and Blockscout to get them running.
Blockscout calls `trace_transaction` on Besu to import the historical blocks. However Besu with Bonsai has a default limit of 512 blocks that we are allowed to look back. Ultimately we switched from Bonsai to Forest to get around the limit in order for Blockscout to properly imports all the historical blocks.
# Open questions
* Queueless approach
* top-ups bypassing the churn (up to ~1200 deposits can be packed into 30M gas block)
* `(pubkey, index)` caches needing special management leading to extra engineering complexity
* Deeper performance analysis on CL side
* Sig verification optimisation during deposit processing
# Gratitude
* EF for giving a [ESP grant](https://esp.ethereum.foundation) to fund [Kevin's](https://blog.ethereum.org/2023/06/15/allocation-update-q1-23) and NC's work
* Kevin and NC for finishing the project voluntarily after we ran out of the funded period
* Besu and Lighthouse team for their support
* Pari and EF Devops team for helping with devnet scripts and providing an infrastructure to run devnets
* Peter Davies for making analysis of a number of deposits that can be packed into a mainnet block considering EVM optimisations allowing to cut gas costs significantly