Skip to content

Commit 1c81f68

Browse files
authored
add support for non-mainnet presets (#226)
* add support for non-mainnet presets * fix for block root calculation * fix linter issues * fix linter issues * fix linter issues * fix linter issues * add setting to enable custom preset support * bump `github.com/ethpandaops/beacon` * add `custom_preset` to README * increase itegration test timeout * do not pass `WithCustomSpecSupport` to go-eth2-client with custon_preset disabled
1 parent 94de39a commit 1c81f68

File tree

17 files changed

+277
-90
lines changed

17 files changed

+277
-90
lines changed

.github/workflows/integration.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- {consensus: nimbus, network: hoodi, image: 'statusim/nimbus-eth2:amd64-v25.11.0'}
2727
- {consensus: lodestar, network: hoodi, image: 'chainsafe/lodestar:v1.36.0'}
2828
runs-on: ubuntu-latest
29-
timeout-minutes: 10
29+
timeout-minutes: 20
3030
steps:
3131
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
3232
- name: Print details

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ global:
149149

150150
checkpointz:
151151
mode: light
152+
custom_preset: false # Enable this for non-mainnet presets
152153
caches:
153154
blocks:
154155
# Controls the amount of "block" items that can be stored by Checkpointz (minimum 3)

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ require (
66
github.com/attestantio/go-eth2-client v0.27.2
77
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9
88
github.com/creasty/defaults v1.6.0
9-
github.com/ethpandaops/beacon v0.65.0
9+
github.com/ethpandaops/beacon v0.66.0
1010
github.com/ethpandaops/ethwallclock v0.2.0
1111
github.com/go-co-op/gocron v1.18.0
1212
github.com/julienschmidt/httprouter v1.3.0
1313
github.com/nanmu42/gzip v1.2.0
14+
github.com/pk910/dynamic-ssz v1.1.1
1415
github.com/pkg/errors v0.9.1
1516
github.com/prometheus/client_golang v1.23.2
1617
github.com/sirupsen/logrus v1.9.3
@@ -20,7 +21,9 @@ require (
2021
)
2122

2223
require (
24+
github.com/OffchainLabs/hashtree v0.2.1-0.20250530191054-577f0b75c7f7 // indirect
2325
github.com/beorn7/perks v1.0.1 // indirect
26+
github.com/casbin/govaluate v1.8.0 // indirect
2427
github.com/cespare/xxhash/v2 v2.3.0 // indirect
2528
github.com/davecgh/go-spew v1.1.1 // indirect
2629
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
@@ -52,7 +55,6 @@ require (
5255
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
5356
github.com/modern-go/reflect2 v1.0.2 // indirect
5457
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
55-
github.com/pk910/dynamic-ssz v0.0.4 // indirect
5658
github.com/pmezard/go-difflib v1.0.0 // indirect
5759
github.com/prometheus/client_model v0.6.2 // indirect
5860
github.com/prometheus/common v0.66.1 // indirect

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
github.com/OffchainLabs/hashtree v0.2.1-0.20250530191054-577f0b75c7f7 h1:0r1HjExe/tyypkt380UTpjvILd5kLw51Xzl6a+hknQ8=
2+
github.com/OffchainLabs/hashtree v0.2.1-0.20250530191054-577f0b75c7f7/go.mod h1:b07+cRZs+eAR8TR57CB9TQlt5Gnl/06Xs76xt/1wq0M=
13
github.com/attestantio/go-eth2-client v0.27.2 h1:VjA9R39ovy8ryb7IpFfD5eLYBg/20biztxh6fKZ7/K0=
24
github.com/attestantio/go-eth2-client v0.27.2/go.mod h1:i56XBegxVt7wXupnLBOj9IyGwy5cqaoTsCSKlwTubEU=
35
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
46
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
7+
github.com/casbin/govaluate v1.8.0 h1:1dUaV/I0LFP2tcY1uNQEb6wBCbp8GMTcC/zhwQDWvZo=
8+
github.com/casbin/govaluate v1.8.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
59
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
610
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
711
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4=
@@ -24,6 +28,10 @@ github.com/ethereum/go-ethereum v1.16.4 h1:H6dU0r2p/amA7cYg6zyG9Nt2JrKKH6oX2utfc
2428
github.com/ethereum/go-ethereum v1.16.4/go.mod h1:P7551slMFbjn2zOQaKrJShZVN/d8bGxp4/I6yZVlb5w=
2529
github.com/ethpandaops/beacon v0.65.0 h1:ssnab73uiuzhmhtU56q9D3ecVrEYv5n+tuqrXRK4YAg=
2630
github.com/ethpandaops/beacon v0.65.0/go.mod h1:lgzrJjQVV77wZ+PJymsY3bQbAK4jrtP8n3WOwMf1Pcs=
31+
github.com/ethpandaops/beacon v0.65.1-0.20251204024030-639105ffe6c2 h1:WU40omAqHrPF/Mfovc9+eMCrTAl8Km/KOanyYqr3SBg=
32+
github.com/ethpandaops/beacon v0.65.1-0.20251204024030-639105ffe6c2/go.mod h1:lgzrJjQVV77wZ+PJymsY3bQbAK4jrtP8n3WOwMf1Pcs=
33+
github.com/ethpandaops/beacon v0.66.0 h1:BRnf4yTEzkZwHW6sTp1x+mBoO5pwbQOX6wtLt3Nh1Y4=
34+
github.com/ethpandaops/beacon v0.66.0/go.mod h1:lgzrJjQVV77wZ+PJymsY3bQbAK4jrtP8n3WOwMf1Pcs=
2735
github.com/ethpandaops/ethwallclock v0.2.0 h1:EeFKtZ7v6TAdn/oAh0xaPujD7N4amjBxrWIByraUfLM=
2836
github.com/ethpandaops/ethwallclock v0.2.0/go.mod h1:y0Cu+mhGLlem19vnAV2x0hpFS5KZ7oOi2SWYayv9l24=
2937
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
@@ -154,6 +162,8 @@ github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
154162
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
155163
github.com/pk910/dynamic-ssz v0.0.4 h1:DT29+1055tCEPCaR4V/ez+MOKW7BzBsmjyFvBRqx0ME=
156164
github.com/pk910/dynamic-ssz v0.0.4/go.mod h1:b6CrLaB2X7pYA+OSEEbkgXDEcRnjLOZIxZTsMuO/Y9c=
165+
github.com/pk910/dynamic-ssz v1.1.1 h1:b8sPR8fyhBvz8SHa2RH20SNtt5VDzAEY6fKsPCUcYX4=
166+
github.com/pk910/dynamic-ssz v1.1.1/go.mod h1:3zyemisUysY2PWACZ8LeZS2tAw8AkuTb2GaLmqYsg1I=
157167
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
158168
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
159169
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

pkg/api/handler.go

Lines changed: 12 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import (
1010
"strings"
1111
"time"
1212

13-
"github.com/attestantio/go-eth2-client/spec"
1413
"github.com/ethpandaops/checkpointz/pkg/beacon"
14+
"github.com/ethpandaops/checkpointz/pkg/beacon/ssz"
1515
"github.com/ethpandaops/checkpointz/pkg/service/checkpointz"
1616
"github.com/ethpandaops/checkpointz/pkg/service/eth"
1717
"github.com/julienschmidt/httprouter"
@@ -26,6 +26,7 @@ type Handler struct {
2626

2727
eth *eth.Handler
2828
checkpointz *checkpointz.Handler
29+
sszEncoder *ssz.Encoder
2930
publicURL string
3031
brandName string
3132
brandImageURL string
@@ -39,6 +40,7 @@ func NewHandler(log logrus.FieldLogger, beac beacon.FinalityProvider, config *be
3940

4041
eth: eth.NewHandler(log, beac, "checkpointz"),
4142
checkpointz: checkpointz.NewHandler(log, beac),
43+
sszEncoder: beac.SSZEncoder(),
4244
publicURL: config.Frontend.PublicURL,
4345
brandName: config.Frontend.BrandName,
4446
brandImageURL: config.Frontend.BrandImageURL,
@@ -171,47 +173,14 @@ func (h *Handler) handleEthV2BeaconBlocks(ctx context.Context, r *http.Request,
171173
return NewInternalServerErrorResponse(nil), err
172174
}
173175

174-
var rsp = &HTTPResponse{}
175-
176-
switch strings.ToLower(block.Version.String()) {
177-
case spec.DataVersionPhase0.String():
178-
rsp = NewSuccessResponse(ContentTypeResolvers{
179-
ContentTypeJSON: block.Phase0.MarshalJSON,
180-
ContentTypeSSZ: block.Phase0.MarshalSSZ,
181-
})
182-
case spec.DataVersionAltair.String():
183-
rsp = NewSuccessResponse(ContentTypeResolvers{
184-
ContentTypeJSON: block.Altair.MarshalJSON,
185-
ContentTypeSSZ: block.Altair.MarshalSSZ,
186-
})
187-
case spec.DataVersionBellatrix.String():
188-
rsp = NewSuccessResponse(ContentTypeResolvers{
189-
ContentTypeJSON: block.Bellatrix.MarshalJSON,
190-
ContentTypeSSZ: block.Bellatrix.MarshalSSZ,
191-
})
192-
case spec.DataVersionCapella.String():
193-
rsp = NewSuccessResponse(ContentTypeResolvers{
194-
ContentTypeJSON: block.Capella.MarshalJSON,
195-
ContentTypeSSZ: block.Capella.MarshalSSZ,
196-
})
197-
case spec.DataVersionDeneb.String():
198-
rsp = NewSuccessResponse(ContentTypeResolvers{
199-
ContentTypeJSON: block.Deneb.MarshalJSON,
200-
ContentTypeSSZ: block.Deneb.MarshalSSZ,
201-
})
202-
case spec.DataVersionElectra.String():
203-
rsp = NewSuccessResponse(ContentTypeResolvers{
204-
ContentTypeJSON: block.Electra.MarshalJSON,
205-
ContentTypeSSZ: block.Electra.MarshalSSZ,
206-
})
207-
case spec.DataVersionFulu.String():
208-
rsp = NewSuccessResponse(ContentTypeResolvers{
209-
ContentTypeJSON: block.Fulu.MarshalJSON,
210-
ContentTypeSSZ: block.Fulu.MarshalSSZ,
211-
})
212-
default:
213-
return NewInternalServerErrorResponse(nil), errors.New("unknown block version")
214-
}
176+
rsp := NewSuccessResponse(ContentTypeResolvers{
177+
ContentTypeJSON: func() ([]byte, error) {
178+
return h.sszEncoder.EncodeBlockJSON(block)
179+
},
180+
ContentTypeSSZ: func() ([]byte, error) {
181+
return h.sszEncoder.EncodeBlockSSZ(block)
182+
},
183+
})
215184

216185
rsp.AddExtraData("version", block.Version.String())
217186
rsp.AddExtraData("execution_optimistic", false)
@@ -251,24 +220,7 @@ func (h *Handler) handleEthV2DebugBeaconStates(ctx context.Context, r *http.Requ
251220

252221
rsp := NewSuccessResponse(ContentTypeResolvers{
253222
ContentTypeSSZ: func() ([]byte, error) {
254-
switch strings.ToLower(state.Version.String()) {
255-
case spec.DataVersionPhase0.String():
256-
return state.Phase0.MarshalSSZ()
257-
case spec.DataVersionAltair.String():
258-
return state.Altair.MarshalSSZ()
259-
case spec.DataVersionBellatrix.String():
260-
return state.Bellatrix.MarshalSSZ()
261-
case spec.DataVersionCapella.String():
262-
return state.Capella.MarshalSSZ()
263-
case spec.DataVersionDeneb.String():
264-
return state.Deneb.MarshalSSZ()
265-
case spec.DataVersionElectra.String():
266-
return state.Electra.MarshalSSZ()
267-
case "fulu":
268-
return state.Fulu.MarshalSSZ()
269-
default:
270-
return nil, fmt.Errorf("unknown state version: %s", state.Version.String())
271-
}
223+
return h.sszEncoder.EncodeStateSSZ(state)
272224
},
273225
})
274226

pkg/beacon/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
type Config struct {
1212
// Mode sets the operational mode of the provider.
1313
Mode OperatingMode `yaml:"mode" default:"light"`
14+
// CustomPreset enables the use of a custom preset for the provider.
15+
CustomPreset bool `yaml:"custom_preset" default:"false"`
1416
// Cache holds configuration for the caches.
1517
Caches CacheConfig `yaml:"caches"`
1618

pkg/beacon/default.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/ethpandaops/beacon/pkg/beacon/state"
1818
"github.com/ethpandaops/checkpointz/pkg/beacon/checkpoints"
1919
"github.com/ethpandaops/checkpointz/pkg/beacon/node"
20+
"github.com/ethpandaops/checkpointz/pkg/beacon/ssz"
2021
"github.com/ethpandaops/checkpointz/pkg/beacon/store"
2122
"github.com/ethpandaops/checkpointz/pkg/eth"
2223
"github.com/ethpandaops/ethwallclock"
@@ -32,6 +33,7 @@ type Default struct {
3233
nodeConfigs []node.Config
3334
nodes Nodes
3435
broker *emission.Emitter
36+
sszEncoder *ssz.Encoder
3537

3638
head *v1.Finality
3739
servingBundle *v1.Finality
@@ -70,7 +72,7 @@ func NewDefaultProvider(namespace string, log logrus.FieldLogger, nodes []node.C
7072
return &Default{
7173
nodeConfigs: nodes,
7274
log: log.WithField("module", "beacon/default"),
73-
nodes: NewNodesFromConfig(log, nodes, namespace),
75+
nodes: NewNodesFromConfig(log, nodes, namespace, config.CustomPreset),
7476
config: config,
7577

7678
head: &v1.Finality{},
@@ -79,6 +81,7 @@ func NewDefaultProvider(namespace string, log logrus.FieldLogger, nodes []node.C
7981
historicalSlotFailures: make(map[phase0.Slot]int),
8082

8183
broker: emission.NewEmitter(),
84+
sszEncoder: ssz.NewEncoder(config.CustomPreset),
8285
blocks: store.NewBlock(log, config.Caches.Blocks, namespace),
8386
states: store.NewBeaconState(log, config.Caches.States, namespace),
8487
depositSnapshots: store.NewDepositSnapshot(log, config.Caches.DepositSnapshots, namespace),
@@ -439,6 +442,7 @@ func (d *Default) setSpec(s *state.Spec) {
439442
defer d.specMutex.Unlock()
440443

441444
d.spec = s
445+
d.sszEncoder.SetSpec(s)
442446
}
443447

444448
func (d *Default) Spec() (*state.Spec, error) {
@@ -454,6 +458,10 @@ func (d *Default) Spec() (*state.Spec, error) {
454458
return &copied, nil
455459
}
456460

461+
func (d *Default) SSZEncoder() *ssz.Encoder {
462+
return d.sszEncoder
463+
}
464+
457465
func (d *Default) OperatingMode() OperatingMode {
458466
return d.config.Mode
459467
}
@@ -659,7 +667,7 @@ func (d *Default) storeBlock(_ context.Context, block *spec.VersionedSignedBeaco
659667
return errors.New("block is nil")
660668
}
661669

662-
root, err := block.Root()
670+
root, err := d.sszEncoder.GetBlockRoot(block)
663671
if err != nil {
664672
return err
665673
}
@@ -680,7 +688,7 @@ func (d *Default) storeBlock(_ context.Context, block *spec.VersionedSignedBeaco
680688
expiresAt = time.Now().Add(999999 * time.Hour)
681689
}
682690

683-
if err := d.blocks.Add(block, expiresAt); err != nil {
691+
if err := d.blocks.Add(root, block, expiresAt); err != nil {
684692
return err
685693
}
686694

@@ -742,7 +750,6 @@ func (d *Default) ListFinalizedSlots(ctx context.Context) ([]phase0.Slot, error)
742750

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

745-
//nolint:gosec // This is not a security issue
746753
for i, val := uint64(latestSlot), uint64(latestSlot)-uint64(sp.SlotsPerEpoch)*uint64(d.config.HistoricalEpochCount); i > val; i -= uint64(sp.SlotsPerEpoch) {
747754
slots = append(slots, phase0.Slot(i))
748755
}

pkg/beacon/download.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (d *Default) checkGenesis(ctx context.Context) error {
115115
return errors.New("invalid genesis block")
116116
}
117117

118-
genesisBlockRoot, err := genesisBlock.Root()
118+
genesisBlockRoot, err := d.sszEncoder.GetBlockRoot(genesisBlock)
119119
if err != nil {
120120
return err
121121
}
@@ -173,12 +173,12 @@ func (d *Default) fetchHistoricalCheckpoints(ctx context.Context, checkpoint *v1
173173
// We'll derive the current finalized slot and then work back in intervals of SLOTS_PER_EPOCH.
174174
currentSlot := uint64(checkpoint.Finalized.Epoch) * uint64(sp.SlotsPerEpoch)
175175
for i := 1; i < d.config.HistoricalEpochCount; i++ {
176-
//nolint:gosec // This is not a security issue
177176
if uint64(i)*uint64(sp.SlotsPerEpoch) > currentSlot {
178177
break
179178
}
180-
//nolint:gosec // This is not a security issue
179+
181180
slot := phase0.Slot(currentSlot - uint64(i)*uint64(sp.SlotsPerEpoch))
181+
182182
slotsInScope[slot] = struct{}{}
183183
}
184184

@@ -260,7 +260,7 @@ func (d *Default) downloadBlock(ctx context.Context, slot phase0.Slot, upstream
260260
return nil, err
261261
}
262262

263-
root, err := block.Root()
263+
root, err := d.sszEncoder.GetBlockRoot(block)
264264
if err != nil {
265265
return nil, err
266266
}
@@ -302,13 +302,13 @@ func (d *Default) fetchBundle(ctx context.Context, root phase0.Root, upstream *N
302302
return nil, fmt.Errorf("failed to get state root from block: %w", err)
303303
}
304304

305-
blockRoot, err := block.Root()
305+
blockRoot, err := d.sszEncoder.GetBlockRoot(block)
306306
if err != nil {
307307
return nil, fmt.Errorf("failed to get block root from block: %w", err)
308308
}
309309

310310
if blockRoot != root {
311-
return nil, errors.New("block root does not match")
311+
return nil, fmt.Errorf("block root does not match: %#x != %#x", blockRoot, root)
312312
}
313313

314314
slot, err := block.Slot()

pkg/beacon/expire_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,10 @@ var (
1313
)
1414

1515
func CalculateSlotExpiration(slot phase0.Slot, slotsOfHistory int) phase0.Slot {
16-
//nolint:gosec // This is not a security issue
1716
return slot + phase0.Slot(slotsOfHistory)
1817
}
1918

2019
func GetSlotTime(slot phase0.Slot, secondsPerSlot time.Duration, genesis time.Time) time.Time {
21-
//nolint:gosec // This is not a security issue
2220
return genesis.Add(time.Duration(slot) * secondsPerSlot)
2321
}
2422

pkg/beacon/finality_provider.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/attestantio/go-eth2-client/spec/phase0"
1010
"github.com/ethpandaops/beacon/pkg/beacon/api/types"
1111
"github.com/ethpandaops/beacon/pkg/beacon/state"
12+
"github.com/ethpandaops/checkpointz/pkg/beacon/ssz"
1213
"github.com/ethpandaops/checkpointz/pkg/eth"
1314
)
1415

@@ -34,6 +35,8 @@ type FinalityProvider interface {
3435
Genesis(ctx context.Context) (*v1.Genesis, error)
3536
// Spec returns the chain spec.
3637
Spec() (*state.Spec, error)
38+
// SSZEncoder returns the SSZ encoder for the provider.
39+
SSZEncoder() *ssz.Encoder
3740
// UpstreamsStatus returns the status of all the upstreams.
3841
UpstreamsStatus(ctx context.Context) (map[string]*UpstreamStatus, error)
3942
// GetBlockBySlot returns the block at the given slot.

0 commit comments

Comments
 (0)