An analysis of the spec changes and the evolution of the Besu codebase leading to the omission of the DEPOSIT_CONTRACT_ADDRESS
for Holesky and Sepolia.
This ultimately lead to a supermajority chain split on Holesky since Geth and Nethermind had a similar bug: https://github.com/ethereum/pm/blob/master/Pectra/holesky-postmortem.md
The point of this postmortem is not to assign blame, but rather help understand how we might avoid similar problems in the future.
DEPOSIT_CONTRACT_ADDRESS
compared to MainnetdepositContractAddress
in any of the EL configsdepositContractAddress
for genesis.json; not besu.json or chainspec.jsoneth_config
https://hackmd.io/@shemnon/eth_config
eth_config
might mitigate this anyway)
https://eth2book.info/capella/part2/deposits-withdrawals/contract/
The deposit contract was deployed on October the 14th, 2020, at 09:22:52 UTC to Ethereum address
0x00000000219ab540356cbb839cbe05303d7705fa
.
https://github.com/eth-clients/sepolia/
Sepolia launched (without deposit contract)
Sepolia deposit contract deployed on Jun-09-2022 02:58:14 PM UTC
to 0x7f02C3E3c98b133055B8B348B2Ac625669Ed295D
https://github.com/ethereum/EIPs/pull/6110
Initial spec only includes Mainnet DEPOSIT_CONTRACT_ADDRESS
At this point Sepolia deposit contract existed but Holesky network did not exist.
At time of writing (March 2025) the spec still only contains the Mainnet DEPOSIT_CONTRACT_ADDRESS
https://github.com/hyperledger/besu/pull/5295 (EIP-6110: Add deposit validation and apis)
DEFAULT_DEPOSIT_CONTRACT_ADDRESS
added: public static final Address DEFAULT_DEPOSIT_CONTRACT_ADDRESS =
Address.fromHexString("0x00000000219ab540356cbb839cbe05303d7705fa");
final Address depositContractAddress =
genesisConfigOptions.getDepositContractAddress().orElse(DEFAULT_DEPOSIT_CONTRACT_ADDRESS);
https://github.com/eth-clients/holesky
Holesky launched with DEPOSIT_CONTRACT_ADDRESS
predeployed in genesis to
0x4242424242424242424242424242424242424242
Besu did not add the depositContractAddress
to the Holesky config that EIP-6110 would use (neither Mainnet, Sepolia nor Goerli had it either).
http://github.com/hyperledger/besu/pull/6783 (Adding engine_getPayloadV4 and engine_newPayloadV4)
DEFAULT_DEPOSIT_CONTRACT_ADDRESS
wired up for Prague forkfinal Address depositContractAddress =
genesisConfigOptions.getDepositContractAddress().orElse(DEFAULT_DEPOSIT_CONTRACT_ADDRESS);
Various PRs (implementing various spec changes) by various people which refactored the location of the DEFAULT_DEPOSIT_CONTRACT_ADDRESS
and wiring/loading code.
These refactorings didn't have a significant impact on the root cause, but potentially contributed to diluting the knowledge and understanding of the system.
https://github.com/hyperledger/besu/pull/7068 (EIP-7685 General purpose execution layer requests)
https://github.com/hyperledger/besu/pull/7647 (Add consolidationRequestContract in jsonGenesisConfig)
https://github.com/hyperledger/besu/pull/7771 (Change requests to use flat encoding)