cmd,eth,beacon: accept custom http headers for beacon api

This commit is contained in:
lightclient 2024-01-05 10:40:14 -07:00
parent 2333dcdb24
commit 70e2451c57
No known key found for this signature in database
GPG key ID: 75C916AFEE20183E
6 changed files with 50 additions and 17 deletions

View file

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"strings"
eth2client "github.com/attestantio/go-eth2-client" eth2client "github.com/attestantio/go-eth2-client"
eth2api "github.com/attestantio/go-eth2-client/api" eth2api "github.com/attestantio/go-eth2-client/api"
@ -41,26 +42,40 @@ var (
// Client is a wrapper around the attestantio/go-eth2-client beacon api client. // Client is a wrapper around the attestantio/go-eth2-client beacon api client.
type Client struct { type Client struct {
ctx context.Context ctx context.Context
url string url string
client eth2client.Service http *http.Client
extraHeaders map[string]string
client eth2client.Service
} }
// NewClient creates a Client for the given server URL. // NewClient creates a Client for the given server URL.
func NewClient(ctx context.Context, server string) (*Client, error) { func NewClient(ctx context.Context, server string, headers []string) (*Client, error) {
// Parse additional headers.
extraHeaders := make(map[string]string)
for _, h := range headers {
s := strings.Split(h, ":")
if len(s) != 2 {
return nil, fmt.Errorf("malformed extra header: %s", h)
}
extraHeaders[s[0]] = s[1]
}
client, err := eth2http.New( client, err := eth2http.New(
ctx, ctx,
eth2http.WithAddress(server), eth2http.WithAddress(server),
eth2http.WithLogLevel(zerolog.WarnLevel), eth2http.WithLogLevel(zerolog.WarnLevel),
eth2http.WithEnforceJSON(true), eth2http.WithEnforceJSON(true),
eth2http.WithExtraHeaders(extraHeaders),
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Client{ return &Client{
ctx: ctx, ctx: ctx,
url: server, url: server,
client: client, http: &http.Client{},
extraHeaders: extraHeaders,
client: client,
}, nil }, nil
} }
@ -68,7 +83,7 @@ func NewClient(ctx context.Context, server string) (*Client, error) {
// the beacon server. // the beacon server.
func (c *Client) Bootstrap(root common.Hash) (*types.Bootstrap, error) { func (c *Client) Bootstrap(root common.Hash) (*types.Bootstrap, error) {
var bs types.Bootstrap var bs types.Bootstrap
if err := fetch(fmt.Sprintf("%s/%s/%s", c.url, beaconLightClientBootstrap, root.String()), &bs); err != nil { if err := c.fetch(fmt.Sprintf("%s/%s/%s", c.url, beaconLightClientBootstrap, root.String()), &bs); err != nil {
return nil, err return nil, err
} }
return &bs, nil return &bs, nil
@ -78,7 +93,7 @@ func (c *Client) Bootstrap(root common.Hash) (*types.Bootstrap, error) {
// include the next sync committee and finalized header from the period. // include the next sync committee and finalized header from the period.
func (c *Client) GetRangeUpdate(start, count int) ([]*types.LightClientUpdate, error) { func (c *Client) GetRangeUpdate(start, count int) ([]*types.LightClientUpdate, error) {
var u []*types.LightClientUpdate var u []*types.LightClientUpdate
if err := fetch(fmt.Sprintf("%s/%s?start_period=%d&count=%d", c.url, beaconLightClientUpdate, start, count), &u); err != nil { if err := c.fetch(fmt.Sprintf("%s/%s?start_period=%d&count=%d", c.url, beaconLightClientUpdate, start, count), &u); err != nil {
return nil, err return nil, err
} }
return u, nil return u, nil
@ -89,7 +104,7 @@ func (c *Client) GetRangeUpdate(start, count int) ([]*types.LightClientUpdate, e
// beacon api server. // beacon api server.
func (c *Client) GetOptimisticUpdate() (*types.LightClientUpdate, error) { func (c *Client) GetOptimisticUpdate() (*types.LightClientUpdate, error) {
var u types.LightClientUpdate var u types.LightClientUpdate
if err := fetch(fmt.Sprintf("%s/%s", c.url, beaconLightClientOptimisticUpdate), &u); err != nil { if err := c.fetch(fmt.Sprintf("%s/%s", c.url, beaconLightClientOptimisticUpdate), &u); err != nil {
return nil, err return nil, err
} }
return &u, nil return &u, nil
@ -99,7 +114,7 @@ func (c *Client) GetOptimisticUpdate() (*types.LightClientUpdate, error) {
// beacon api server. // beacon api server.
func (c *Client) GetFinalityUpdate() (*types.LightClientUpdate, error) { func (c *Client) GetFinalityUpdate() (*types.LightClientUpdate, error) {
var u types.LightClientUpdate var u types.LightClientUpdate
if err := fetch(fmt.Sprintf("%s/%s", c.url, beaconLightClientFinalityUpdate), &u); err != nil { if err := c.fetch(fmt.Sprintf("%s/%s", c.url, beaconLightClientFinalityUpdate), &u); err != nil {
return nil, err return nil, err
} }
return &u, nil return &u, nil
@ -118,11 +133,21 @@ func (c *Client) GetBlock(root common.Hash) (*eth2spec.VersionedSignedBeaconBloc
return resp.Data, nil return resp.Data, nil
} }
func fetch(url string, val any) error { func (c *Client) fetch(url string, val any) error {
resp, err := http.Get(url) req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
for k, v := range c.extraHeaders {
req.Header.Set(k, v)
}
resp, err := c.http.Do(req)
if err != nil { if err != nil {
return fmt.Errorf("failed http request: %w", err) return fmt.Errorf("failed http request: %w", err)
} }
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed http request: status %d", resp.StatusCode)
}
b, err := io.ReadAll(resp.Body) b, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return fmt.Errorf("failed to read response: %w", err) return fmt.Errorf("failed to read response: %w", err)

View file

@ -45,8 +45,8 @@ type LightClient struct {
// Bootstrap retrieves a light client bootstrap and authenticates it against the // Bootstrap retrieves a light client bootstrap and authenticates it against the
// provided trusted root. // provided trusted root.
func Bootstrap(ctx context.Context, server string, root common.Hash) (*LightClient, error) { func Bootstrap(server string, headers []string, root common.Hash) (*LightClient, error) {
api, err := beaclient.NewClient(ctx, server) api, err := beaclient.NewClient(context.Background(), server, headers)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to connect to beacon server: %w", err) return nil, fmt.Errorf("failed to connect to beacon server: %w", err)
} }

View file

@ -147,6 +147,7 @@ var (
utils.LogDebugFlag, utils.LogDebugFlag,
utils.LogBacktraceAtFlag, utils.LogBacktraceAtFlag,
utils.BeaconAPIFlag, utils.BeaconAPIFlag,
utils.BeaconAPIHeadersFlag,
utils.BeaconTrustedBlockRootFlag, utils.BeaconTrustedBlockRootFlag,
}, utils.NetworkFlags, utils.DatabaseFlags) }, utils.NetworkFlags, utils.DatabaseFlags)

View file

@ -915,6 +915,11 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
Usage: "server which provides the beacon light client apis", Usage: "server which provides the beacon light client apis",
Category: flags.BeaconCategory, Category: flags.BeaconCategory,
} }
BeaconAPIHeadersFlag = &cli.StringSliceFlag{
Name: "beacon.api.headers",
Usage: "headers to provide with each request",
Category: flags.BeaconCategory,
}
BeaconTrustedBlockRootFlag = &cli.StringFlag{ BeaconTrustedBlockRootFlag = &cli.StringFlag{
Name: "beacon.wss", Name: "beacon.wss",
Usage: "root of trusted block within the weak-subjectivity window", Usage: "root of trusted block within the weak-subjectivity window",
@ -1599,6 +1604,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// Avoid conflicting network flags // Avoid conflicting network flags
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag, HoleskyFlag) CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag, HoleskyFlag)
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
CheckExclusive(ctx, DeveloperFlag, BeaconAPIFlag) // Can't use both simulated beacon and blsync
// Set configurations from CLI flags // Set configurations from CLI flags
setEtherbase(ctx, cfg) setEtherbase(ctx, cfg)
@ -1859,6 +1865,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
Fatalf("Must set both beacon api flag and beacon trusted block root flag to use beacon light client") Fatalf("Must set both beacon api flag and beacon trusted block root flag to use beacon light client")
} }
cfg.BeaconAPI = ctx.String(BeaconAPIFlag.Name) cfg.BeaconAPI = ctx.String(BeaconAPIFlag.Name)
cfg.BeaconAPIHeaders = ctx.StringSlice(BeaconAPIHeadersFlag.Name)
cfg.BeaconTrustedBlockRoot = ctx.String(BeaconTrustedBlockRootFlag.Name) cfg.BeaconTrustedBlockRoot = ctx.String(BeaconTrustedBlockRootFlag.Name)
} }

View file

@ -18,7 +18,6 @@
package eth package eth
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
@ -277,7 +276,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
} }
if config.BeaconAPI != "" && config.BeaconTrustedBlockRoot != "" { if config.BeaconAPI != "" && config.BeaconTrustedBlockRoot != "" {
beacon, err := light.Bootstrap(context.Background(), config.BeaconAPI, common.HexToHash(config.BeaconTrustedBlockRoot)) beacon, err := light.Bootstrap(config.BeaconAPI, config.BeaconAPIHeaders, common.HexToHash(config.BeaconTrustedBlockRoot))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -115,6 +115,7 @@ type Config struct {
// Beacon light client options // Beacon light client options
BeaconAPI string BeaconAPI string
BeaconAPIHeaders []string
BeaconTrustedBlockRoot string BeaconTrustedBlockRoot string
// Database options // Database options