Skip to content

Commit

Permalink
feat: Update LightClient methods to use structured responses
Browse files Browse the repository at this point in the history
  • Loading branch information
samcm committed Oct 23, 2024
1 parent 30947fa commit d1834c1
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 30 deletions.
87 changes: 58 additions & 29 deletions pkg/beacon/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ type ConsensusClient interface {
RawDebugBeaconState(ctx context.Context, stateID string, contentType string) ([]byte, error)
DepositSnapshot(ctx context.Context) (*types.DepositSnapshot, error)
NodeIdentity(ctx context.Context) (*types.Identity, error)
LightClientBootstrap(ctx context.Context, blockRoot string) (*lightclient.Bootstrap, error)
LightClientUpdate(ctx context.Context, startPeriod, count int) (*lightclient.Update, error)
LightClientFinalityUpdate(ctx context.Context) (*lightclient.FinalityUpdate, error)
LightClientOptimisticUpdate(ctx context.Context) (*lightclient.OptimisticUpdate, error)
LightClientBootstrap(ctx context.Context, blockRoot string) (*LightClientBootstrapResponse, error)
LightClientUpdates(ctx context.Context, startPeriod, count int) (*LightClientUpdatesResponse, error)
LightClientFinalityUpdate(ctx context.Context) (*LightClientFinalityUpdateResponse, error)
LightClientOptimisticUpdate(ctx context.Context) (*LightClientOptimisticUpdateResponse, error)
}

type consensusClient struct {
Expand All @@ -47,12 +47,13 @@ func NewConsensusClient(ctx context.Context, log logrus.FieldLogger, url string,
}
}

