feat(deposits): Migration to EIP-6110 style processing#2794
feat(deposits): Migration to EIP-6110 style processing#2794
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2794 +/- ##
==========================================
+ Coverage 60.42% 60.84% +0.42%
==========================================
Files 367 367
Lines 18600 18726 +126
==========================================
+ Hits 11239 11394 +155
+ Misses 6422 6376 -46
- Partials 939 956 +17
🚀 New features to boost your workflow:
|
|
Supportive of this approach. Especially since its <200 line diff and gets rid of the CL querying the EL which isn't a great pattern. Agreed that the Block Gas Limit is a good enough protection from hitting the deposit queue. It's unlikely we'll be x10'ing the gas limit arbitrarily in the coming months as that's not a bottleneck. As you noted, the EL change is trivial. There is a point to be noted that we deviate from the spec by removing the deposit queue altogether, which is present in the spec. In the spec, deposit processing is done per epoch, whereas we do it per block. This allows us to avoid any BeaconState changes. IMO the processing overhead of deposits is low enough that it doesn't need to be batched into an epoch (don't have empirical data to support this tho). Unsure on the solution for the overlap period between old system and new. Perhaps it's just a custom state transition as part of |
It does "deviate from spec" but not any more than we already are "deviated from the spec". Currently also deposits are processed block by block and the state transition operation for each is not too expensive (signatures are not verified on repeat -- most -- deposits). This change only affects the "lag" at which deposits are processed but not the frequency of processing itself. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4ea7da0ba3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR migrates deposit handling toward an EIP-6110-style “deposit requests” flow activated at the new Electra2 fork, including special-case logic to avoid losing pre-fork deposits and updating configs/tests to recognize the new fork version.
Changes:
- Add Electra2 fork plumbing across chain spec/versioning, engine API selection, and block/type version support.
- Shift deposit handling: pre-Electra2 uses the deposit DB + block-body deposits; Electra2 uses execution requests (with a one-block catchup mechanism at the fork boundary).
- Update devnet/kurtosis/e2e/test specs and genesis templates for new fork timing and deposit contract address wiring.
Reviewed changes
Copilot reviewed 53 out of 53 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| testing/simulated/components.go | Adds Electra2 fork time to simulated chain specs. |
| testing/networks/80094/spec.toml | Adds electra-two-fork-time for mainnet test config. |
| testing/networks/80094/eth-genesis.json | Adds depositContractAddress (currently duplicated). |
| testing/networks/80069/spec.toml | Adds electra-two-fork-time for Bepolia config. |
| testing/networks/80069/eth-genesis.json | Adds depositContractAddress to EL genesis. |
| testing/files/spec.toml | Adds electra-two-fork-time to fixture spec. |
| testing/files/eth-genesis.json | Adds depositContractAddress to fixture EL genesis. |
| testing/e2e/standard/withdrawal_test.go | Updates expected fork version to Electra2. |
| testing/e2e/standard/beacon_api_test.go | Updates balance caps and expected fork version (Electra2 / 33 BERA). |
| testing/e2e/config/defaults.go | Pins reth image to a specific digest/tag. |
| storage/deposit/v1/store.go | Avoids huge slice preallocation (needed for MaxUint64 ranges). |
| state-transition/core/validation_deposits.go | Renames/extends non-genesis deposit validation for pre-/first-block Electra2. |
| state-transition/core/validation_deposits_test.go | Adds Electra2 catchup test coverage and adjusts setup for pre-Electra2. |
| state-transition/core/state_processor.go | Passes previous block fork version into operations processing. |
| state-transition/core/state_processor_withdrawals_test.go | Moves deposits into execution requests in tests (Electra2-style). |
| state-transition/core/state_processor_staking.go | Adds Electra2 deposit-request path + first-block catchup deposit processing. |
| state-transition/core/state_processor_staking_test.go | Updates staking tests to provide deposits via execution requests. |
| state-transition/core/state_processor_forks.go | Adds Electra2 fork upgrade + logging; refactors Electra1 upgrade flow. |
| state-transition/core/core_test.go | Adds helper to keep tests pre-Electra2 by default. |
| primitives/version/versions.go | Documents Electra2 version constant. |
| primitives/version/name.go | Adds “electra2” to version naming. |
| node-core/components/validator_service.go | Injects deposit contract into validator service. |
| kurtosis/src/nodes/consensus/beacond/node.star | Sets DEPOSIT_AMOUNT to 33e9. |
| kurtosis/src/nodes/consensus/beacond/launcher.star | Sets DEPOSIT_AMOUNT to 33e9. |
| kurtosis/src/networks/kurtosis-devnet/network-configs/genesis.json.template | Adds depositContractAddress to EL genesis template. |
| execution/deposit/interfaces.go | Removes redundant comment line. |
| execution/deposit/contract.go | Comment change for ReadDeposits (currently mismatched with exported name). |
| execution/client/ethclient/engine.go | Treats Electra2 like Electra1 for V4P11 engine methods. |
| execution/client/ethclient/engine_test.go | Adjusts invalid-version test now that Electra2 is supported. |
| consensus-types/types/signed_beacon_block.go | Allows Electra2 in empty signed block creation. |
| consensus-types/types/payload.go | Allows Electra2 payload->header conversion. |
| consensus-types/types/block.go | Allows Electra2 block construction. |
| config/spec/testnet.go | Adds Electra2 fork time to testnet spec data. |
| config/spec/mainnet.go | Adds Electra2 fork time to mainnet spec data. |
| config/spec/devnet.go | Sets devnet to start at Electra2 (fork time 0). |
| cli/commands/genesis/payload.go | Extends supported fork range up to Electra2. |
| cli/commands/deposit/db_check.go | Updates command to use pre-Electra2 deposit validation function. |
| chain/spec.go | Adds Electra2ForkTime to spec interface + validation ordering + inflation fork handling. |
| chain/spec_test.go | Extends fork-order tests to include Electra2. |
| chain/helpers.go | Adds Electra2 to ActiveForkVersionForTimestamp. |
| chain/helpers_test.go | Adds Electra2ForkTime to helper test spec. |
| chain/data.go | Adds Electra2ForkTime to SpecData / mapstructure key. |
| beacon/validator/service.go | Stores deposit contract on validator service; updates constructor signature. |
| beacon/validator/errors.go | Removes ErrDepositStoreIncomplete (moved to deposits package). |
| beacon/validator/block_builder.go | Uses new deposits helper functions for catchup + setting deposits on block body. |
| beacon/deposits/interfaces.go | New: ChainSpec interface for deposit helpers. |
| beacon/deposits/errors.go | New: ErrDepositStoreIncomplete moved here. |
| beacon/deposits/deposits.go | New: Electra2 catchup + pre-Electra2 fetch + block-body deposit setting helpers. |
| beacon/blockchain/service.go | Removes old deposit retry loop; adds a catchup sync.Once. |
| beacon/blockchain/process_proposal.go | Calls Electra2 catchup during incoming block verification (currently guarded by sync.Once). |
| beacon/blockchain/interfaces.go | Extends ServiceChainSpec with MaxDepositsPerBlock. |
| beacon/blockchain/finalize_block.go | Uses new pre-Electra2 fetch helper and Electra2 catchup (currently guarded by sync.Once). |
| beacon/blockchain/deposit.go | Deleted: old deposit fetch + retry queue implementation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
fridrik01
left a comment
There was a problem hiding this comment.
did a quick pass, overall looks good but will need to do another
| blockToFetch := blockNum - eth1FollowDistance | ||
| deposits, err := depositContract.ReadDeposits(ctx, blockToFetch) | ||
| if err != nil { | ||
| logger.Error("Failed to read deposits", "block", blockNum, "error", err) |
There was a problem hiding this comment.
is it safe to drop the error and only log, we had a retry mechanism before in depositCatchupFetcher?
There was a problem hiding this comment.
yes, this doesnt change any behavior. That retry mechanism was non-effectual, long overdue cleaning of tech debt
| // If we already fetched deposits for this block, we don't need to do it again. | ||
| if depositContract.LastBlockNumber() == lph.GetNumber() { |
There was a problem hiding this comment.
is this to guard against calling this several times from different places if isFirstFuluBlock is true?
There was a problem hiding this comment.
Yes this guards only to prevent catchup. We will return from this function early if we are not in the first fulu block. If we are in first fulu block we need to run this fetch once amongst prepare/processProposal and finalizeBlock
|
@calbera we should test this soon on devnet with load testing deposits |
| // LastBlockNumber returns the last block number that was successfully | ||
| // consumed from the deposit contract. | ||
| func (dc *WrappedDepositContract) LastBlockNumber() math.U64 { | ||
| return math.U64(dc.lastBlockNumber.Load()) |
There was a problem hiding this comment.
@calbera something I noticed: lastBlockNumber zero-value doesn't distinguish "fetched at block 0" from "never fetched." Should be harmless in practice since IIUC genesis deposits bypass contract events, but wanted to call it out anyway.
There was a problem hiding this comment.
yeah thats true, but for the purpose of deposit contract, its functionally equivalent to say that we "never fetched" deposits if the last block fetched was 0 since 0 block will not have any deposits. We start at 1 onwards.
|
|
||
| // If on the first block of Fulu, catchup the previous block's deposits. Between | ||
| // Prepare/ProcessProposal and FinalizeBlock, this only needs to be done once. | ||
| func CatchupFuluDeposits( |
There was a problem hiding this comment.
nit: unit tests for these would be nice (though they should be covered in simulation/integration tests)
There was a problem hiding this comment.
I didn't add unit tests for this file since most of the logic already existed in our processproposal / finalizeblock flows, so refactoring it to here should change no behavior, which can be enforced by syncing mainnet, and other e2e tests
EIP-6110 style deposits
3 requirements
Given an 8192 limit of deposits per block, we need to guarantee that an EL doesn't include more than 8192 deposits in 1 block.
Given the way deposits are taken from EL deposit contract events and then processed in CL, as is we will lose deposits from the 2 blocks right before the Fulu fork (which starts using 6110) is activated.
Forking on the EL to change the deposit event signature to Berachain's:
0x68af751683498a9f9be59fe8b0d52a64dd155255d85cdb29fea30b1e3f891d46(sample deposit)TODOs:
Remaining Testing
On a live devnet/testnet, ensure that timeouts do not occur when building first Fulu block as the build and verification procedures for this block now include an extra JSON RPC call to the EL. Look for PrepareProposal/ProcessProposal/FinalizeBlock timeouts.