Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
6 changes: 5 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ linters:
- tparallel
- unconvert
- whitespace
- wsl
- wsl_v5
settings:
errcheck:
check-type-assertions: true
Expand All @@ -39,6 +39,10 @@ linters:
nolintlint:
require-explanation: true
require-specific: true
wsl_v5:
allow-first-in-block: true
allow-whole-block: false
branch-max-lines: 2
exclusions:
generated: lax
presets:
Expand Down
1 change: 0 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ func loadConfigFromFile(file string) (*checkpointz.Config, error) {
}

yamlFile, err := os.ReadFile(file)

if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/ethpandaops/checkpointz

go 1.25.1

replace github.com/attestantio/go-eth2-client => github.com/qu0b/go-eth2-client v0.0.0-20260102172406-1deaa6deedd2

require (
github.com/attestantio/go-eth2-client v0.27.2
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9
Expand Down Expand Up @@ -79,7 +81,6 @@ require (
golang.org/x/text v0.31.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
12 changes: 2 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
github.com/OffchainLabs/hashtree v0.2.1-0.20250530191054-577f0b75c7f7 h1:0r1HjExe/tyypkt380UTpjvILd5kLw51Xzl6a+hknQ8=
github.com/OffchainLabs/hashtree v0.2.1-0.20250530191054-577f0b75c7f7/go.mod h1:b07+cRZs+eAR8TR57CB9TQlt5Gnl/06Xs76xt/1wq0M=
github.com/attestantio/go-eth2-client v0.27.2 h1:VjA9R39ovy8ryb7IpFfD5eLYBg/20biztxh6fKZ7/K0=
github.com/attestantio/go-eth2-client v0.27.2/go.mod h1:i56XBegxVt7wXupnLBOj9IyGwy5cqaoTsCSKlwTubEU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/casbin/govaluate v1.8.0 h1:1dUaV/I0LFP2tcY1uNQEb6wBCbp8GMTcC/zhwQDWvZo=
Expand All @@ -26,10 +24,6 @@ github.com/emicklei/dot v1.6.4 h1:cG9ycT67d9Yw22G+mAb4XiuUz6E6H1S0zePp/5Cwe/c=
github.com/emicklei/dot v1.6.4/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/ethereum/go-ethereum v1.16.4 h1:H6dU0r2p/amA7cYg6zyG9Nt2JrKKH6oX2utfcqrSpkQ=
github.com/ethereum/go-ethereum v1.16.4/go.mod h1:P7551slMFbjn2zOQaKrJShZVN/d8bGxp4/I6yZVlb5w=
github.com/ethpandaops/beacon v0.65.0 h1:ssnab73uiuzhmhtU56q9D3ecVrEYv5n+tuqrXRK4YAg=
github.com/ethpandaops/beacon v0.65.0/go.mod h1:lgzrJjQVV77wZ+PJymsY3bQbAK4jrtP8n3WOwMf1Pcs=
github.com/ethpandaops/beacon v0.65.1-0.20251204024030-639105ffe6c2 h1:WU40omAqHrPF/Mfovc9+eMCrTAl8Km/KOanyYqr3SBg=
github.com/ethpandaops/beacon v0.65.1-0.20251204024030-639105ffe6c2/go.mod h1:lgzrJjQVV77wZ+PJymsY3bQbAK4jrtP8n3WOwMf1Pcs=
github.com/ethpandaops/beacon v0.66.0 h1:BRnf4yTEzkZwHW6sTp1x+mBoO5pwbQOX6wtLt3Nh1Y4=
github.com/ethpandaops/beacon v0.66.0/go.mod h1:lgzrJjQVV77wZ+PJymsY3bQbAK4jrtP8n3WOwMf1Pcs=
github.com/ethpandaops/ethwallclock v0.2.0 h1:EeFKtZ7v6TAdn/oAh0xaPujD7N4amjBxrWIByraUfLM=
Expand Down Expand Up @@ -160,8 +154,6 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pk910/dynamic-ssz v0.0.4 h1:DT29+1055tCEPCaR4V/ez+MOKW7BzBsmjyFvBRqx0ME=
github.com/pk910/dynamic-ssz v0.0.4/go.mod h1:b6CrLaB2X7pYA+OSEEbkgXDEcRnjLOZIxZTsMuO/Y9c=
github.com/pk910/dynamic-ssz v1.1.1 h1:b8sPR8fyhBvz8SHa2RH20SNtt5VDzAEY6fKsPCUcYX4=
github.com/pk910/dynamic-ssz v1.1.1/go.mod h1:3zyemisUysY2PWACZ8LeZS2tAw8AkuTb2GaLmqYsg1I=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
Expand All @@ -181,6 +173,8 @@ github.com/prysmaticlabs/go-bitfield v0.0.0-20240618144021-706c95b2dd15 h1:lC8ki
github.com/prysmaticlabs/go-bitfield v0.0.0-20240618144021-706c95b2dd15/go.mod h1:8svFBIKKu31YriBG/pNizo9N0Jr9i5PQ+dFkxWg3x5k=
github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4=
github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs=
github.com/qu0b/go-eth2-client v0.0.0-20260102172406-1deaa6deedd2 h1:39SAuAZOwmEnoLFjfr+o2tTmW58liWI9pLfS2+gvd7w=
github.com/qu0b/go-eth2-client v0.0.0-20260102172406-1deaa6deedd2/go.mod h1:fvULSL9WtNskkOB4i+Yyr6BKpNHXvmpGZj9969fCrfY=
github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0=
github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
Expand Down Expand Up @@ -290,8 +284,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc=
gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y=
gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
1 change: 1 addition & 0 deletions pkg/beacon/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ func (d *Default) ListFinalizedSlots(ctx context.Context) ([]phase0.Slot, error)

latestSlot := phase0.Slot(uint64(finality.Finalized.Epoch) * uint64(sp.SlotsPerEpoch))

//nolint:gosec // HistoricalEpochCount is validated as positive in config.
for i, val := uint64(latestSlot), uint64(latestSlot)-uint64(sp.SlotsPerEpoch)*uint64(d.config.HistoricalEpochCount); i > val; i -= uint64(sp.SlotsPerEpoch) {
slots = append(slots, phase0.Slot(i))
}
Expand Down
16 changes: 10 additions & 6 deletions pkg/beacon/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,13 @@ func (d *Default) fetchHistoricalCheckpoints(ctx context.Context, checkpoint *v1
// We'll derive the current finalized slot and then work back in intervals of SLOTS_PER_EPOCH.
currentSlot := uint64(checkpoint.Finalized.Epoch) * uint64(sp.SlotsPerEpoch)
for i := 1; i < d.config.HistoricalEpochCount; i++ {
//nolint:gosec // i starts at 1 and increments, always positive.
if uint64(i)*uint64(sp.SlotsPerEpoch) > currentSlot {
break
}

slot := phase0.Slot(currentSlot - uint64(i)*uint64(sp.SlotsPerEpoch))

slotsInScope[slot] = struct{}{}
//nolint:gosec // i starts at 1 and increments, always positive.
slotsInScope[phase0.Slot(currentSlot-uint64(i)*uint64(sp.SlotsPerEpoch))] = struct{}{}
}

for slot := range slotsInScope {
Expand Down Expand Up @@ -352,10 +352,14 @@ func (d *Default) fetchBundle(ctx context.Context, root phase0.Root, upstream *N
denebFork, err := sp.ForkEpochs.GetByName("deneb")
if err == nil && denebFork != nil {
if denebFork.Active(epoch) {
// Check if Fulu is active - if so, don't fetch blobs as they're no longer in blocks
// Check if Fulu or Gloas is active - if so, don't fetch blobs as they're no longer in blocks
fuluFork, fuluErr := sp.ForkEpochs.GetByName("fulu")
if fuluErr == nil && fuluFork != nil && fuluFork.Active(epoch) {
d.log.WithField("epoch", epoch).Debug("Skipping blob sidecar download - Fulu fork active")
gloasFork, gloasErr := sp.ForkEpochs.GetByName("gloas")
fuluActive := fuluErr == nil && fuluFork != nil && fuluFork.Active(epoch)
gloasActive := gloasErr == nil && gloasFork != nil && gloasFork.Active(epoch)

if fuluActive || gloasActive {
d.log.WithField("epoch", epoch).Debug("Skipping blob sidecar download - Fulu/Gloas fork active")
} else {
// Download and store blob sidecars
if err := d.downloadAndStoreBlobSidecars(ctx, slot, upstream); err != nil {
Expand Down
2 changes: 2 additions & 0 deletions pkg/beacon/expire_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ var (
)

func CalculateSlotExpiration(slot phase0.Slot, slotsOfHistory int) phase0.Slot {
//nolint:gosec // slotsOfHistory is a small positive test value
return slot + phase0.Slot(slotsOfHistory)
}

func GetSlotTime(slot phase0.Slot, secondsPerSlot time.Duration, genesis time.Time) time.Time {
//nolint:gosec // slot values are within safe range for time.Duration
return genesis.Add(time.Duration(slot) * secondsPerSlot)
}

Expand Down
57 changes: 33 additions & 24 deletions pkg/beacon/ssz/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,25 @@ func (e *Encoder) SetSpec(newSpec *state.Spec) {
e.dynssz = nil
}

func (e *Encoder) marshalSSZ(obj sszutils.FastsszMarshaler) ([]byte, error) {
var (
ssz []byte
err error
)

if e.customPreset {
ssz, err = e.getDynamicSSZ().MarshalSSZ(obj)
} else {
ssz, err = obj.MarshalSSZ()
}

if err != nil {
return nil, err
}

return ssz, nil
}

func (e *Encoder) GetBlockRoot(block *spec.VersionedSignedBeaconBlock) (root phase0.Root, err error) {
var blockObj sszutils.FastsszHashRoot

Expand All @@ -63,6 +82,8 @@ func (e *Encoder) GetBlockRoot(block *spec.VersionedSignedBeaconBlock) (root pha
blockObj = block.Electra.Message
case spec.DataVersionFulu:
blockObj = block.Fulu.Message
case spec.DataVersionGloas:
blockObj = block.Gloas.Message
default:
return phase0.Root{}, errors.New("unknown block version")
}
Expand All @@ -80,7 +101,7 @@ func (e *Encoder) GetBlockRoot(block *spec.VersionedSignedBeaconBlock) (root pha
return root, nil
}

func (e *Encoder) EncodeBlockSSZ(block *spec.VersionedSignedBeaconBlock) (ssz []byte, err error) {
func (e *Encoder) EncodeBlockSSZ(block *spec.VersionedSignedBeaconBlock) ([]byte, error) {
var blockObj sszutils.FastsszMarshaler

switch block.Version {
Expand All @@ -98,21 +119,13 @@ func (e *Encoder) EncodeBlockSSZ(block *spec.VersionedSignedBeaconBlock) (ssz []
blockObj = block.Electra
case spec.DataVersionFulu:
blockObj = block.Fulu
case spec.DataVersionGloas:
blockObj = block.Gloas
default:
return nil, errors.New("unknown block version")
}

if e.customPreset {
ssz, err = e.getDynamicSSZ().MarshalSSZ(blockObj)
} else {
ssz, err = blockObj.MarshalSSZ()
}

if err != nil {
return nil, err
}

return ssz, nil
return e.marshalSSZ(blockObj)
}

func (e *Encoder) EncodeBlockJSON(block *spec.VersionedSignedBeaconBlock) ([]byte, error) {
Expand All @@ -133,6 +146,8 @@ func (e *Encoder) EncodeBlockJSON(block *spec.VersionedSignedBeaconBlock) ([]byt
blockObj = block.Electra
case spec.DataVersionFulu:
blockObj = block.Fulu
case spec.DataVersionGloas:
blockObj = block.Gloas
default:
return nil, errors.New("unknown block version")
}
Expand Down Expand Up @@ -163,6 +178,8 @@ func (e *Encoder) GetStateRoot(beaconState *spec.VersionedBeaconState) (root pha
stateObj = beaconState.Electra
case spec.DataVersionFulu:
stateObj = beaconState.Fulu
case spec.DataVersionGloas:
stateObj = beaconState.Gloas
default:
return phase0.Root{}, errors.New("unknown state version")
}
Expand All @@ -179,7 +196,7 @@ func (e *Encoder) GetStateRoot(beaconState *spec.VersionedBeaconState) (root pha

return root, nil
}
func (e *Encoder) EncodeStateSSZ(beaconState *spec.VersionedBeaconState) (ssz []byte, err error) {
func (e *Encoder) EncodeStateSSZ(beaconState *spec.VersionedBeaconState) ([]byte, error) {
var stateObj sszutils.FastsszMarshaler

switch beaconState.Version {
Expand All @@ -197,19 +214,11 @@ func (e *Encoder) EncodeStateSSZ(beaconState *spec.VersionedBeaconState) (ssz []
stateObj = beaconState.Electra
case spec.DataVersionFulu:
stateObj = beaconState.Fulu
case spec.DataVersionGloas:
stateObj = beaconState.Gloas
default:
return nil, errors.New("unknown state version")
}

if e.customPreset {
ssz, err = e.getDynamicSSZ().MarshalSSZ(stateObj)
} else {
ssz, err = stateObj.MarshalSSZ()
}

if err != nil {
return nil, err
}

return ssz, nil
return e.marshalSSZ(stateObj)
}
2 changes: 2 additions & 0 deletions pkg/cache/ttl.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func NewTTLMap(maxItems int, name, namespace string) (m *TTLMap) {
go func() {
for now := range time.Tick(time.Second * 1) {
m.l.Lock()

for k, v := range m.m {
if v.invincible {
continue
Expand All @@ -49,6 +50,7 @@ func NewTTLMap(maxItems int, name, namespace string) (m *TTLMap) {
m.delete(k, v.value, v.expiresAt)
}
}

m.l.Unlock()
}
}()
Expand Down
1 change: 1 addition & 0 deletions pkg/eth/slot.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type SlotTime struct {
}

func CalculateSlotTime(slot phase0.Slot, genesisTime time.Time, durationPerSlot time.Duration) SlotTime {
//nolint:gosec // slot values are within safe range for time.Duration
slotStartTime := genesisTime.Add(time.Duration(slot) * durationPerSlot).UTC()

return SlotTime{
Expand Down
4 changes: 4 additions & 0 deletions pkg/service/eth/block_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ func NewSlotFromString(id string) (phase0.Slot, error) {
return 0, err
}

if slot < 0 {
return 0, errors.New("slot cannot be negative")
}

return phase0.Slot(slot), nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/service/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ func (h *Handler) BlobSidecars(ctx context.Context, blockID BlockIdentifier, ind

// Find the sidecar with the given index
for i, sidecar := range sidecars {
if index == int(sidecar.Index) {
if deneb.BlobIndex(index) == sidecar.Index {
filtered = append(filtered, sidecars[i])

break
Expand Down
2 changes: 2 additions & 0 deletions web/src/parts/slot/Slot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export default function Slot(props: { slot: number }) {
return data?.data?.block?.Electra;
case 'FULU':
return data?.data?.block?.Fulu;
case 'GLOAS':
return data?.data?.block?.Gloas;
case 'PHASE0':
return data?.data?.block?.Phase0;
}
Expand Down
3 changes: 2 additions & 1 deletion web/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,15 @@ export interface APIBeaconBlockMessage {
}

export interface APIBeaconBlock {
Version: 'BELLATRIX' | 'ALTAIR' | 'PHASE0' | 'CAPELLA' | 'DENEB' | 'ELECTRA' | 'FULU';
Version: 'BELLATRIX' | 'ALTAIR' | 'PHASE0' | 'CAPELLA' | 'DENEB' | 'ELECTRA' | 'FULU' | 'GLOAS';
Altair?: APIBeaconBlockMessage;
Bellatrix?: APIBeaconBlockMessage;
Capella?: APIBeaconBlockMessage;
Deneb?: APIBeaconBlockMessage;
Phase0?: APIBeaconBlockMessage;
Electra?: APIBeaconBlockMessage;
Fulu?: APIBeaconBlockMessage;
Gloas?: APIBeaconBlockMessage;
}

export interface APIBeaconSlotBlock {
Expand Down
Loading