Commit b40933ec authored by Sebastian Stammler's avatar Sebastian Stammler Committed by GitHub

op-node: Support multiple beacon archiver/fallbacks (#10655)

* op-node: Support multiple beacon archiver/fallbacks

* op-node: rename flag to l1.beacon-fallbacks

- retain old flag name and env var
- fix some tests

* Update op-node/node/client_test.go
Co-authored-by: default avatarAdrian Sutton <adrian@oplabs.co>

---------
Co-authored-by: default avatarAdrian Sutton <adrian@oplabs.co>
parent 5c187bc7
......@@ -34,8 +34,12 @@ func init() {
cli.VersionFlag.(*cli.BoolFlag).Category = MiscCategory
}
func prefixEnvVars(name string) []string {
return []string{EnvVarPrefix + "_" + name}
func prefixEnvVars(names ...string) []string {
envs := make([]string, 0, len(names))
for _, name := range names {
envs = append(envs, EnvVarPrefix+"_"+name)
}
return envs
}
var (
......@@ -76,11 +80,11 @@ var (
EnvVars: prefixEnvVars("L1_BEACON_HEADER"),
Category: L1RPCCategory,
}
BeaconArchiverAddr = &cli.StringFlag{
Name: "l1.beacon-archiver",
Usage: "Address of L1 Beacon-node compatible HTTP endpoint to use. This is used to fetch blobs that the --l1.beacon does not have (i.e expired blobs).",
Required: false,
EnvVars: prefixEnvVars("L1_BEACON_ARCHIVER"),
BeaconFallbackAddrs = &cli.StringSliceFlag{
Name: "l1.beacon-fallbacks",
Aliases: []string{"l1.beacon-archiver"},
Usage: "Addresses of L1 Beacon-API compatible HTTP fallback endpoints. Used to fetch blob sidecars not availalbe at the l1.beacon (e.g. expired blobs).",
EnvVars: prefixEnvVars("L1_BEACON_FALLBACKS", "L1_BEACON_ARCHIVER"),
Category: L1RPCCategory,
}
BeaconCheckIgnore = &cli.BoolFlag{
......@@ -364,7 +368,7 @@ var requiredFlags = []cli.Flag{
var optionalFlags = []cli.Flag{
BeaconAddr,
BeaconHeader,
BeaconArchiverAddr,
BeaconFallbackAddrs,
BeaconCheckIgnore,
BeaconFetchAllSidecars,
SyncModeFlag,
......
......@@ -61,7 +61,6 @@ func TestDeprecatedFlagsAreHidden(t *testing.T) {
flagName := flag.Names()[0]
t.Run(flagName, func(t *testing.T) {
visibleFlag, ok := flag.(interface {
IsVisible() bool
})
......@@ -72,6 +71,11 @@ func TestDeprecatedFlagsAreHidden(t *testing.T) {
}
func TestHasEnvVar(t *testing.T) {
// known exceptions to the number of env vars
expEnvVars := map[string]int{
BeaconFallbackAddrs.Name: 2,
}
for _, flag := range Flags {
flag := flag
flagName := flag.Names()[0]
......@@ -83,39 +87,43 @@ func TestHasEnvVar(t *testing.T) {
envFlagGetter, ok := flag.(interface {
GetEnvVars() []string
})
envFlags := envFlagGetter.GetEnvVars()
require.True(t, ok, "must be able to cast the flag to an EnvVar interface")
require.Equal(t, 1, len(envFlags), "flags should have exactly one env var")
envFlags := envFlagGetter.GetEnvVars()
if numEnvVars, ok := expEnvVars[flagName]; ok {
require.Equalf(t, numEnvVars, len(envFlags), "flags should have %d env vars", numEnvVars)
} else {
require.Equal(t, 1, len(envFlags), "flags should have exactly one env var")
}
})
}
}
func TestEnvVarFormat(t *testing.T) {
skippedFlags := []string{
L1NodeAddr.Name,
L2EngineAddr.Name,
L2EngineJWTSecret.Name,
L1TrustRPC.Name,
L1RPCProviderKind.Name,
SnapshotLog.Name,
BackupL2UnsafeSyncRPC.Name,
BackupL2UnsafeSyncRPCTrustRPC.Name,
"p2p.scoring",
"p2p.ban.peers",
"p2p.ban.threshold",
"p2p.ban.duration",
"p2p.listen.tcp",
"p2p.listen.udp",
"p2p.useragent",
"p2p.gossip.mesh.lo",
"p2p.gossip.mesh.floodpublish",
"l2.engine-sync",
}
for _, flag := range Flags {
flag := flag
flagName := flag.Names()[0]
skippedFlags := []string{
L1NodeAddr.Name,
L2EngineAddr.Name,
L2EngineJWTSecret.Name,
L1TrustRPC.Name,
L1RPCProviderKind.Name,
SnapshotLog.Name,
BackupL2UnsafeSyncRPC.Name,
BackupL2UnsafeSyncRPCTrustRPC.Name,
"p2p.scoring",
"p2p.ban.peers",
"p2p.ban.threshold",
"p2p.ban.duration",
"p2p.listen.tcp",
"p2p.listen.udp",
"p2p.useragent",
"p2p.gossip.mesh.lo",
"p2p.gossip.mesh.floodpublish",
"l2.engine-sync",
}
t.Run(flagName, func(t *testing.T) {
if slices.Contains(skippedFlags, flagName) {
t.Skipf("Skipping flag %v which is known to not have a standard flag name <-> env var conversion", flagName)
......@@ -126,9 +134,8 @@ func TestEnvVarFormat(t *testing.T) {
envFlagGetter, ok := flag.(interface {
GetEnvVars() []string
})
envFlags := envFlagGetter.GetEnvVars()
require.True(t, ok, "must be able to cast the flag to an EnvVar interface")
require.Equal(t, 1, len(envFlags), "flags should have exactly one env var")
envFlags := envFlagGetter.GetEnvVars()
expectedEnvVar := opservice.FlagNameToEnvVarName(flagName, "OP_NODE")
require.Equal(t, expectedEnvVar, envFlags[0])
})
......
......@@ -178,11 +178,11 @@ func (cfg *PreparedL1Endpoint) Check() error {
}
type L1BeaconEndpointConfig struct {
BeaconAddr string // Address of L1 User Beacon-API endpoint to use (beacon namespace required)
BeaconHeader string // Optional HTTP header for all requests to L1 Beacon
BeaconArchiverAddr string // Address of L1 User Beacon-API Archive endpoint to use for expired blobs (beacon namespace required)
BeaconCheckIgnore bool // When false, halt startup if the beacon version endpoint fails
BeaconFetchAllSidecars bool // Whether to fetch all blob sidecars and filter locally
BeaconAddr string // Address of L1 User Beacon-API endpoint to use (beacon namespace required)
BeaconHeader string // Optional HTTP header for all requests to L1 Beacon
BeaconFallbackAddrs []string // Addresses of L1 Beacon-API fallback endpoints (only for blob sidecars retrieval)
BeaconCheckIgnore bool // When false, halt startup if the beacon version endpoint fails
BeaconFetchAllSidecars bool // Whether to fetch all blob sidecars and filter locally
}
var _ L1BeaconEndpointSetup = (*L1BeaconEndpointConfig)(nil)
......@@ -197,11 +197,12 @@ func (cfg *L1BeaconEndpointConfig) Setup(ctx context.Context, log log.Logger) (c
opts = append(opts, client.WithHeader(hdr))
}
a := client.NewBasicHTTPClient(cfg.BeaconAddr, log, opts...)
if cfg.BeaconArchiverAddr != "" {
b := client.NewBasicHTTPClient(cfg.BeaconArchiverAddr, log)
for _, addr := range cfg.BeaconFallbackAddrs {
b := client.NewBasicHTTPClient(addr, log)
fb = append(fb, sources.NewBeaconHTTPClient(b))
}
a := client.NewBasicHTTPClient(cfg.BeaconAddr, log, opts...)
return sources.NewBeaconHTTPClient(a), fb, nil
}
......
package node
import (
"context"
"net/http"
"testing"
......@@ -60,3 +61,32 @@ func TestParseHTTPHeader(t *testing.T) {
})
}
}
func TestL1BeaconEndpointConfig_Setup(t *testing.T) {
for _, test := range []struct {
desc string
baa []string
len int
}{
{
desc: "empty",
},
{
desc: "one",
baa: []string{"http://foo.bar"},
len: 1,
},
{
desc: "three",
baa: []string{"http://foo.bar", "http://op.ti", "http://ba.se"},
len: 3,
},
} {
t.Run(test.desc, func(t *testing.T) {
cfg := L1BeaconEndpointConfig{BeaconFallbackAddrs: test.baa}
_, fb, err := cfg.Setup(context.Background(), nil)
require.NoError(t, err)
require.Len(t, fb, test.len)
})
}
}
......@@ -132,7 +132,7 @@ func NewBeaconEndpointConfig(ctx *cli.Context) node.L1BeaconEndpointSetup {
return &node.L1BeaconEndpointConfig{
BeaconAddr: ctx.String(flags.BeaconAddr.Name),
BeaconHeader: ctx.String(flags.BeaconHeader.Name),
BeaconArchiverAddr: ctx.String(flags.BeaconArchiverAddr.Name),
BeaconFallbackAddrs: ctx.StringSlice(flags.BeaconFallbackAddrs.Name),
BeaconCheckIgnore: ctx.Bool(flags.BeaconCheckIgnore.Name),
BeaconFetchAllSidecars: ctx.Bool(flags.BeaconFetchAllSidecars.Name),
}
......
......@@ -179,8 +179,9 @@ func NewL1BeaconClient(cl BeaconClient, cfg L1BeaconClientConfig, fallbacks ...B
cs := append([]BlobSideCarsFetcher{cl}, fallbacks...)
return &L1BeaconClient{
cl: cl,
pool: NewClientPool[BlobSideCarsFetcher](cs...),
cfg: cfg}
pool: NewClientPool(cs...),
cfg: cfg,
}
}
type TimeToSlotFn func(timestamp uint64) (uint64, error)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment