Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a86f4b7
bet
calbera May 28, 2025
275e139
nit
calbera May 28, 2025
fbfdcc5
empty eth1data
calbera May 28, 2025
d39fb79
nit
calbera May 28, 2025
a6f4d3b
more simplifications
calbera May 28, 2025
65c201c
lint
calbera May 28, 2025
2e3098d
process electra1 fork
calbera May 29, 2025
b7dae5e
EL genesis config update
calbera May 29, 2025
9ea411b
lint
calbera May 29, 2025
2b364dd
upgradeToElectra1
calbera May 29, 2025
c847971
Merge branch 'main' of github.com:berachain/beacon-kit into tmp-6110-poc
calbera Jun 2, 2025
2df992f
fix some unit tests
calbera Jun 3, 2025
3b0912a
fix withdrawals fork handling
calbera Jun 3, 2025
3a88dba
refactor for readability
calbera Jun 3, 2025
af9313c
nit
calbera Jun 3, 2025
741e0d0
fix nilaway
calbera Jun 3, 2025
d7d485c
Merge branch 'main' of github.com:berachain/beacon-kit into tmp-6110-poc
calbera Jun 4, 2025
fdac98b
handle all fork versions
calbera Jun 4, 2025
3b9f7f0
Merge branch 'main' of github.com:berachain/beacon-kit into tmp-6110-poc
calbera Aug 28, 2025
e283f37
lint
calbera Aug 28, 2025
f544fc1
Merge branch 'main' into tmp-6110-poc
calbera Aug 31, 2025
9742809
Merge branch 'main' into tmp-6110-poc
calbera Sep 10, 2025
2bafcaf
Merge branch 'main' of github.com:berachain/beacon-kit into tmp-6110-poc
calbera Dec 10, 2025
05ed5d1
fix merge conflict
calbera Dec 10, 2025
f8b4392
nits to Electra2
calbera Dec 10, 2025
febd7f0
electra2 in chain spec
calbera Dec 11, 2025
932b51b
format && lint
calbera Dec 11, 2025
339f692
tmp nit
calbera Jan 6, 2026
652c122
Merge branch 'main' into tmp-6110-poc
calbera Feb 26, 2026
c387d79
feat(deposits): add Electra2 fork with EIP-6110 style deposit process…
calbera Mar 4, 2026
2cb095a
Merge branch 'main' into tmp-6110-poc
calbera Mar 4, 2026
a119ca7
Merge branch 'main' into tmp-6110-poc
calbera Mar 18, 2026
944d594
fix catchup logic for block building
calbera Mar 19, 2026
79956a3
refactored beacon services to allow catchup deposits
calbera Mar 19, 2026
c3991e7
add validation logic
calbera Mar 19, 2026
700ecfd
format
calbera Mar 19, 2026
24e7bc5
fix default network configs for electra2
calbera Mar 20, 2026
03d2c27
validation deposits unit tests
calbera Mar 20, 2026
9a692e5
fix more tests
calbera Mar 20, 2026
5069d62
Fix state-transition UTs
calbera Mar 20, 2026
25e45a6
fix TestSubmitPartialWithdrawalTransaction
calbera Mar 20, 2026
1f109fd
use bera-reth image with 6110 enabled
calbera Mar 21, 2026
4ea7da0
fix processOperations logic
calbera Mar 21, 2026
9bd3177
format
calbera Mar 21, 2026
1e09f1c
sim test for deposits catchup
calbera Mar 21, 2026
00269f6
only fetch catchup deposits once
calbera Mar 21, 2026
4263338
correct approach
calbera Mar 21, 2026
9647cb3
try pectra deposit sim test fix
calbera Mar 21, 2026
0419af5
use fulu instead of electra2
calbera Mar 21, 2026
ed0e6c1
format
calbera Mar 23, 2026
5f1f731
Address comments
calbera Mar 24, 2026
ee2ed00
fix sim test log check
calbera Mar 24, 2026
40b66d5
reorder catchup func
calbera Mar 25, 2026
03faa80
atomic.Uint64
calbera Mar 26, 2026
7806133
supported version
calbera Apr 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions beacon/blockchain/finalize_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ import (
"github.com/berachain/beacon-kit/consensus/types"
"github.com/berachain/beacon-kit/primitives/math"
"github.com/berachain/beacon-kit/primitives/transition"
"github.com/berachain/beacon-kit/primitives/version"
statedb "github.com/berachain/beacon-kit/state-transition/core/state"
cmtabci "github.com/cometbft/cometbft/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

//nolint:funlen // Okay for now.
func (s *Service) FinalizeBlock(
ctx sdk.Context,
req *cmtabci.FinalizeBlockRequest,
Expand Down Expand Up @@ -109,9 +111,12 @@ func (s *Service) FinalizeBlock(

// STEP 4: Post Finalizations cleanups.

// Fetch and store the deposit for the block.
blockNum := blk.GetBody().GetExecutionPayload().GetNumber()
s.depositFetcher(ctx, blockNum)
// Before Electra1, deposits must be fetched from the EL directly in the CL.
if version.IsBefore(blk.GetForkVersion(), version.Electra1()) {
// Fetch and store the deposit for the block.
blockNum := blk.GetBody().GetExecutionPayload().GetNumber()
s.depositFetcher(ctx, blockNum)
}

// Store the finalized block in the KVStore.
//
Expand Down
55 changes: 29 additions & 26 deletions beacon/validator/block_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,36 +291,39 @@ func (s *Service) buildBlockBody(
// Set the KZG commitments on the block body.
body.SetBlobKzgCommitments(blobsBundle.GetCommitments())

// Dequeue deposits from the state.
depositIndex, err := st.GetEth1DepositIndex()
if err != nil {
return fmt.Errorf("failed loading eth1 deposit index: %w", err)
}
// Before Electra1, deposits are processed from the beacon block body directly.
if version.IsBefore(body.GetForkVersion(), version.Electra1()) {
// Dequeue deposits from the state.
depositIndex, err := st.GetEth1DepositIndex()
if err != nil {
return fmt.Errorf("failed loading eth1 deposit index: %w", err)
}

// Grab all previous deposits from genesis up to the current index + max deposits per block.
deposits, err := s.sb.DepositStore().GetDepositsByIndex(
ctx,
constants.FirstDepositIndex,
depositIndex+s.chainSpec.MaxDepositsPerBlock(),
)
if err != nil {
return err
}
if uint64(len(deposits)) < depositIndex {
return errors.Wrapf(ErrDepositStoreIncomplete,
"all historical deposits not available, expected: %d, got: %d",
depositIndex, len(deposits),
// Grab all previous deposits from genesis up to the current index + max deposits per block.
deposits, err := s.sb.DepositStore().GetDepositsByIndex(
ctx,
constants.FirstDepositIndex,
depositIndex+s.chainSpec.MaxDepositsPerBlock(),
)
}
if err != nil {
return err
}
if uint64(len(deposits)) < depositIndex {
return errors.Wrapf(ErrDepositStoreIncomplete,
"all historical deposits not available, expected: %d, got: %d",
depositIndex, len(deposits),
)
}

eth1Data := ctypes.NewEth1Data(deposits.HashTreeRoot())
body.SetEth1Data(eth1Data)
eth1Data := ctypes.NewEth1Data(deposits.HashTreeRoot())
body.SetEth1Data(eth1Data)

s.logger.Info(
"Building block body with local deposits",
"start_index", depositIndex, "num_deposits", uint64(len(deposits))-depositIndex,
)
body.SetDeposits(deposits[depositIndex:])
s.logger.Info(
"Building block body with local deposits",
"start_index", depositIndex, "num_deposits", uint64(len(deposits))-depositIndex,
)
body.SetDeposits(deposits[depositIndex:])
}

// Set the graffiti on the block body.
sizedGraffiti := bytes.ExtendToSize([]byte(s.cfg.Graffiti), bytes.B32Size)
Expand Down
2 changes: 2 additions & 0 deletions chain/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ type SpecData struct {
Deneb1ForkTime uint64 `mapstructure:"deneb-one-fork-time"`
// ElectraForkTime is the time at which the Electra fork is activated.
ElectraForkTime uint64 `mapstructure:"electra-fork-time"`
// Electra1ForkTime is the time at which the Electra1 fork is activated.
Electra1ForkTime uint64 `mapstructure:"electra-one-fork-time"`

// State list lengths
//
Expand Down
9 changes: 9 additions & 0 deletions chain/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ type ForkSpec interface {

// ElectraForkTime returns the time at which the Electra fork takes effect.
ElectraForkTime() uint64

// Electra1ForkTime returns the time at which the Electra1 fork takes effect.
Electra1ForkTime() uint64
}

type BlobSpec interface {
Expand Down Expand Up @@ -252,6 +255,7 @@ func (s spec) validate() error {
s.Data.GenesisTime,
s.Data.Deneb1ForkTime,
s.Data.ElectraForkTime,
s.Data.Electra1ForkTime,
}
for i := 1; i < len(orderedForkTimes); i++ {
prev, cur := orderedForkTimes[i-1], orderedForkTimes[i]
Expand Down Expand Up @@ -393,6 +397,11 @@ func (s spec) ElectraForkTime() uint64 {
return s.Data.ElectraForkTime
}

// Electra1ForkTime returns the epoch of the Electra1 fork.
func (s spec) Electra1ForkTime() uint64 {
return s.Data.Electra1ForkTime
}

// EpochsPerHistoricalVector returns the number of epochs per historical vector.
func (s spec) EpochsPerHistoricalVector() uint64 {
return s.Data.EpochsPerHistoricalVector
Expand Down
1 change: 1 addition & 0 deletions config/spec/mainnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,10 @@
TargetSecondsPerEth1Block: defaultTargetSecondsPerEth1Block,

// Fork-related values.
GenesisTime: mainnetGenesisTime,

Check failure on line 133 in config/spec/mainnet.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gci)
Deneb1ForkTime: mainnetDeneb1ForkTime,
ElectraForkTime: mainnetElectraForkTime,
Electra1ForkTime: 9999999999999999999,

Check failure on line 136 in config/spec/mainnet.go

View workflow job for this annotation

GitHub Actions / lint

Magic number: 9999999999999999999, in <assign> detected (mnd)

// State list length constants.
EpochsPerHistoricalVector: defaultEpochsPerHistoricalVector,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"max": 6,
"baseFeeUpdateFraction": 3338477
}
}
},
"depositContractAddress": "0x4242424242424242424242424242424242424242"
},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x0",
Expand Down
46 changes: 46 additions & 0 deletions state-transition/core/state_processor_forks.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ func (sp *StateProcessor) ProcessFork(
if logUpgrade {
sp.logElectraFork(stateFork.PreviousVersion, timestamp, slot)
}
case version.Electra1():
if err = sp.upgradeToElectra1(st, stateFork, slot); err != nil {
return err
}

// Log the upgrade to Electra1 if requested.
if logUpgrade {
sp.logElectra1Fork(stateFork.PreviousVersion, timestamp, slot)
}
Comment thread
calbera marked this conversation as resolved.
default:
panic(fmt.Sprintf("unsupported fork version: %s", forkVersion))
}
Expand Down Expand Up @@ -208,3 +217,40 @@ func (sp *StateProcessor) logElectraFork(
sp.cs.SlotToEpoch(slot).Unwrap(),
))
}

// upgradeToElectra1 upgrades the state to the Electra1 fork version. It just sets the Fork struct
// in the BeaconState.
func (sp *StateProcessor) upgradeToElectra1(
st *statedb.StateDB, fork *types.Fork, slot math.Slot,
) error {
// Set the fork on BeaconState.
fork.PreviousVersion = fork.CurrentVersion
fork.CurrentVersion = version.Electra1()
fork.Epoch = sp.cs.SlotToEpoch(slot)
return st.SetFork(fork)
}

// logElectra1Fork logs information about the Electra1 fork.
func (sp *StateProcessor) logElectra1Fork(
previousVersion common.Version, timestamp math.U64, slot math.Slot,
) {
sp.logger.Info(fmt.Sprintf(`


⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️

+ ✅ welcome to the electra1 (0x05010000) fork! 🎉
+ 🚝 previous fork: %s (%s)
+ ⏱️ electra1 fork time: %d
+ 🍴 first slot / timestamp of electra1: %d / %d
+ ⛓️ current beacon epoch: %d

⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️

`,
version.Name(previousVersion), previousVersion.String(),
sp.cs.Electra1ForkTime(),
slot.Unwrap(), timestamp.Unwrap(),
sp.cs.SlotToEpoch(slot).Unwrap(),
))
}
77 changes: 45 additions & 32 deletions state-transition/core/state_processor_staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,50 +37,63 @@ func (sp *StateProcessor) processOperations(
st *state.StateDB,
blk *ctypes.BeaconBlock,
) error {
// Verify that outstanding deposits are processed up to the maximum number of deposits.
//
// Unlike Eth 2.0 specs we don't check that
// `len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)`
deposits := blk.GetBody().GetDeposits()
if uint64(len(deposits)) > sp.cs.MaxDepositsPerBlock() {
return errors.Wrapf(
ErrExceedsBlockDepositLimit, "expected: %d, got: %d",
sp.cs.MaxDepositsPerBlock(), len(deposits),
)
// Before Electra1, deposits are processed from the beacon block body directly.
if version.IsBefore(blk.GetForkVersion(), version.Electra1()) {
// Verify that outstanding deposits are processed up to the maximum number of deposits.
//
// Unlike Ethereum 2.0 specs, we don't check that
// `len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)`.
deposits := blk.GetBody().GetDeposits()
if uint64(len(deposits)) > sp.cs.MaxDepositsPerBlock() {
return errors.Wrapf(
ErrExceedsBlockDepositLimit, "expected: %d, got: %d",
sp.cs.MaxDepositsPerBlock(), len(deposits),
)
}

// Instead, we directly compare block deposits with our local store ones.
if err := ValidateNonGenesisDeposits(
ctx.ConsensusCtx(),
st,
sp.ds,
sp.cs.MaxDepositsPerBlock(),
deposits,
blk.GetBody().GetEth1Data().DepositRoot,
); err != nil {
return err
}

for _, dep := range deposits {
if err := sp.processDeposit(st, dep); err != nil {
return err
}
}

return st.SetEth1Data(blk.GetBody().Eth1Data)
}

// Instead we directly compare block deposits with our local store ones.
if err := ValidateNonGenesisDeposits(
ctx.ConsensusCtx(),
st,
sp.ds,
sp.cs.MaxDepositsPerBlock(),
deposits,
blk.GetBody().GetEth1Data().DepositRoot,
); err != nil {
// After Electra1, validators increase/decrease stake through execution requests which must
// be handled.
requests, err := blk.GetBody().GetExecutionRequests()
if err != nil {
return err
}

for _, dep := range deposits {
if err := sp.processDeposit(st, dep); err != nil {
return err
// EIP-7002 Withdrawals.
for _, withdrawal := range requests.Withdrawals {
if withdrawErr := sp.processWithdrawalRequest(st, withdrawal); withdrawErr != nil {
return withdrawErr
}
}

if version.EqualsOrIsAfter(blk.GetForkVersion(), version.Electra()) {
// After Electra, validators can request withdrawals through execution requests which must be handled.
requests, err := blk.GetBody().GetExecutionRequests()
if err != nil {
// EIP-6110 Deposits.
for _, dep := range requests.Deposits {
Comment thread
calbera marked this conversation as resolved.
Outdated
if err = sp.processDeposit(st, dep); err != nil {
return err
}
for _, withdrawal := range requests.Withdrawals {
if withdrawErr := sp.processWithdrawalRequest(st, withdrawal); withdrawErr != nil {
return withdrawErr
}
}
}

return st.SetEth1Data(blk.GetBody().Eth1Data)
return st.SetEth1Data(ctypes.NewEmptyEth1Data())
}

// processDeposit processes the deposit and ensures it matches the local state.
Expand Down
3 changes: 2 additions & 1 deletion testing/files/eth-genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"max": 6,
"baseFeeUpdateFraction": 3338477
}
}
},
"depositContractAddress": "0x4242424242424242424242424242424242424242"
},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x0",
Expand Down
1 change: 1 addition & 0 deletions testing/files/spec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ target-seconds-per-eth1-block = 2
genesis-time = 0
deneb-one-fork-time = 0
electra-fork-time = 0
electra-one-fork-time = 9_999_999_999_999_999_999

# State list lengths
epochs-per-historical-vector = 8
Expand Down
3 changes: 2 additions & 1 deletion testing/networks/80069/eth-genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@
"max": 6,
"baseFeeUpdateFraction": 3338477
}
}
},
"depositContractAddress": "0x4242424242424242424242424242424242424242"
},
"difficulty": "0x01",
"extraData": "",
Expand Down
1 change: 1 addition & 0 deletions testing/networks/80069/spec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ target-seconds-per-eth1-block = 2
genesis-time = 1_739_976_735
deneb-one-fork-time = 1_740_090_694
electra-fork-time = 1_746_633_600
electra-one-fork-time = 9_999_999_999_999_999_999

# State list lengths
epochs-per-historical-vector = 8
Expand Down
3 changes: 2 additions & 1 deletion testing/networks/80094/eth-genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"baseFeeUpdateFraction": 3338477
}
},
"ethash": {}
"ethash": {},
"depositContractAddress": "0x4242424242424242424242424242424242424242"
},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x01",
Expand Down
1 change: 1 addition & 0 deletions testing/networks/80094/spec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ target-seconds-per-eth1-block = 2
genesis-time = 1_737_381_600
deneb-one-fork-time = 1_738_415_507
electra-fork-time = 1_749_056_400
electra-one-fork-time = 9_999_999_999_999_999_999

# State list lengths
epochs-per-historical-vector = 8
Expand Down
Loading