type apiResponse struct {
Data json.RawMessage `json:"data"`
type BeaconAPIResponse struct {
Data json.RawMessage `json:"data"`
Version string `json:"version"`
}

//nolint:unused // this is used in the future
func (c *consensusClient) post(ctx context.Context, path string, body map[string]interface{}) (json.RawMessage, error) {
func (c *consensusClient) post(ctx context.Context, path string, body map[string]interface{}) (*BeaconAPIResponse, error) {
jsonData, err := json.Marshal(body)
if err != nil {
return nil, err
Expand Down Expand Up @@ -84,16 +85,16 @@ func (c *consensusClient) post(ctx context.Context, path string, body map[string
return nil, err
}

resp := new(apiResponse)
resp := new(BeaconAPIResponse)
if err := json.Unmarshal(data, resp); err != nil {
return nil, err
}

return resp.Data, nil
return resp, nil
}

//nolint:unparam // ctx will probably be used in the future
func (c *consensusClient) get(ctx context.Context, path string) (json.RawMessage, error) {
func (c *consensusClient) get(ctx context.Context, path string) (*BeaconAPIResponse, error) {
req, err := http.NewRequestWithContext(ctx, "GET", c.url+path, nil)
if err != nil {
return nil, err
Expand All @@ -120,12 +121,12 @@ func (c *consensusClient) get(ctx context.Context, path string) (json.RawMessage
return nil, err
}

resp := new(apiResponse)
resp := new(BeaconAPIResponse)
if err := json.Unmarshal(data, resp); err != nil {
return nil, err
}

return resp.Data, nil
return resp, nil
}

func (c *consensusClient) getRaw(ctx context.Context, path string, contentType string) ([]byte, error) {
Expand Down Expand Up @@ -171,7 +172,7 @@ func (c *consensusClient) NodePeers(ctx context.Context) (types.Peers, error) {
}

rsp := types.Peers{}
if err := json.Unmarshal(data, &rsp); err != nil {
if err := json.Unmarshal(data.Data, &rsp); err != nil {
return nil, err
}

Expand All @@ -186,7 +187,7 @@ func (c *consensusClient) NodePeer(ctx context.Context, peerID string) (types.Pe
}

rsp := types.Peer{}
if err := json.Unmarshal(data, &rsp); err != nil {
if err := json.Unmarshal(data.Data, &rsp); err != nil {
return types.Peer{}, err
}

Expand All @@ -201,7 +202,7 @@ func (c *consensusClient) NodePeerCount(ctx context.Context) (types.PeerCount, e
}

rsp := types.PeerCount{}
if err := json.Unmarshal(data, &rsp); err != nil {
if err := json.Unmarshal(data.Data, &rsp); err != nil {
return types.PeerCount{}, err
}

Expand Down Expand Up @@ -236,7 +237,7 @@ func (c *consensusClient) DepositSnapshot(ctx context.Context) (*types.DepositSn
}

rsp := types.DepositSnapshot{}
if err := json.Unmarshal(data, &rsp); err != nil {
if err := json.Unmarshal(data.Data, &rsp); err != nil {
return nil, err
}

Expand All @@ -250,28 +251,35 @@ func (c *consensusClient) NodeIdentity(ctx context.Context) (*types.Identity, er
}

rsp := types.Identity{}
if err := json.Unmarshal(data, &rsp); err != nil {
if err := json.Unmarshal(data.Data, &rsp); err != nil {
return nil, err
}

return &rsp, nil
}

func (c *consensusClient) LightClientBootstrap(ctx context.Context, blockRoot string) (*lightclient.Bootstrap, error) {
func (c *consensusClient) LightClientBootstrap(ctx context.Context, blockRoot string) (*LightClientBootstrapResponse, error) {
data, err := c.get(ctx, fmt.Sprintf("/eth/v1/beacon/light_client/bootstrap/%s", blockRoot))
if err != nil {
return nil, err
}

rsp := lightclient.Bootstrap{}
if err := json.Unmarshal(data, &rsp); err != nil {
rsp := LightClientBootstrapResponse{
Response: Response[*lightclient.Bootstrap]{
Data: &lightclient.Bootstrap{},
Metadata: map[string]any{
"version": data.Version,
},
},
}
if err := json.Unmarshal(data.Data, &rsp.Data); err != nil {
return nil, err
}

return &rsp, nil
}

func (c *consensusClient) LightClientUpdate(ctx context.Context, startPeriod, count int) (*lightclient.Update, error) {
func (c *consensusClient) LightClientUpdates(ctx context.Context, startPeriod, count int) (*LightClientUpdatesResponse, error) {
if count == 0 {
return nil, errors.New("count must be greater than 0")
}
Expand All @@ -285,36 +293,57 @@ func (c *consensusClient) LightClientUpdate(ctx context.Context, startPeriod, co
return nil, err
}

rsp := lightclient.Update{}
if err := json.Unmarshal(data, &rsp); err != nil {
rsp := LightClientUpdatesResponse{
Response: Response[*lightclient.Updates]{
Data: &lightclient.Updates{},
Metadata: map[string]any{
"version": data.Version,
},
},
}
if err := json.Unmarshal(data.Data, &rsp.Data); err != nil {
return nil, err
}

return &rsp, nil
}

func (c *consensusClient) LightClientFinalityUpdate(ctx context.Context) (*lightclient.FinalityUpdate, error) {
func (c *consensusClient) LightClientFinalityUpdate(ctx context.Context) (*LightClientFinalityUpdateResponse, error) {
data, err := c.get(ctx, "/eth/v1/beacon/light_client/finality_update")
if err != nil {
return nil, err
}

rsp := lightclient.FinalityUpdate{}
if err := json.Unmarshal(data, &rsp); err != nil {
rsp := LightClientFinalityUpdateResponse{
Response: Response[*lightclient.FinalityUpdate]{
Data: &lightclient.FinalityUpdate{},
Metadata: map[string]any{
"version": data.Version,
},
},
}
if err := json.Unmarshal(data.Data, &rsp.Data); err != nil {
return nil, err
}

return &rsp, nil
}

func (c *consensusClient) LightClientOptimisticUpdate(ctx context.Context) (*lightclient.OptimisticUpdate, error) {
func (c *consensusClient) LightClientOptimisticUpdate(ctx context.Context) (*LightClientOptimisticUpdateResponse, error) {
data, err := c.get(ctx, "/eth/v1/beacon/light_client/optimistic_update")
if err != nil {
return nil, err
}

rsp := lightclient.OptimisticUpdate{}
if err := json.Unmarshal(data, &rsp); err != nil {
rsp := LightClientOptimisticUpdateResponse{
Response: Response[*lightclient.OptimisticUpdate]{
Data: &lightclient.OptimisticUpdate{},
Metadata: map[string]any{
"version": data.Version,
},
},
}
if err := json.Unmarshal(data.Data, &rsp.Data); err != nil {
return nil, err
}

Expand Down
24 changes: 24 additions & 0 deletions pkg/beacon/api/responses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package api

import "github.com/ethpandaops/beacon/pkg/beacon/api/types/lightclient"

type Response[T any] struct {
Data T `json:"data"`
Metadata map[string]any `json:"metadata"`
}

type LightClientUpdatesResponse struct {
Response[*lightclient.Updates]
}

type LightClientBootstrapResponse struct {
Response[*lightclient.Bootstrap]
}

type LightClientFinalityUpdateResponse struct {
Response[*lightclient.FinalityUpdate]
}

type LightClientOptimisticUpdateResponse struct {
Response[*lightclient.OptimisticUpdate]
}
8 changes: 8 additions & 0 deletions pkg/beacon/api/types/agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const (
AgentPrysm Agent = "prysm"
// AgentLodestar is a Lodestar agent.
AgentLodestar Agent = "lodestar"
// AgentGrandine is a Grandine agent.
AgentGrandine Agent = "grandine"
)

// AllAgents is a list of all agents.
Expand All @@ -30,6 +32,7 @@ var AllAgents = []Agent{
AgentTeku,
AgentPrysm,
AgentLodestar,
AgentGrandine,
}

// AgentCount represents the number of peers with each agent.
Expand All @@ -40,6 +43,7 @@ type AgentCount struct {
Teku int `json:"teku"`
Prysm int `json:"prysm"`
Lodestar int `json:"lodestar"`
Grandine int `json:"grandine"`
}

// AgentFromString returns the agent from the given string.
Expand All @@ -66,5 +70,9 @@ func AgentFromString(agent string) Agent {
return AgentLodestar
}

if strings.Contains(asLower, "grandine") {
return AgentGrandine
}

return AgentUnknown
}
2 changes: 1 addition & 1 deletion pkg/beacon/api/types/lightclient/finality_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (f FinalityUpdate) MarshalJSON() ([]byte, error) {
func (f *FinalityUpdate) ToJSON() finalityUpdateJSON {
finalityBranch := make([]string, len(f.FinalityBranch))
for i, root := range f.FinalityBranch {
finalityBranch[i] = fmt.Sprintf("%x", root)
finalityBranch[i] = fmt.Sprintf("%#x", root)
}

return finalityUpdateJSON{
Expand Down
3 changes: 3 additions & 0 deletions pkg/beacon/api/types/lightclient/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import (
"github.com/pkg/errors"
)

// Updates represents a light client updates.
type Updates []*Update

// Update represents a light client update.
type Update struct {
AttestedHeader LightClientHeader `json:"attested_header"`
Expand Down
8 changes: 8 additions & 0 deletions pkg/beacon/beacon.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ type Node interface {
FetchBeaconBlockHeader(ctx context.Context, opts *eapi.BeaconBlockHeaderOpts) (*v1.BeaconBlockHeader, error)
// FetchNodeIdentity fetches the node identity.
FetchNodeIdentity(ctx context.Context) (*types.Identity, error)
// FetchLightClientBootstrap fetches the light client bootstrap.
FetchLightClientBootstrap(ctx context.Context, root phase0.Root) (*api.LightClientBootstrapResponse, error)
// FetchLightClientFinalityUpdate fetches the light client finality update.
FetchLightClientFinalityUpdate(ctx context.Context) (*api.LightClientFinalityUpdateResponse, error)
// FetchLightClientOptimisticUpdate fetches the light client optimistic update.
FetchLightClientOptimisticUpdate(ctx context.Context) (*api.LightClientOptimisticUpdateResponse, error)
// FetchLightClientUpdates fetches the light client updates.
FetchLightClientUpdates(ctx context.Context, startPeriod, count int) (*api.LightClientUpdatesResponse, error)

// Subscriptions
// - Proxied Beacon events
Expand Down
72 changes: 72 additions & 0 deletions pkg/beacon/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package beacon
import (
"context"
"errors"
"fmt"

eth2client "github.com/attestantio/go-eth2-client"
"github.com/attestantio/go-eth2-client/api"
v1 "github.com/attestantio/go-eth2-client/api/v1"
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/deneb"
"github.com/attestantio/go-eth2-client/spec/phase0"
bapi "github.com/ethpandaops/beacon/pkg/beacon/api"
"github.com/ethpandaops/beacon/pkg/beacon/api/types"
"github.com/ethpandaops/beacon/pkg/beacon/state"
)
Expand Down Expand Up @@ -526,3 +528,73 @@ func (n *node) FetchBeaconBlockHeader(ctx context.Context, opts *api.BeaconBlock

return rsp.Data, nil
}

func (n *node) FetchLightClientBootstrap(ctx context.Context, root phase0.Root) (*bapi.LightClientBootstrapResponse, error) {
rootAsHex := fmt.Sprintf("0x%x", root)

logCtx := n.log.WithField("method", "FetchLightClientBootstrap").WithField("root", rootAsHex)

logCtx.Debug("Fetching light client bootstrap")

rsp, err := n.api.LightClientBootstrap(ctx, rootAsHex)
if err != nil {
logCtx.WithError(err).Error("failed to fetch light client bootstrap")

return nil, err
}

logCtx.Debug("Successfully fetched light client bootstrap")

return rsp, nil
}

func (n *node) FetchLightClientFinalityUpdate(ctx context.Context) (*bapi.LightClientFinalityUpdateResponse, error) {
logCtx := n.log.WithField("method", "FetchLightClientFinalityUpdate")

logCtx.Debug("Fetching light client finality update")

rsp, err := n.api.LightClientFinalityUpdate(ctx)
if err != nil {
logCtx.WithError(err).Error("failed to fetch light client finality update")

return nil, err
}

logCtx.Debug("Successfully fetched light client finality update")

return rsp, nil
}

func (n *node) FetchLightClientOptimisticUpdate(ctx context.Context) (*bapi.LightClientOptimisticUpdateResponse, error) {
logCtx := n.log.WithField("method", "FetchLightClientOptimisticUpdate")

logCtx.Debug("Fetching light client optimistic update")

rsp, err := n.api.LightClientOptimisticUpdate(ctx)
if err != nil {
logCtx.WithError(err).Error("failed to fetch light client optimistic update")

return nil, err
}

logCtx.Debug("Successfully fetched light client optimistic update")

return rsp, nil
}

func (n *node) FetchLightClientUpdates(ctx context.Context, startPeriod, count int) (*bapi.LightClientUpdatesResponse, error) {
logCtx := n.log.WithField("method", "FetchLightClientUpdates").WithField("start_period", startPeriod).WithField("count", count)

logCtx.Debug("Fetching light client update")

rsp, err := n.api.LightClientUpdates(ctx, startPeriod, count)
if err != nil {
logCtx.WithError(err).Error("failed to fetch light client update")

return nil, err
}

logCtx.Debug("Successfully fetched light client update")

return rsp, nil
}

0 comments on commit d1834c1

Please sign in to comment.