Skip to content

beacon/types, beacon/blsync: update for electra #31243

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions beacon/blsync/block_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ func (s *beaconBlockSync) updateEventFeed() {
return
}
s.chainHeadFeed.Send(types.ChainHeadEvent{
BeaconHead: optimistic.Attested.Header,
Block: execBlock,
Finalized: finalizedHash,
BeaconHead: optimistic.Attested.Header,
Block: execBlock,
ExecRequests: headBlock.ExecutionRequestsList(),
Finalized: finalizedHash,
})
}
7 changes: 6 additions & 1 deletion beacon/blsync/engineclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ func (ec *engineClient) callNewPayload(fork string, event types.ChainHeadEvent)
params = []any{execData}
)
switch fork {
case "electra":
method = "engine_newPayloadV4"
parentBeaconRoot := event.BeaconHead.ParentRoot
blobHashes := collectBlobHashes(event.Block)
params = append(params, blobHashes, parentBeaconRoot, event.ExecRequests)
case "deneb":
method = "engine_newPayloadV3"
parentBeaconRoot := event.BeaconHead.ParentRoot
Expand Down Expand Up @@ -135,7 +140,7 @@ func (ec *engineClient) callForkchoiceUpdated(fork string, event types.ChainHead

var method string
switch fork {
case "deneb":
case "deneb", "electra":
method = "engine_forkchoiceUpdatedV3"
case "capella":
method = "engine_forkchoiceUpdatedV2"
Expand Down
14 changes: 8 additions & 6 deletions beacon/light/api/light_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ var (
)

type CommitteeUpdate struct {
Version string
Update types.LightClientUpdate
NextSyncCommittee types.SerializedSyncCommittee
}
Expand Down Expand Up @@ -81,9 +80,9 @@ func (u *CommitteeUpdate) UnmarshalJSON(input []byte) error {
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
u.Version = dec.Version
u.NextSyncCommittee = dec.Data.NextSyncCommittee
u.Update = types.LightClientUpdate{
Version: dec.Version,
AttestedHeader: types.SignedHeader{
Header: dec.Data.Header.Beacon,
Signature: dec.Data.SyncAggregate,
Expand Down Expand Up @@ -206,7 +205,7 @@ func (api *BeaconLightApi) GetOptimisticUpdate() (types.OptimisticUpdate, error)

func decodeOptimisticUpdate(enc []byte) (types.OptimisticUpdate, error) {
var data struct {
Version string
Version string `json:"version"`
Data struct {
Attested jsonHeaderWithExecProof `json:"attested_header"`
Aggregate types.SyncAggregate `json:"sync_aggregate"`
Expand Down Expand Up @@ -259,7 +258,7 @@ func (api *BeaconLightApi) GetFinalityUpdate() (types.FinalityUpdate, error) {

func decodeFinalityUpdate(enc []byte) (types.FinalityUpdate, error) {
var data struct {
Version string
Version string `json:"version"`
Data struct {
Attested jsonHeaderWithExecProof `json:"attested_header"`
Finalized jsonHeaderWithExecProof `json:"finalized_header"`
Expand Down Expand Up @@ -289,6 +288,7 @@ func decodeFinalityUpdate(enc []byte) (types.FinalityUpdate, error) {
}

return types.FinalityUpdate{
Version: data.Version,
Attested: types.HeaderWithExecProof{
Header: data.Data.Attested.Beacon,
PayloadHeader: attestedExecHeader,
Expand Down Expand Up @@ -355,7 +355,8 @@ func (api *BeaconLightApi) GetCheckpointData(checkpointHash common.Hash) (*types
// See data structure definition here:
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientbootstrap
type bootstrapData struct {
Data struct {
Version string `json:"version"`
Data struct {
Header jsonBeaconHeader `json:"header"`
Committee *types.SerializedSyncCommittee `json:"current_sync_committee"`
CommitteeBranch merkle.Values `json:"current_sync_committee_branch"`
Expand All @@ -374,6 +375,7 @@ func (api *BeaconLightApi) GetCheckpointData(checkpointHash common.Hash) (*types
return nil, fmt.Errorf("invalid checkpoint block header, have %v want %v", header.Hash(), checkpointHash)
}
checkpoint := &types.BootstrapData{
Version: data.Version,
Header: header,
CommitteeBranch: data.Data.CommitteeBranch,
CommitteeRoot: data.Data.Committee.Root(),
Expand All @@ -395,7 +397,7 @@ func (api *BeaconLightApi) GetBeaconBlock(blockRoot common.Hash) (*types.BeaconB
}

var beaconBlockMessage struct {
Version string
Version string `json:"version"`
Data struct {
Message json.RawMessage `json:"message"`
}
Expand Down
8 changes: 4 additions & 4 deletions beacon/light/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ func GenerateTestUpdate(config *params.ChainConfig, period uint64, committee, ne
var attestedHeader types.Header
if finalizedHeader {
update.FinalizedHeader = new(types.Header)
*update.FinalizedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+100, params.StateIndexNextSyncCommittee, merkle.Value(update.NextSyncCommitteeRoot))
attestedHeader, update.FinalityBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexFinalBlock, merkle.Value(update.FinalizedHeader.Hash()))
*update.FinalizedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+100, params.StateIndexNextSyncCommittee(""), merkle.Value(update.NextSyncCommitteeRoot))
attestedHeader, update.FinalityBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexFinalBlock(""), merkle.Value(update.FinalizedHeader.Hash()))
} else {
attestedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+2000, params.StateIndexNextSyncCommittee, merkle.Value(update.NextSyncCommitteeRoot))
attestedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+2000, params.StateIndexNextSyncCommittee(""), merkle.Value(update.NextSyncCommitteeRoot))
}
update.AttestedHeader = GenerateTestSignedHeader(attestedHeader, config, committee, attestedHeader.Slot+1, signerCount)
return update
Expand All @@ -63,7 +63,7 @@ func GenerateTestSignedHeader(header types.Header, config *params.ChainConfig, c
}

func GenerateTestCheckpoint(period uint64, committee *types.SerializedSyncCommittee) *types.BootstrapData {
header, branch := makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexSyncCommittee, merkle.Value(committee.Root()))
header, branch := makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexSyncCommittee(""), merkle.Value(committee.Root()))
return &types.BootstrapData{
Header: header,
Committee: committee,
Expand Down
6 changes: 4 additions & 2 deletions beacon/params/networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ var (
AddFork("ALTAIR", 50, []byte{144, 0, 0, 112}).
AddFork("BELLATRIX", 100, []byte{144, 0, 0, 113}).
AddFork("CAPELLA", 56832, []byte{144, 0, 0, 114}).
AddFork("DENEB", 132608, []byte{144, 0, 0, 115})
AddFork("DENEB", 132608, []byte{144, 0, 0, 115}).
AddFork("ELECTRA", 222464, []byte{144, 0, 0, 116})

HoleskyLightConfig = (&ChainConfig{
GenesisValidatorsRoot: common.HexToHash("0x9143aa7c615a7f7115e2b6aac319c03529df8242ae705fba9df39b79c59fa8b1"),
Expand All @@ -52,5 +53,6 @@ var (
AddFork("ALTAIR", 0, []byte{2, 1, 112, 0}).
AddFork("BELLATRIX", 0, []byte{3, 1, 112, 0}).
AddFork("CAPELLA", 256, []byte{4, 1, 112, 0}).
AddFork("DENEB", 29696, []byte{5, 1, 112, 0})
AddFork("DENEB", 29696, []byte{5, 1, 112, 0}).
AddFork("ELECTRA", 115968, []byte{6, 1, 112, 0})
)
52 changes: 40 additions & 12 deletions beacon/params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,46 @@ const (
)

const (
StateIndexGenesisTime = 32
StateIndexGenesisValidators = 33
StateIndexForkVersion = 141
StateIndexLatestHeader = 36
StateIndexBlockRoots = 37
StateIndexStateRoots = 38
StateIndexHistoricRoots = 39
StateIndexFinalBlock = 105
StateIndexSyncCommittee = 54
StateIndexNextSyncCommittee = 55
StateIndexExecPayload = 56
StateIndexExecHead = 908
StateIndexGenesisTime = 32
StateIndexGenesisValidators = 33
StateIndexForkVersion = 141
StateIndexLatestHeader = 36
StateIndexBlockRoots = 37
StateIndexStateRoots = 38
StateIndexHistoricRoots = 39
StateIndexFinalBlockOld = 105
StateIndexFinalBlockElectra = 169
StateIndexSyncCommitteeOld = 54
StateIndexSyncCommitteeElectra = 86
StateIndexNextSyncCommitteeOld = 55
StateIndexNextSyncCommitteeElectra = 87
StateIndexExecPayload = 56
StateIndexExecHead = 908

BodyIndexExecPayload = 25
)

func StateIndexFinalBlock(forkName string) uint64 {
switch forkName {
case "bellatrix", "capella", "deneb":
return StateIndexFinalBlockOld
default:
return StateIndexFinalBlockElectra
}
}
func StateIndexSyncCommittee(forkName string) uint64 {
switch forkName {
case "bellatrix", "capella", "deneb":
return StateIndexSyncCommitteeOld
default:
return StateIndexSyncCommitteeElectra
}
}
func StateIndexNextSyncCommittee(forkName string) uint64 {
switch forkName {
case "bellatrix", "capella", "deneb":
return StateIndexNextSyncCommitteeOld
default:
return StateIndexNextSyncCommitteeElectra
}
}
63 changes: 57 additions & 6 deletions beacon/types/beacon_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@
package types

import (
"bytes"
"encoding/json"
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/protolambda/zrnt/eth2/beacon/capella"
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
"github.com/protolambda/zrnt/eth2/beacon/deneb"
"github.com/protolambda/zrnt/eth2/configs"
"github.com/protolambda/ztyp/codec"
"github.com/protolambda/ztyp/tree"

// beacon forks
"github.com/protolambda/zrnt/eth2/beacon/capella"
"github.com/protolambda/zrnt/eth2/beacon/deneb"
"github.com/protolambda/zrnt/eth2/beacon/electra"
)

type blockObject interface {
Expand All @@ -43,10 +48,12 @@ type BeaconBlock struct {
func BlockFromJSON(forkName string, data []byte) (*BeaconBlock, error) {
var obj blockObject
switch forkName {
case "deneb":
obj = new(deneb.BeaconBlock)
case "capella":
obj = new(capella.BeaconBlock)
case "deneb":
obj = new(deneb.BeaconBlock)
case "electra":
obj = new(electra.BeaconBlock)
default:
return nil, fmt.Errorf("unsupported fork: %s", forkName)
}
Expand All @@ -63,6 +70,8 @@ func NewBeaconBlock(obj blockObject) *BeaconBlock {
return &BeaconBlock{obj}
case *deneb.BeaconBlock:
return &BeaconBlock{obj}
case *electra.BeaconBlock:
return &BeaconBlock{obj}
default:
panic(fmt.Errorf("unsupported block type %T", obj))
}
Expand All @@ -75,6 +84,8 @@ func (b *BeaconBlock) Slot() uint64 {
return uint64(obj.Slot)
case *deneb.BeaconBlock:
return uint64(obj.Slot)
case *electra.BeaconBlock:
return uint64(obj.Slot)
default:
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
}
Expand All @@ -84,9 +95,12 @@ func (b *BeaconBlock) Slot() uint64 {
func (b *BeaconBlock) ExecutionPayload() (*types.Block, error) {
switch obj := b.blockObj.(type) {
case *capella.BeaconBlock:
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot)
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot, nil)
case *deneb.BeaconBlock:
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot)
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot, nil)
case *electra.BeaconBlock:
requests := b.ExecutionRequestsList()
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot, requests)
default:
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
}
Expand All @@ -99,6 +113,8 @@ func (b *BeaconBlock) Header() Header {
return headerFromZRNT(obj.Header(configs.Mainnet))
case *deneb.BeaconBlock:
return headerFromZRNT(obj.Header(configs.Mainnet))
case *electra.BeaconBlock:
return headerFromZRNT(obj.Header(configs.Mainnet))
default:
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
}
Expand All @@ -108,3 +124,38 @@ func (b *BeaconBlock) Header() Header {
func (b *BeaconBlock) Root() common.Hash {
return common.Hash(b.blockObj.HashTreeRoot(configs.Mainnet, tree.GetHashFn()))
}

// ExecutionRequestsList returns the execution layer requests of the block.
func (b *BeaconBlock) ExecutionRequestsList() [][]byte {
switch obj := b.blockObj.(type) {
case *capella.BeaconBlock, *deneb.BeaconBlock:
return nil
case *electra.BeaconBlock:
r := obj.Body.ExecutionRequests
return marshalRequests(configs.Mainnet,
&r.Deposits,
&r.Withdrawals,
&r.Consolidations,
)
default:
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
}
}

func marshalRequests(spec *zrntcommon.Spec, items ...zrntcommon.SpecObj) (list [][]byte) {
var buf bytes.Buffer
list = [][]byte{}
for typ, data := range items {
buf.Reset()
buf.WriteByte(byte(typ))
w := codec.NewEncodingWriter(&buf)
if err := data.Serialize(spec, w); err != nil {
panic(err)
}
if buf.Len() == 1 {
continue // skip empty requests
}
list = append(list, bytes.Clone(buf.Bytes()))
}
return list
}
21 changes: 21 additions & 0 deletions beacon/types/beacon_block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,27 @@ func TestBlockFromJSON(t *testing.T) {
wantBlockHash common.Hash
}
tests := []blocktest{
{
file: "block_electra_withdrawals.json",
version: "electra",
wantSlot: 151850,
wantBlockNumber: 141654,
wantBlockHash: common.HexToHash("0xf6730485a38be5ada3e110990a2c7adaabd2e8d4a49782134f1a8bfbc246a5d7"),
},
{
file: "block_electra_deposits.json",
version: "electra",
wantSlot: 151016,
wantBlockNumber: 140858,
wantBlockHash: common.HexToHash("0x1f2637170986346c7993d5adbadbebbf4c9ed89c6a4d2dff653db99c8c168076"),
},
{
file: "block_electra_consolidations.json",
version: "electra",
wantSlot: 151717,
wantBlockNumber: 141529,
wantBlockHash: common.HexToHash("0xc8807f7a1f96b0a073ff27065776dd21eff6b7e64079c60bffd33f690efbb330"),
},
{
file: "block_deneb.json",
version: "deneb",
Expand Down
8 changes: 5 additions & 3 deletions beacon/types/exec_header.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import (

"github.com/ethereum/go-ethereum/beacon/merkle"
"github.com/ethereum/go-ethereum/common"
"github.com/protolambda/zrnt/eth2/beacon/capella"
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
"github.com/protolambda/zrnt/eth2/beacon/deneb"
"github.com/protolambda/ztyp/tree"

// beacon chain forks
"github.com/protolambda/zrnt/eth2/beacon/capella"
"github.com/protolambda/zrnt/eth2/beacon/deneb"
)

type headerObject interface {
Expand All @@ -43,7 +45,7 @@ func ExecutionHeaderFromJSON(forkName string, data []byte) (*ExecutionHeader, er
switch forkName {
case "capella":
obj = new(capella.ExecutionPayloadHeader)
case "deneb":
case "deneb", "electra": // note: the payload type was not changed in electra
obj = new(deneb.ExecutionPayloadHeader)
default:
return nil, fmt.Errorf("unsupported fork: %s", forkName)
Expand Down
7 changes: 5 additions & 2 deletions beacon/types/exec_payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type payloadType interface {
}

// convertPayload converts a beacon chain execution payload to types.Block.
func convertPayload[T payloadType](payload T, parentRoot *zrntcommon.Root) (*types.Block, error) {
func convertPayload[T payloadType](payload T, parentRoot *zrntcommon.Root, requests [][]byte) (*types.Block, error) {
var (
header types.Header
transactions []*types.Transaction
Expand Down Expand Up @@ -62,7 +62,10 @@ func convertPayload[T payloadType](payload T, parentRoot *zrntcommon.Root) (*typ
default:
panic("unsupported block type")
}

if requests != nil {
reqHash := types.CalcRequestsHash(requests)
header.RequestsHash = &reqHash
}
block := types.NewBlockWithHeader(&header).WithBody(types.Body{Transactions: transactions, Withdrawals: withdrawals})
if hash := block.Hash(); hash != expectedHash {
return nil, fmt.Errorf("sanity check failed, payload hash does not match (expected %x, got %x)", expectedHash, hash)
Expand Down
Loading