mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -05:00
Allow custom headers in validator client HTTP requests (#15884)
* Allow custom headers in validator client HTTP requests * changelog <3 * improve flag description * Bastin's review * James' review * add godoc for NodeConnectionOption
This commit is contained in:
@@ -6,6 +6,7 @@ go_library(
|
|||||||
"client.go",
|
"client.go",
|
||||||
"errors.go",
|
"errors.go",
|
||||||
"options.go",
|
"options.go",
|
||||||
|
"transport.go",
|
||||||
],
|
],
|
||||||
importpath = "github.com/OffchainLabs/prysm/v6/api/client",
|
importpath = "github.com/OffchainLabs/prysm/v6/api/client",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
@@ -14,7 +15,13 @@ go_library(
|
|||||||
|
|
||||||
go_test(
|
go_test(
|
||||||
name = "go_default_test",
|
name = "go_default_test",
|
||||||
srcs = ["client_test.go"],
|
srcs = [
|
||||||
|
"client_test.go",
|
||||||
|
"transport_test.go",
|
||||||
|
],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = ["//testing/require:go_default_library"],
|
deps = [
|
||||||
|
"//testing/assert:go_default_library",
|
||||||
|
"//testing/require:go_default_library",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
25
api/client/transport.go
Normal file
25
api/client/transport.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// CustomHeadersTransport adds custom headers to each request
|
||||||
|
type CustomHeadersTransport struct {
|
||||||
|
base http.RoundTripper
|
||||||
|
headers map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCustomHeadersTransport(base http.RoundTripper, headers map[string][]string) *CustomHeadersTransport {
|
||||||
|
return &CustomHeadersTransport{
|
||||||
|
base: base,
|
||||||
|
headers: headers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CustomHeadersTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
for header, values := range t.headers {
|
||||||
|
for _, value := range values {
|
||||||
|
req.Header.Add(header, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t.base.RoundTrip(req)
|
||||||
|
}
|
||||||
25
api/client/transport_test.go
Normal file
25
api/client/transport_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||||
|
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type noopTransport struct{}
|
||||||
|
|
||||||
|
func (*noopTransport) RoundTrip(*http.Request) (*http.Response, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoundTrip(t *testing.T) {
|
||||||
|
tr := &CustomHeadersTransport{base: &noopTransport{}, headers: map[string][]string{"key1": []string{"value1", "value2"}, "key2": []string{"value3"}}}
|
||||||
|
req := httptest.NewRequest("GET", "http://foo", nil)
|
||||||
|
_, err := tr.RoundTrip(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.DeepEqual(t, []string{"value1", "value2"}, req.Header.Values("key1"))
|
||||||
|
assert.DeepEqual(t, []string{"value3"}, req.Header.Values("key2"))
|
||||||
|
}
|
||||||
3
changelog/radek_rest-custom-headers.md
Normal file
3
changelog/radek_rest-custom-headers.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
### Added
|
||||||
|
|
||||||
|
- Allow custom headers in validator client HTTP requests.
|
||||||
@@ -45,6 +45,13 @@ var (
|
|||||||
Usage: "Beacon node REST API provider endpoint.",
|
Usage: "Beacon node REST API provider endpoint.",
|
||||||
Value: "http://127.0.0.1:3500",
|
Value: "http://127.0.0.1:3500",
|
||||||
}
|
}
|
||||||
|
// BeaconRESTApiHeaders defines a list of headers to send with all HTTP requests to the beacon node.
|
||||||
|
BeaconRESTApiHeaders = &cli.StringFlag{
|
||||||
|
Name: "beacon-rest-api-headers",
|
||||||
|
Usage: `Comma-separated list of key value pairs to pass as headers for all HTTP calls to the beacon node.
|
||||||
|
To provide multiple values for the same key, specify the same key for each value.
|
||||||
|
Example: --grpc-headers=key1=value1,key1=value2,key2=value3`,
|
||||||
|
}
|
||||||
// CertFlag defines a flag for the node's TLS certificate.
|
// CertFlag defines a flag for the node's TLS certificate.
|
||||||
CertFlag = &cli.StringFlag{
|
CertFlag = &cli.StringFlag{
|
||||||
Name: "tls-cert",
|
Name: "tls-cert",
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ func startNode(ctx *cli.Context) error {
|
|||||||
var appFlags = []cli.Flag{
|
var appFlags = []cli.Flag{
|
||||||
flags.BeaconRPCProviderFlag,
|
flags.BeaconRPCProviderFlag,
|
||||||
flags.BeaconRESTApiProviderFlag,
|
flags.BeaconRESTApiProviderFlag,
|
||||||
|
flags.BeaconRESTApiHeaders,
|
||||||
flags.CertFlag,
|
flags.CertFlag,
|
||||||
flags.GraffitiFlag,
|
flags.GraffitiFlag,
|
||||||
flags.DisablePenaltyRewardLogFlag,
|
flags.DisablePenaltyRewardLogFlag,
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ var appHelpFlagGroups = []flagGroup{
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
flags.CertFlag,
|
flags.CertFlag,
|
||||||
flags.BeaconRPCProviderFlag,
|
flags.BeaconRPCProviderFlag,
|
||||||
|
flags.BeaconRESTApiHeaders,
|
||||||
flags.EnableRPCFlag,
|
flags.EnableRPCFlag,
|
||||||
flags.RPCHost,
|
flags.RPCHost,
|
||||||
flags.RPCPort,
|
flags.RPCPort,
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ func (acm *CLIManager) prepareBeaconClients(ctx context.Context) (*iface.Validat
|
|||||||
conn := validatorHelpers.NewNodeConnection(
|
conn := validatorHelpers.NewNodeConnection(
|
||||||
grpcConn,
|
grpcConn,
|
||||||
acm.beaconApiEndpoint,
|
acm.beaconApiEndpoint,
|
||||||
acm.beaconApiTimeout,
|
validatorHelpers.WithBeaconApiTimeout(acm.beaconApiTimeout),
|
||||||
)
|
)
|
||||||
|
|
||||||
restHandler := beaconApi.NewBeaconApiRestHandler(
|
restHandler := beaconApi.NewBeaconApiRestHandler(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
api "github.com/OffchainLabs/prysm/v6/api/client"
|
||||||
eventClient "github.com/OffchainLabs/prysm/v6/api/client/event"
|
eventClient "github.com/OffchainLabs/prysm/v6/api/client/event"
|
||||||
grpcutil "github.com/OffchainLabs/prysm/v6/api/grpc"
|
grpcutil "github.com/OffchainLabs/prysm/v6/api/grpc"
|
||||||
"github.com/OffchainLabs/prysm/v6/async/event"
|
"github.com/OffchainLabs/prysm/v6/async/event"
|
||||||
@@ -79,6 +80,7 @@ type Config struct {
|
|||||||
BeaconNodeGRPCEndpoint string
|
BeaconNodeGRPCEndpoint string
|
||||||
BeaconNodeCert string
|
BeaconNodeCert string
|
||||||
BeaconApiEndpoint string
|
BeaconApiEndpoint string
|
||||||
|
BeaconApiHeaders map[string][]string
|
||||||
BeaconApiTimeout time.Duration
|
BeaconApiTimeout time.Duration
|
||||||
Graffiti string
|
Graffiti string
|
||||||
GraffitiStruct *graffiti.Graffiti
|
GraffitiStruct *graffiti.Graffiti
|
||||||
@@ -142,7 +144,8 @@ func NewValidatorService(ctx context.Context, cfg *Config) (*ValidatorService, e
|
|||||||
s.conn = validatorHelpers.NewNodeConnection(
|
s.conn = validatorHelpers.NewNodeConnection(
|
||||||
grpcConn,
|
grpcConn,
|
||||||
cfg.BeaconApiEndpoint,
|
cfg.BeaconApiEndpoint,
|
||||||
cfg.BeaconApiTimeout,
|
validatorHelpers.WithBeaconApiHeaders(cfg.BeaconApiHeaders),
|
||||||
|
validatorHelpers.WithBeaconApiTimeout(cfg.BeaconApiTimeout),
|
||||||
)
|
)
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
@@ -185,8 +188,9 @@ func (v *ValidatorService) Start() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
headersTransport := api.NewCustomHeadersTransport(http.DefaultTransport, v.conn.GetBeaconApiHeaders())
|
||||||
restHandler := beaconApi.NewBeaconApiRestHandler(
|
restHandler := beaconApi.NewBeaconApiRestHandler(
|
||||||
http.Client{Timeout: v.conn.GetBeaconApiTimeout(), Transport: otelhttp.NewTransport(http.DefaultTransport)},
|
http.Client{Timeout: v.conn.GetBeaconApiTimeout(), Transport: otelhttp.NewTransport(headersTransport)},
|
||||||
hosts[0],
|
hosts[0],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -10,16 +10,37 @@ import (
|
|||||||
type NodeConnection interface {
|
type NodeConnection interface {
|
||||||
GetGrpcClientConn() *grpc.ClientConn
|
GetGrpcClientConn() *grpc.ClientConn
|
||||||
GetBeaconApiUrl() string
|
GetBeaconApiUrl() string
|
||||||
|
GetBeaconApiHeaders() map[string][]string
|
||||||
|
setBeaconApiHeaders(map[string][]string)
|
||||||
GetBeaconApiTimeout() time.Duration
|
GetBeaconApiTimeout() time.Duration
|
||||||
|
setBeaconApiTimeout(time.Duration)
|
||||||
dummy()
|
dummy()
|
||||||
}
|
}
|
||||||
|
|
||||||
type nodeConnection struct {
|
type nodeConnection struct {
|
||||||
grpcClientConn *grpc.ClientConn
|
grpcClientConn *grpc.ClientConn
|
||||||
beaconApiUrl string
|
beaconApiUrl string
|
||||||
|
beaconApiHeaders map[string][]string
|
||||||
beaconApiTimeout time.Duration
|
beaconApiTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NodeConnectionOption is a functional option for configuring the node connection.
|
||||||
|
type NodeConnectionOption func(nc NodeConnection)
|
||||||
|
|
||||||
|
// WithBeaconApiHeaders sets the HTTP headers that should be sent to the server along with each request.
|
||||||
|
func WithBeaconApiHeaders(headers map[string][]string) NodeConnectionOption {
|
||||||
|
return func(nc NodeConnection) {
|
||||||
|
nc.setBeaconApiHeaders(headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithBeaconApiTimeout sets the HTTP request timeout.
|
||||||
|
func WithBeaconApiTimeout(timeout time.Duration) NodeConnectionOption {
|
||||||
|
return func(nc NodeConnection) {
|
||||||
|
nc.setBeaconApiTimeout(timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *nodeConnection) GetGrpcClientConn() *grpc.ClientConn {
|
func (c *nodeConnection) GetGrpcClientConn() *grpc.ClientConn {
|
||||||
return c.grpcClientConn
|
return c.grpcClientConn
|
||||||
}
|
}
|
||||||
@@ -28,16 +49,30 @@ func (c *nodeConnection) GetBeaconApiUrl() string {
|
|||||||
return c.beaconApiUrl
|
return c.beaconApiUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *nodeConnection) GetBeaconApiHeaders() map[string][]string {
|
||||||
|
return c.beaconApiHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *nodeConnection) setBeaconApiHeaders(headers map[string][]string) {
|
||||||
|
c.beaconApiHeaders = headers
|
||||||
|
}
|
||||||
|
|
||||||
func (c *nodeConnection) GetBeaconApiTimeout() time.Duration {
|
func (c *nodeConnection) GetBeaconApiTimeout() time.Duration {
|
||||||
return c.beaconApiTimeout
|
return c.beaconApiTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *nodeConnection) setBeaconApiTimeout(timeout time.Duration) {
|
||||||
|
c.beaconApiTimeout = timeout
|
||||||
|
}
|
||||||
|
|
||||||
func (*nodeConnection) dummy() {}
|
func (*nodeConnection) dummy() {}
|
||||||
|
|
||||||
func NewNodeConnection(grpcConn *grpc.ClientConn, beaconApiUrl string, beaconApiTimeout time.Duration) NodeConnection {
|
func NewNodeConnection(grpcConn *grpc.ClientConn, beaconApiUrl string, opts ...NodeConnectionOption) NodeConnection {
|
||||||
conn := &nodeConnection{}
|
conn := &nodeConnection{}
|
||||||
conn.grpcClientConn = grpcConn
|
conn.grpcClientConn = grpcConn
|
||||||
conn.beaconApiUrl = beaconApiUrl
|
conn.beaconApiUrl = beaconApiUrl
|
||||||
conn.beaconApiTimeout = beaconApiTimeout
|
for _, opt := range opts {
|
||||||
|
opt(conn)
|
||||||
|
}
|
||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -433,6 +433,7 @@ func (c *ValidatorClient) registerValidatorService(cliCtx *cli.Context) error {
|
|||||||
BeaconNodeGRPCEndpoint: cliCtx.String(flags.BeaconRPCProviderFlag.Name),
|
BeaconNodeGRPCEndpoint: cliCtx.String(flags.BeaconRPCProviderFlag.Name),
|
||||||
BeaconNodeCert: cliCtx.String(flags.CertFlag.Name),
|
BeaconNodeCert: cliCtx.String(flags.CertFlag.Name),
|
||||||
BeaconApiEndpoint: cliCtx.String(flags.BeaconRESTApiProviderFlag.Name),
|
BeaconApiEndpoint: cliCtx.String(flags.BeaconRESTApiProviderFlag.Name),
|
||||||
|
BeaconApiHeaders: parseBeaconApiHeaders(cliCtx.String(flags.BeaconRESTApiHeaders.Name)),
|
||||||
BeaconApiTimeout: time.Second * 30,
|
BeaconApiTimeout: time.Second * 30,
|
||||||
Graffiti: g.ParseHexGraffiti(cliCtx.String(flags.GraffitiFlag.Name)),
|
Graffiti: g.ParseHexGraffiti(cliCtx.String(flags.GraffitiFlag.Name)),
|
||||||
GraffitiStruct: graffitiStruct,
|
GraffitiStruct: graffitiStruct,
|
||||||
@@ -552,6 +553,7 @@ func (c *ValidatorClient) registerRPCService(cliCtx *cli.Context) error {
|
|||||||
GRPCHeaders: strings.Split(cliCtx.String(flags.GRPCHeadersFlag.Name), ","),
|
GRPCHeaders: strings.Split(cliCtx.String(flags.GRPCHeadersFlag.Name), ","),
|
||||||
BeaconNodeGRPCEndpoint: cliCtx.String(flags.BeaconRPCProviderFlag.Name),
|
BeaconNodeGRPCEndpoint: cliCtx.String(flags.BeaconRPCProviderFlag.Name),
|
||||||
BeaconApiEndpoint: cliCtx.String(flags.BeaconRESTApiProviderFlag.Name),
|
BeaconApiEndpoint: cliCtx.String(flags.BeaconRESTApiProviderFlag.Name),
|
||||||
|
BeaconAPIHeaders: parseBeaconApiHeaders(cliCtx.String(flags.BeaconRESTApiHeaders.Name)),
|
||||||
BeaconApiTimeout: time.Second * 30,
|
BeaconApiTimeout: time.Second * 30,
|
||||||
BeaconNodeCert: cliCtx.String(flags.CertFlag.Name),
|
BeaconNodeCert: cliCtx.String(flags.CertFlag.Name),
|
||||||
DB: c.db,
|
DB: c.db,
|
||||||
@@ -636,3 +638,23 @@ func clearDB(ctx context.Context, dataDir string, force bool, isDatabaseMinimal
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseBeaconApiHeaders(rawHeaders string) map[string][]string {
|
||||||
|
result := make(map[string][]string)
|
||||||
|
pairs := strings.Split(rawHeaders, ",")
|
||||||
|
for _, pair := range pairs {
|
||||||
|
key, value, found := strings.Cut(pair, "=")
|
||||||
|
if !found {
|
||||||
|
// Skip malformed pairs
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key = strings.TrimSpace(key)
|
||||||
|
value = strings.TrimSpace(value)
|
||||||
|
if key == "" || value == "" {
|
||||||
|
// Skip malformed pairs
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result[key] = append(result[key], value)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -308,3 +308,17 @@ func TestWeb3SignerConfig(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_parseBeaconApiHeaders(t *testing.T) {
|
||||||
|
t.Run("ok", func(t *testing.T) {
|
||||||
|
h := parseBeaconApiHeaders("key1=value1,key1=value2,key2=value3")
|
||||||
|
assert.Equal(t, 2, len(h))
|
||||||
|
assert.DeepEqual(t, []string{"value1", "value2"}, h["key1"])
|
||||||
|
assert.DeepEqual(t, []string{"value3"}, h["key2"])
|
||||||
|
})
|
||||||
|
t.Run("ignores malformed", func(t *testing.T) {
|
||||||
|
h := parseBeaconApiHeaders("key1=value1,key2value2,key3=,=key4")
|
||||||
|
assert.Equal(t, 1, len(h))
|
||||||
|
assert.DeepEqual(t, []string{"value1"}, h["key1"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ go_library(
|
|||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
"//api:go_default_library",
|
"//api:go_default_library",
|
||||||
|
"//api/client:go_default_library",
|
||||||
"//api/grpc:go_default_library",
|
"//api/grpc:go_default_library",
|
||||||
"//api/pagination:go_default_library",
|
"//api/pagination:go_default_library",
|
||||||
"//api/server:go_default_library",
|
"//api/server:go_default_library",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package rpc
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
api "github.com/OffchainLabs/prysm/v6/api/client"
|
||||||
grpcutil "github.com/OffchainLabs/prysm/v6/api/grpc"
|
grpcutil "github.com/OffchainLabs/prysm/v6/api/grpc"
|
||||||
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||||
"github.com/OffchainLabs/prysm/v6/validator/client"
|
"github.com/OffchainLabs/prysm/v6/validator/client"
|
||||||
@@ -52,11 +53,13 @@ func (s *Server) registerBeaconClient() error {
|
|||||||
conn := validatorHelpers.NewNodeConnection(
|
conn := validatorHelpers.NewNodeConnection(
|
||||||
grpcConn,
|
grpcConn,
|
||||||
s.beaconApiEndpoint,
|
s.beaconApiEndpoint,
|
||||||
s.beaconApiTimeout,
|
validatorHelpers.WithBeaconApiHeaders(s.beaconApiHeaders),
|
||||||
|
validatorHelpers.WithBeaconApiTimeout(s.beaconApiTimeout),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
headersTransport := api.NewCustomHeadersTransport(http.DefaultTransport, conn.GetBeaconApiHeaders())
|
||||||
restHandler := beaconApi.NewBeaconApiRestHandler(
|
restHandler := beaconApi.NewBeaconApiRestHandler(
|
||||||
http.Client{Timeout: s.beaconApiTimeout, Transport: otelhttp.NewTransport(http.DefaultTransport)},
|
http.Client{Timeout: s.beaconApiTimeout, Transport: otelhttp.NewTransport(headersTransport)},
|
||||||
s.beaconApiEndpoint,
|
s.beaconApiEndpoint,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type Config struct {
|
|||||||
GRPCHeaders []string
|
GRPCHeaders []string
|
||||||
BeaconNodeGRPCEndpoint string
|
BeaconNodeGRPCEndpoint string
|
||||||
BeaconApiEndpoint string
|
BeaconApiEndpoint string
|
||||||
|
BeaconAPIHeaders map[string][]string
|
||||||
BeaconApiTimeout time.Duration
|
BeaconApiTimeout time.Duration
|
||||||
BeaconNodeCert string
|
BeaconNodeCert string
|
||||||
DB db.Database
|
DB db.Database
|
||||||
@@ -64,6 +65,7 @@ type Server struct {
|
|||||||
authTokenPath string
|
authTokenPath string
|
||||||
beaconNodeCert string
|
beaconNodeCert string
|
||||||
beaconApiEndpoint string
|
beaconApiEndpoint string
|
||||||
|
beaconApiHeaders map[string][]string
|
||||||
beaconNodeEndpoint string
|
beaconNodeEndpoint string
|
||||||
healthClient ethpb.HealthClient
|
healthClient ethpb.HealthClient
|
||||||
nodeClient iface.NodeClient
|
nodeClient iface.NodeClient
|
||||||
@@ -103,6 +105,7 @@ func NewServer(ctx context.Context, cfg *Config) *Server {
|
|||||||
wallet: cfg.Wallet,
|
wallet: cfg.Wallet,
|
||||||
beaconApiTimeout: cfg.BeaconApiTimeout,
|
beaconApiTimeout: cfg.BeaconApiTimeout,
|
||||||
beaconApiEndpoint: cfg.BeaconApiEndpoint,
|
beaconApiEndpoint: cfg.BeaconApiEndpoint,
|
||||||
|
beaconApiHeaders: cfg.BeaconAPIHeaders,
|
||||||
beaconNodeEndpoint: cfg.BeaconNodeGRPCEndpoint,
|
beaconNodeEndpoint: cfg.BeaconNodeGRPCEndpoint,
|
||||||
router: cfg.Router,
|
router: cfg.Router,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user