Skip to content
Draft
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pk910/dynamic-ssz v0.0.4 // indirect
github.com/pk910/dynamic-ssz v0.0.6 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ 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 v0.0.6 h1:Tu97LSc2TtCyqRfoSbhG9XuR/FbA7CkKeAnlkgUydFY=
github.com/pk910/dynamic-ssz v0.0.6/go.mod h1:b6CrLaB2X7pYA+OSEEbkgXDEcRnjLOZIxZTsMuO/Y9c=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down
6 changes: 3 additions & 3 deletions pkg/api/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ type Handler struct {
metrics Metrics
}

func NewHandler(log logrus.FieldLogger, beac beacon.FinalityProvider, config *beacon.Config) *Handler {
func NewHandler(log logrus.FieldLogger, beac beacon.FinalityProvider, config *beacon.Config, hashTreeRoot func(block *spec.VersionedSignedBeaconBlock) ([32]byte, error)) *Handler {
return &Handler{
log: log.WithField("module", "api"),

eth: eth.NewHandler(log, beac, "checkpointz"),
checkpointz: checkpointz.NewHandler(log, beac),
eth: eth.NewHandler(log, beac, "checkpointz", hashTreeRoot),
checkpointz: checkpointz.NewHandler(log, beac, hashTreeRoot),
publicURL: config.Frontend.PublicURL,
brandName: config.Frontend.BrandName,
brandImageURL: config.Frontend.BrandImageURL,
Expand Down
90 changes: 82 additions & 8 deletions pkg/beacon/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (
"sync"
"time"

"github.com/attestantio/go-eth2-client/api"
v1 "github.com/attestantio/go-eth2-client/api/v1"
"github.com/attestantio/go-eth2-client/http"
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/deneb"
"github.com/attestantio/go-eth2-client/spec/phase0"
Expand All @@ -21,7 +23,9 @@ import (
"github.com/ethpandaops/checkpointz/pkg/eth"
"github.com/ethpandaops/ethwallclock"
"github.com/go-co-op/gocron"
dynssz "github.com/pk910/dynamic-ssz"
perrors "github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -52,6 +56,9 @@ type Default struct {
majorityMutex sync.Mutex

metrics *Metrics

dynSsz *dynssz.DynSsz
dynSszMutex sync.Mutex
}

var _ FinalityProvider = (*Default)(nil)
Expand All @@ -66,7 +73,7 @@ const (
FinalityHaltedServingPeriod = 14 * 24 * time.Hour
)

func NewDefaultProvider(namespace string, log logrus.FieldLogger, nodes []node.Config, config *Config) FinalityProvider {
func NewDefaultProvider(namespace string, log logrus.FieldLogger, nodes []node.Config, config *Config) *Default {
return &Default{
nodeConfigs: nodes,
log: log.WithField("module", "beacon/default"),
Expand Down Expand Up @@ -441,6 +448,13 @@ func (d *Default) setSpec(s *state.Spec) {
d.spec = s
}

func (d *Default) setDynSsz(dynSsz *dynssz.DynSsz) {
d.dynSszMutex.Lock()
defer d.dynSszMutex.Unlock()

d.dynSsz = dynSsz
}

func (d *Default) Spec() (*state.Spec, error) {
d.specMutex.Lock()
defer d.specMutex.Unlock()
Expand All @@ -454,6 +468,65 @@ func (d *Default) Spec() (*state.Spec, error) {
return &copied, nil
}

func (d *Default) NewDynSsz() (*dynssz.DynSsz, error) {
ctx := context.Background()

for _, node := range d.nodes {
if !node.Beacon.Status().Healthy() {
continue
}

client, err := http.New(ctx, http.WithAddress(node.Config.Address), http.WithLogLevel(zerolog.Disabled))
if err != nil {
continue
}

spec, err := client.(*http.Service).Spec(ctx, &api.SpecOpts{})
if err != nil {
continue
}

return dynssz.NewDynSsz(spec.Data), nil
}

return nil, errors.New("no healthy nodes")
}

func (d *Default) DynSsz() (*dynssz.DynSsz, error) {
d.dynSszMutex.Lock()
defer d.dynSszMutex.Unlock()

if d.dynSsz == nil {
return nil, errors.New("DynSsz spec not yet available")
}

return d.dynSsz, nil
}

func (d *Default) HashTreeRoot(block *spec.VersionedSignedBeaconBlock) ([32]byte, error) {
ssz, err := d.DynSsz()
if err != nil {
return [32]byte{}, err
}

switch block.Version {
case spec.DataVersionPhase0:
return ssz.HashTreeRoot(block.Phase0.Message)
case spec.DataVersionAltair:
return ssz.HashTreeRoot(block.Altair.Message)
case spec.DataVersionBellatrix:
return ssz.HashTreeRoot(block.Bellatrix.Message)
case spec.DataVersionCapella:
return ssz.HashTreeRoot(block.Capella.Message)
case spec.DataVersionDeneb:
return ssz.HashTreeRoot(block.Deneb.Message)
case spec.DataVersionElectra:
return ssz.HashTreeRoot(block.Electra.Message)
default:
return [32]byte{}, errors.New("unknown version")
}
}

func (d *Default) OperatingMode() OperatingMode {
return d.config.Mode
}
Expand Down Expand Up @@ -514,8 +587,14 @@ func (d *Default) refreshSpec(ctx context.Context) error {
return err
}

dynSsz, err := d.NewDynSsz()
if err != nil {
return err
}

// store the beacon state spec
d.setSpec(s)
d.setDynSsz(dynSsz)

d.log.Debug("Fetched beacon spec")

Expand Down Expand Up @@ -646,11 +725,6 @@ func (d *Default) GetBeaconStateByRoot(ctx context.Context, root phase0.Root) (*
}

func (d *Default) storeBlock(_ context.Context, block *spec.VersionedSignedBeaconBlock) error {
_, err := d.Spec()
if err != nil {
return err
}

if d.genesis == nil {
return errors.New("genesis time is unknown")
}
Expand All @@ -659,7 +733,7 @@ func (d *Default) storeBlock(_ context.Context, block *spec.VersionedSignedBeaco
return errors.New("block is nil")
}

root, err := block.Root()
root, err := d.HashTreeRoot(block)
if err != nil {
return err
}
Expand All @@ -680,7 +754,7 @@ func (d *Default) storeBlock(_ context.Context, block *spec.VersionedSignedBeaco
expiresAt = time.Now().Add(999999 * time.Hour)
}

if err := d.blocks.Add(block, expiresAt); err != nil {
if err := d.blocks.Add(block, expiresAt, root); err != nil {
return err
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/beacon/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (d *Default) checkGenesis(ctx context.Context) error {
return errors.New("invalid genesis block")
}

genesisBlockRoot, err := genesisBlock.Root()
genesisBlockRoot, err := d.HashTreeRoot(genesisBlock)
if err != nil {
return err
}
Expand Down Expand Up @@ -256,7 +256,7 @@ func (d *Default) downloadBlock(ctx context.Context, slot phase0.Slot, upstream
return nil, err
}

root, err := block.Root()
root, err := d.HashTreeRoot(block)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -298,7 +298,7 @@ func (d *Default) fetchBundle(ctx context.Context, root phase0.Root, upstream *N
return nil, fmt.Errorf("failed to get state root from block: %w", err)
}

blockRoot, err := block.Root()
blockRoot, err := d.HashTreeRoot(block)
if err != nil {
return nil, fmt.Errorf("failed to get block root from block: %w", err)
}
Expand Down
7 changes: 1 addition & 6 deletions pkg/beacon/store/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,7 @@ func NewBlock(log logrus.FieldLogger, config Config, namespace string) *Block {
return c
}

func (c *Block) Add(block *spec.VersionedSignedBeaconBlock, expiresAt time.Time) error {
root, err := block.Root()
if err != nil {
return err
}

func (c *Block) Add(block *spec.VersionedSignedBeaconBlock, expiresAt time.Time, root [32]byte) error {
slot, err := block.Slot()
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion pkg/checkpointz/checkpointz.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func NewServer(log *logrus.Logger, conf *Config) *Server {
Cfg: *conf,
log: log,

http: api.NewHandler(log, provider, &conf.Checkpointz),
http: api.NewHandler(log, provider, &conf.Checkpointz, provider.HashTreeRoot),

provider: provider,
}
Expand Down
12 changes: 8 additions & 4 deletions pkg/service/checkpointz/checkpointz.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package checkpointz
import (
"context"

"github.com/attestantio/go-eth2-client/spec"
"github.com/ethpandaops/checkpointz/pkg/beacon"
"github.com/ethpandaops/checkpointz/pkg/eth"
"github.com/ethpandaops/checkpointz/pkg/version"
Expand All @@ -14,13 +15,16 @@ import (
type Handler struct {
log logrus.FieldLogger
provider beacon.FinalityProvider

hashTreeRoot func(block *spec.VersionedSignedBeaconBlock) ([32]byte, error)
}

// NewHandler returns a new Handler instance.
func NewHandler(log logrus.FieldLogger, beac beacon.FinalityProvider) *Handler {
func NewHandler(log logrus.FieldLogger, beac beacon.FinalityProvider, hashTreeRoot func(block *spec.VersionedSignedBeaconBlock) ([32]byte, error)) *Handler {
return &Handler{
log: log.WithField("module", "api/checkpointz"),
provider: beac,
log: log.WithField("module", "api/checkpointz"),
provider: beac,
hashTreeRoot: hashTreeRoot,
}
}

Expand Down Expand Up @@ -72,7 +76,7 @@ func (h *Handler) V1BeaconSlots(ctx context.Context, req *BeaconSlotsRequest) (*
}

if block, err := h.provider.GetBlockBySlot(ctx, slot.Slot); err == nil {
if blockRoot, err := block.Root(); err == nil {
if blockRoot, err := h.hashTreeRoot(block); err == nil {
slot.BlockRoot = eth.RootAsString(blockRoot)
}

Expand Down
14 changes: 9 additions & 5 deletions pkg/service/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@ type Handler struct {
provider beacon.FinalityProvider

metrics *Metrics

hashTreeRoot func(block *spec.VersionedSignedBeaconBlock) ([32]byte, error)
}

// NewHandler returns a new Handler instance.
func NewHandler(log logrus.FieldLogger, beac beacon.FinalityProvider, namespace string) *Handler {
func NewHandler(log logrus.FieldLogger, beac beacon.FinalityProvider, namespace string, hashTreeRoot func(block *spec.VersionedSignedBeaconBlock) ([32]byte, error)) *Handler {
return &Handler{
log: log.WithField("module", "service/eth"),
provider: beac,

metrics: NewMetrics(namespace),

hashTreeRoot: hashTreeRoot,
}
}

Expand Down Expand Up @@ -380,7 +384,7 @@ func (h *Handler) BlockRoot(ctx context.Context, blockID BlockIdentifier) (phase
return phase0.Root{}, fmt.Errorf("no genesis block")
}

return block.Root()
return h.hashTreeRoot(block)
case BlockIDSlot:
slot, err := NewSlotFromString(blockID.Value())
if err != nil {
Expand All @@ -396,7 +400,7 @@ func (h *Handler) BlockRoot(ctx context.Context, blockID BlockIdentifier) (phase
return phase0.Root{}, fmt.Errorf("no block for slot %v", slot)
}

return block.Root()
return h.hashTreeRoot(block)
case BlockIDRoot:
root, err := blockID.AsRoot()
if err != nil {
Expand All @@ -412,7 +416,7 @@ func (h *Handler) BlockRoot(ctx context.Context, blockID BlockIdentifier) (phase
return phase0.Root{}, fmt.Errorf("no block for root %v", root)
}

return block.Root()
return h.hashTreeRoot(block)
case BlockIDFinalized:
finality, err := h.provider.Finalized(ctx)
if err != nil {
Expand All @@ -432,7 +436,7 @@ func (h *Handler) BlockRoot(ctx context.Context, blockID BlockIdentifier) (phase
return phase0.Root{}, fmt.Errorf("no block for finalized root %v", finality.Finalized.Root)
}

return block.Root()
return h.hashTreeRoot(block)
default:
return phase0.Root{}, fmt.Errorf("invalid block id: %v", blockID.String())
}
Expand Down