Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
c260e36
add basic eip7732 (epbs) support
pk910 Jan 27, 2025
32a348f
trigger CI
pk910 Jan 27, 2025
311e4af
bump go-eth2-client
pk910 Feb 5, 2025
c4fd40c
Merge branch 'master' into eip7732-support
pk910 Feb 5, 2025
3c911be
bump go-eth2-client
pk910 Feb 5, 2025
0167d8b
subscribe to execution_payload events & load execution payload envelopes
pk910 Feb 5, 2025
edd68ac
bump go-eth2-client
pk910 Feb 6, 2025
f820feb
epbs payload handling & db persistence
pk910 Feb 8, 2025
48ce452
Merge branch 'eip7732-support' into pk910/eip7732-workspace
pk910 Feb 8, 2025
30a4d6d
fix schema upgrade sql for sqlite
pk910 Feb 8, 2025
efb9454
various small fixes for epbs implementation
pk910 Feb 8, 2025
4cc1d38
fixes for epbs payload loading
pk910 Feb 9, 2025
804d4b8
fetch execution payloads and show execution payload status on UI
pk910 Feb 9, 2025
687d581
Merge pull request #234 from ethpandaops/pk910/eip7732-workspace
pk910 Feb 9, 2025
3c637f9
fix payload badges on knockout rendered startpage
pk910 Feb 11, 2025
6da622d
show payload header data on slot details page
pk910 Feb 11, 2025
4fe1f72
add payload_status & remove has_payload to allow additional payload s…
pk910 Feb 12, 2025
c0030e4
Merge branch 'master' into eip7732-support
pk910 Feb 17, 2025
241771c
fix missing getters for eip7732 states
pk910 Feb 17, 2025
8384b64
Merge branch 'master' into eip7732-support
pk910 Mar 17, 2025
5dd85e4
Merge branch 'master' into eip7732-support
pk910 Mar 19, 2025
e09f649
Merge branch 'master' into eip7732-support
pk910 Apr 4, 2025
70c93b5
Merge branch 'master' into eip7732-support
pk910 Apr 4, 2025
7eeff90
Merge branch 'master' into eip7732-support
pk910 Apr 7, 2025
0380f13
save execution payload to block store
pk910 Apr 7, 2025
889db43
Merge branch 'master' into eip7732-support
barnabasbusa Apr 8, 2025
93a0089
Merge branch 'master' into eip7732-support
pk910 Apr 24, 2025
82a87f4
Merge branch 'master' into eip7732-support
pk910 Jul 15, 2025
499ef5a
fix merge issue with blob kzg blob kzg commitments
pk910 Jul 15, 2025
eef1a3d
add missing block helpers for eip7732
pk910 Jul 15, 2025
6010458
add gloas fork badge and internal getters
pk910 Sep 22, 2025
3556da8
Merge branch 'eip7732-support' into gloas-support
pk910 Sep 22, 2025
e35dc1a
fix merge conflicts
pk910 Sep 22, 2025
b4982ae
trigger CI
pk910 Sep 22, 2025
4628726
Merge branch 'master' into gloas-support
pk910 Dec 15, 2025
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
16 changes: 9 additions & 7 deletions blockdb/blockdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,19 @@ func (db *BlockDb) Close() error {
return db.engine.Close()
}

func (db *BlockDb) GetBlock(ctx context.Context, slot uint64, root []byte, parseBlock func(uint64, []byte) (interface{}, error)) (*types.BlockData, error) {
return db.engine.GetBlock(ctx, slot, root, parseBlock)
func (db *BlockDb) GetBlock(ctx context.Context, slot uint64, root []byte, parseBlock func(uint64, []byte) (interface{}, error), parsePayload func(uint64, []byte) (interface{}, error)) (*types.BlockData, error) {
return db.engine.GetBlock(ctx, slot, root, parseBlock, parsePayload)
}

func (db *BlockDb) AddBlock(ctx context.Context, slot uint64, root []byte, header_ver uint64, header_data []byte, body_ver uint64, body_data []byte) (bool, error) {
func (db *BlockDb) AddBlock(ctx context.Context, slot uint64, root []byte, header_ver uint64, header_data []byte, body_ver uint64, body_data []byte, payload_ver uint64, payload_data []byte) (bool, error) {
return db.engine.AddBlock(ctx, slot, root, func() (*types.BlockData, error) {
return &types.BlockData{
HeaderVersion: header_ver,
HeaderData: header_data,
BodyVersion: body_ver,
BodyData: body_data,
HeaderVersion: header_ver,
HeaderData: header_data,
BodyVersion: body_ver,
BodyData: body_data,
PayloadVersion: payload_ver,
PayloadData: payload_data,
}, nil
})
}
Expand Down
62 changes: 59 additions & 3 deletions blockdb/pebble/pebble.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ const (
)

const (
BlockTypeHeader uint16 = 1
BlockTypeBody uint16 = 2
BlockTypeHeader uint16 = 1
BlockTypeBody uint16 = 2
BlockTypePayload uint16 = 3
)

type PebbleEngine struct {
Expand Down Expand Up @@ -97,7 +98,34 @@ func (e *PebbleEngine) getBlockBody(root []byte, parser func(uint64, []byte) (in
return body, version, nil
}

func (e *PebbleEngine) GetBlock(_ context.Context, _ uint64, root []byte, parseBlock func(uint64, []byte) (interface{}, error)) (*types.BlockData, error) {
func (e *PebbleEngine) getBlockPayload(root []byte, parser func(uint64, []byte) (interface{}, error)) (interface{}, uint64, error) {
key := make([]byte, 2+len(root)+2)
binary.BigEndian.PutUint16(key[:2], KeyNamespaceBlock)
copy(key[2:], root)
binary.BigEndian.PutUint16(key[2+len(root):], BlockTypePayload)

res, closer, err := e.db.Get(key)
if err != nil && err != pebble.ErrNotFound {
return nil, 0, err
}
defer closer.Close()

if err == pebble.ErrNotFound || len(res) == 0 {
return nil, 0, nil
}

version := binary.BigEndian.Uint64(res[:8])
block := res[8:]

body, err := parser(version, block)
if err != nil {
return nil, 0, err
}

return body, version, nil
}

func (e *PebbleEngine) GetBlock(_ context.Context, _ uint64, root []byte, parseBlock func(uint64, []byte) (interface{}, error), parsePayload func(uint64, []byte) (interface{}, error)) (*types.BlockData, error) {
header, header_ver, err := e.getBlockHeader(root)
if err != nil {
return nil, err
Expand All @@ -124,6 +152,14 @@ func (e *PebbleEngine) GetBlock(_ context.Context, _ uint64, root []byte, parseB
blockData.Body = body
blockData.BodyVersion = body_ver

payload, payload_ver, err := e.getBlockPayload(root, parsePayload)
if err != nil {
return nil, err
}

blockData.Payload = payload
blockData.PayloadVersion = payload_ver

return blockData, nil
}

Expand Down Expand Up @@ -157,6 +193,19 @@ func (e *PebbleEngine) addBlockBody(root []byte, version uint64, block []byte) e
return e.db.Set(key, data, nil)
}

func (e *PebbleEngine) addBlockPayload(root []byte, version uint64, payload []byte) error {
key := make([]byte, 2+len(root)+2)
binary.BigEndian.PutUint16(key[:2], KeyNamespaceBlock)
copy(key[2:], root)
binary.BigEndian.PutUint16(key[2+len(root):], BlockTypePayload)

data := make([]byte, 8+len(payload))
binary.BigEndian.PutUint64(data[:8], version)
copy(data[8:], payload)

return e.db.Set(key, data, nil)
}

func (e *PebbleEngine) AddBlock(_ context.Context, _ uint64, root []byte, dataCb func() (*types.BlockData, error)) (bool, error) {
key := make([]byte, 2+len(root)+2)
binary.BigEndian.PutUint16(key[:2], KeyNamespaceBlock)
Expand All @@ -182,5 +231,12 @@ func (e *PebbleEngine) AddBlock(_ context.Context, _ uint64, root []byte, dataCb
return false, err
}

if blockData.PayloadVersion != 0 {
err = e.addBlockPayload(root, blockData.PayloadVersion, blockData.PayloadData)
if err != nil {
return false, err
}
}

return true, nil
}
40 changes: 32 additions & 8 deletions blockdb/s3/s3store.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ func (e *S3Engine) getObjectKey(root []byte, slot uint64) string {
}

type objectMetadata struct {
objVersion uint32
headerLength uint32
bodyVersion uint32
bodyLength uint32
objVersion uint32
headerLength uint32
bodyVersion uint32
bodyLength uint32
payloadVersion uint32
payloadLength uint32
}

func (e *S3Engine) readObjectMetadata(data []byte) (*objectMetadata, int, error) {
Expand All @@ -78,6 +80,13 @@ func (e *S3Engine) readObjectMetadata(data []byte) (*objectMetadata, int, error)
metadata.bodyVersion = binary.BigEndian.Uint32(data[8:12])
metadata.bodyLength = binary.BigEndian.Uint32(data[12:16])
metadataLength += 12
case 2:
metadata.headerLength = binary.BigEndian.Uint32(data[4:8])
metadata.bodyVersion = binary.BigEndian.Uint32(data[8:12])
metadata.bodyLength = binary.BigEndian.Uint32(data[12:16])
metadata.payloadVersion = binary.BigEndian.Uint32(data[16:20])
metadata.payloadLength = binary.BigEndian.Uint32(data[20:24])
metadataLength += 20
}

return metadata, metadataLength, nil
Expand All @@ -92,12 +101,18 @@ func (e *S3Engine) writeObjectMetadata(metadata *objectMetadata) []byte {
data = binary.BigEndian.AppendUint32(data, metadata.headerLength)
data = binary.BigEndian.AppendUint32(data, metadata.bodyVersion)
data = binary.BigEndian.AppendUint32(data, metadata.bodyLength)
case 2:
data = binary.BigEndian.AppendUint32(data, metadata.headerLength)
data = binary.BigEndian.AppendUint32(data, metadata.bodyVersion)
data = binary.BigEndian.AppendUint32(data, metadata.bodyLength)
data = binary.BigEndian.AppendUint32(data, metadata.payloadVersion)
data = binary.BigEndian.AppendUint32(data, metadata.payloadLength)
}

return data
}

func (e *S3Engine) GetBlock(ctx context.Context, slot uint64, root []byte, parseBlock func(uint64, []byte) (interface{}, error)) (*types.BlockData, error) {
func (e *S3Engine) GetBlock(ctx context.Context, slot uint64, root []byte, parseBlock func(uint64, []byte) (interface{}, error), parsePayload func(uint64, []byte) (interface{}, error)) (*types.BlockData, error) {
key := e.getObjectKey(root, slot)

obj, err := e.client.GetObject(ctx, e.bucket, key, minio.GetObjectOptions{})
Expand Down Expand Up @@ -184,20 +199,29 @@ func (e *S3Engine) AddBlock(ctx context.Context, slot uint64, root []byte, dataC
}

metadata := &objectMetadata{
objVersion: uint32(blockData.HeaderVersion),
objVersion: 1,
headerLength: uint32(len(blockData.HeaderData)),
bodyVersion: uint32(blockData.BodyVersion),
bodyLength: uint32(len(blockData.BodyData)),
}

if blockData.PayloadVersion != 0 {
metadata.objVersion = 2
metadata.payloadVersion = uint32(blockData.PayloadVersion)
metadata.payloadLength = uint32(len(blockData.PayloadData))
}

metadataBytes := e.writeObjectMetadata(metadata)
metadataLength := len(metadataBytes)

// Prepare data with header and body versions and lengths
data := make([]byte, metadataLength+int(metadata.headerLength)+int(metadata.bodyLength))
data := make([]byte, metadataLength+int(metadata.headerLength)+int(metadata.bodyLength)+int(metadata.payloadLength))
copy(data[:metadataLength], metadataBytes)
copy(data[metadataLength:metadataLength+int(metadata.headerLength)], blockData.HeaderData)
copy(data[metadataLength+int(metadata.headerLength):], blockData.BodyData)
copy(data[metadataLength+int(metadata.headerLength):metadataLength+int(metadata.headerLength)+int(metadata.bodyLength)], blockData.BodyData)
if metadata.objVersion == 2 {
copy(data[metadataLength+int(metadata.headerLength)+int(metadata.bodyLength):metadataLength+int(metadata.headerLength)+int(metadata.bodyLength)+int(metadata.payloadLength)], blockData.PayloadData)
}

// Upload object
_, err = e.client.PutObject(
Expand Down
15 changes: 9 additions & 6 deletions blockdb/types/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ package types
import "context"

type BlockData struct {
HeaderVersion uint64
HeaderData []byte
BodyVersion uint64
BodyData []byte
Body interface{}
HeaderVersion uint64
HeaderData []byte
BodyVersion uint64
BodyData []byte
Body interface{}
PayloadVersion uint64
PayloadData []byte
Payload interface{}
}
type BlockDbEngine interface {
Close() error
GetBlock(ctx context.Context, slot uint64, root []byte, parseBlock func(uint64, []byte) (interface{}, error)) (*BlockData, error)
GetBlock(ctx context.Context, slot uint64, root []byte, parseBlock func(uint64, []byte) (interface{}, error), parsePayload func(uint64, []byte) (interface{}, error)) (*BlockData, error)
AddBlock(ctx context.Context, slot uint64, root []byte, dataCb func() (*BlockData, error)) (bool, error)
}
7 changes: 7 additions & 0 deletions clients/consensus/chainspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type ChainSpecConfig struct {
ElectraForkEpoch *uint64 `yaml:"ELECTRA_FORK_EPOCH" check-if-fork:"ElectraForkEpoch"`
FuluForkVersion phase0.Version `yaml:"FULU_FORK_VERSION" check-if-fork:"FuluForkEpoch"`
FuluForkEpoch *uint64 `yaml:"FULU_FORK_EPOCH" check-if-fork:"FuluForkEpoch"`
GloasForkVersion phase0.Version `yaml:"GLOAS_FORK_VERSION" check-if-fork:"GloasForkEpoch"`
GloasForkEpoch *uint64 `yaml:"GLOAS_FORK_EPOCH" check-if-fork:"GloasForkEpoch"`

// Time parameters
SecondsPerSlot uint64 `yaml:"SECONDS_PER_SLOT"`
Expand Down Expand Up @@ -118,6 +120,11 @@ type ChainSpecConfig struct {
ValidatorCustodyRequirement *uint64 `yaml:"VALIDATOR_CUSTODY_REQUIREMENT" check-if-fork:"FuluForkEpoch"`
BalancePerAdditionalCustodyGroup *uint64 `yaml:"BALANCE_PER_ADDITIONAL_CUSTODY_GROUP" check-if-fork:"FuluForkEpoch"`
BlobSchedule []BlobScheduleEntry `yaml:"BLOB_SCHEDULE" check-if-fork:"FuluForkEpoch"`

// Gloas
PtcSize uint64 `yaml:"PTC_SIZE" check-if-fork:"GloasForkEpoch"`
MaxPayloadAttestations uint64 `yaml:"MAX_PAYLOAD_ATTESTATIONS" check-if-fork:"GloasForkEpoch"`
DomainPtcAttester phase0.DomainType `yaml:"DOMAIN_PTC_ATTESTER" check-if-fork:"GloasForkEpoch"`
}

type ChainSpecPreset struct {
Expand Down
36 changes: 36 additions & 0 deletions clients/consensus/chainstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,34 @@ func (cs *ChainState) GetForkDigestForEpoch(epoch phase0.Epoch) phase0.ForkDiges
return cs.GetForkDigest(currentForkVersion, currentBlobParams)
}

func (cs *ChainState) GetBlobScheduleForEpoch(epoch phase0.Epoch) *BlobScheduleEntry {
if cs.specs == nil {
return nil
}

var blobSchedule *BlobScheduleEntry

if cs.specs.ElectraForkEpoch != nil && epoch >= phase0.Epoch(*cs.specs.ElectraForkEpoch) {
blobSchedule = &BlobScheduleEntry{
Epoch: *cs.specs.ElectraForkEpoch,
MaxBlobsPerBlock: cs.specs.MaxBlobsPerBlockElectra,
}
} else if cs.specs.DenebForkEpoch != nil && epoch >= phase0.Epoch(*cs.specs.DenebForkEpoch) {
blobSchedule = &BlobScheduleEntry{
Epoch: *cs.specs.DenebForkEpoch,
MaxBlobsPerBlock: cs.specs.MaxBlobsPerBlock,
}
}

for i, blobScheduleEntry := range cs.specs.BlobSchedule {
if blobScheduleEntry.Epoch <= uint64(epoch) {
blobSchedule = &cs.specs.BlobSchedule[i]
}
}

return blobSchedule
}

func (cs *ChainState) GetForkDigest(forkVersion phase0.Version, blobParams *BlobScheduleEntry) phase0.ForkDigest {
if cs.specs == nil || cs.genesis == nil {
return phase0.ForkDigest{}
Expand Down Expand Up @@ -444,6 +472,14 @@ func (cs *ChainState) GetValidatorChurnLimit(validatorCount uint64) uint64 {
return adaptable
}

func (cs *ChainState) IsEip7732Enabled(epoch phase0.Epoch) bool {
if cs.specs == nil {
return false
}

return cs.specs.GloasForkEpoch != nil && phase0.Epoch(*cs.specs.GloasForkEpoch) <= epoch
}

func (cs *ChainState) GetBalanceChurnLimit(totalActiveBalance uint64) uint64 {
if cs.specs == nil {
return 0
Expand Down
67 changes: 36 additions & 31 deletions clients/consensus/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,38 @@ type ClientConfig struct {
}

type Client struct {
pool *Pool
clientIdx uint16
endpointConfig *ClientConfig
clientCtx context.Context
clientCtxCancel context.CancelFunc
rpcClient *rpc.BeaconClient
logger *logrus.Entry
isOnline bool
isSyncing bool
isOptimistic bool
versionStr string
nodeIdentity *rpc.NodeIdentity
clientType ClientType
lastEvent time.Time
retryCounter uint64
lastError error
headMutex sync.RWMutex
headRoot phase0.Root
headSlot phase0.Slot
justifiedRoot phase0.Root
justifiedEpoch phase0.Epoch
finalizedRoot phase0.Root
finalizedEpoch phase0.Epoch
lastFinalityUpdateEpoch phase0.Epoch
lastMetadataUpdateEpoch phase0.Epoch
lastMetadataUpdateTime time.Time
lastSyncUpdateEpoch phase0.Epoch
peers []*v1.Peer
blockDispatcher utils.Dispatcher[*v1.BlockEvent]
headDispatcher utils.Dispatcher[*v1.HeadEvent]
checkpointDispatcher utils.Dispatcher[*v1.Finality]
pool *Pool
clientIdx uint16
endpointConfig *ClientConfig
clientCtx context.Context
clientCtxCancel context.CancelFunc
rpcClient *rpc.BeaconClient
logger *logrus.Entry
isOnline bool
isSyncing bool
isOptimistic bool
versionStr string
nodeIdentity *rpc.NodeIdentity
clientType ClientType
lastEvent time.Time
retryCounter uint64
lastError error
headMutex sync.RWMutex
headRoot phase0.Root
headSlot phase0.Slot
justifiedRoot phase0.Root
justifiedEpoch phase0.Epoch
finalizedRoot phase0.Root
finalizedEpoch phase0.Epoch
lastFinalityUpdateEpoch phase0.Epoch
lastMetadataUpdateEpoch phase0.Epoch
lastMetadataUpdateTime time.Time
lastSyncUpdateEpoch phase0.Epoch
peers []*v1.Peer
blockDispatcher utils.Dispatcher[*v1.BlockEvent]
headDispatcher utils.Dispatcher[*v1.HeadEvent]
checkpointDispatcher utils.Dispatcher[*v1.Finality]
executionPayloadDispatcher utils.Dispatcher[*v1.ExecutionPayloadEvent]

specWarnings []string // warnings from incomplete spec checks
specs map[string]interface{}
Expand Down Expand Up @@ -102,6 +103,10 @@ func (client *Client) SubscribeFinalizedEvent(capacity int) *utils.Subscription[
return client.checkpointDispatcher.Subscribe(capacity, false)
}

func (client *Client) SubscribeExecutionPayloadEvent(capacity int, blocking bool) *utils.Subscription[*v1.ExecutionPayloadEvent] {
return client.executionPayloadDispatcher.Subscribe(capacity, blocking)
}

func (client *Client) GetPool() *Pool {
return client.pool
}
Expand Down
Loading