Skip to content

Commit 31ecd4a

Browse files
authored
op-node: Light CL: Frontend Flags (#18270)
* op-node: Light CL: Frontend Flags * better go * op-node: Move unsafeOnly related logs to init * Removed default values for optional flags * Use IsSequencer instead of isVerifier on syncCfg check * Early fail by checking invalid flag combinations * Better logs for unsafe and safe status
1 parent f05a6c7 commit 31ecd4a

File tree

5 files changed

+133
-4
lines changed

5 files changed

+133
-4
lines changed

op-node/flags/flags.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,20 @@ var (
217217
Value: time.Second * 10,
218218
Category: RollupCategory,
219219
}
220+
L2UnsafeOnly = &cli.BoolFlag{
221+
Name: "l2.unsafe-only",
222+
Usage: "Disable derivation",
223+
EnvVars: prefixEnvVars("L2_UNSAFE_ONLY"),
224+
Category: RollupCategory,
225+
Required: false,
226+
}
227+
L2FollowSource = &cli.StringFlag{
228+
Name: "l2.follow.source",
229+
Usage: "Address of L2 EL RPC HTTP endpoint to fetch safe/finalized blocks",
230+
EnvVars: prefixEnvVars("L2_FOLLOW_SOURCE"),
231+
Category: RollupCategory,
232+
Required: false,
233+
}
220234
VerifierL1Confs = &cli.Uint64Flag{
221235
Name: "verifier.l1-confs",
222236
Usage: "Number of L1 blocks to keep distance from the L1 head before deriving L2 data from. Reorgs are supported, but may be slow to perform.",
@@ -486,6 +500,8 @@ var optionalFlags = []cli.Flag{
486500
L1ChainConfig,
487501
L2EngineKind,
488502
L2EngineRpcTimeout,
503+
L2UnsafeOnly,
504+
L2FollowSource,
489505
InteropRPCAddr,
490506
InteropRPCPort,
491507
InteropJWTSecret,

op-node/node/node.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,15 @@ type InitializationOverrides struct {
201201
// some later initialization steps depend on the node being partially initialized with other components,
202202
// so order is important to ensure that all resources are available when needed.
203203
func (n *OpNode) init(ctx context.Context, cfg *config.Config, overrides InitializationOverrides) error {
204-
205204
n.log.Info("Initializing rollup node", "version", n.appVersion)
205+
safe := "enabled"
206+
if cfg.Sync.UnsafeOnly {
207+
safe = cfg.Sync.L2FollowSourceEndpoint
208+
if safe == "" {
209+
safe = "disabled"
210+
}
211+
}
212+
n.log.Info("Safety levels", "unsafe", "enabled", "safe", safe)
206213

207214
var err error
208215

op-node/rollup/sync/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,11 @@ type Config struct {
7474
SkipSyncStartCheck bool `json:"skip_sync_start_check"`
7575

7676
SupportsPostFinalizationELSync bool `json:"supports_post_finalization_elsync"`
77+
78+
UnsafeOnly bool `json:"unsafe_only"`
79+
L2FollowSourceEndpoint string `json:"l2_follow_source_endpoint"`
80+
}
81+
82+
func (c *Config) L2FollowSourceEnabled() bool {
83+
return c.L2FollowSourceEndpoint != ""
7784
}

op-node/service.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,25 +340,49 @@ func NewSyncConfig(ctx cliiface.Context, log log.Logger) (*sync.Config, error) {
340340
} else if ctx.IsSet(flags.L2EngineSyncEnabled.Name) {
341341
log.Error("l2.engine-sync is deprecated and will be removed in a future release. Use --syncmode=execution-layer instead.")
342342
}
343+
unsafeOnly := ctx.Bool(flags.L2UnsafeOnly.Name)
344+
l2FollowSourceEndpoint := ctx.String(flags.L2FollowSource.Name)
345+
if !unsafeOnly && l2FollowSourceEndpoint != "" {
346+
return nil, errors.New("cannot follow external safe/finalized with derivation enabled (--l2.unsafe-only=false): " +
347+
"Either remove --l2.follow.source or set --l2.unsafe-only=true to disable derivation")
348+
}
349+
rrSyncEnabled := ctx.Bool(flags.SyncModeReqRespFlag.Name)
343350
// p2p.sync.req-resp=false && syncmode.req-resp=true is not allowed
344-
if !ctx.Bool(flags.SyncReqRespName) && ctx.Bool(flags.SyncModeReqRespFlag.Name) {
351+
if !ctx.Bool(flags.SyncReqRespName) && rrSyncEnabled {
345352
return nil, errors.New("cannot set --p2p.sync.req-resp=false and --syncmode.req-resp=true at the same time")
346353
}
347354
mode, err := sync.StringToMode(ctx.String(flags.SyncModeFlag.Name))
348355
if err != nil {
349356
return nil, err
350357
}
351-
358+
isSequencer := ctx.Bool(flags.SequencerEnabledFlag.Name)
359+
if unsafeOnly && !isSequencer {
360+
// The verifier node initially gains payloads from the sequencer via CLP2P.
361+
// To sync to the chain tip, the verifier must close the gap between its current
362+
// unsafe view and the sequencer's latest unsafe payloads.
363+
// With derivation disabled, the node can only rely on RR Sync or EL Sync to close this gap.
364+
if rrSyncEnabled {
365+
// Allowing RR Sync technically works, but it is impractical for a verifier to
366+
// rely solely on RR Syncing - bootstrapping would take too long.
367+
// Since RR Sync is also being deprecated, fail early for clarity.
368+
return nil, errors.New("derivation disabled (--l2.unsafe-only=true) and RR sync enabled (--syncmode.req-resp=true): " +
369+
"reaching the unsafe tip would rely solely on RR sync, " +
370+
"which is infeasible for bootstrap. Disable RR sync or enable derivation")
371+
}
372+
// If RR Sync is not used, EL Sync will fill in the unsafe gap.
373+
// This path is much faster and more practical for closing the gap.
374+
}
352375
engineKind := engine.Kind(ctx.String(flags.L2EngineKind.Name))
353376
cfg := &sync.Config{
354377
SyncMode: mode,
355378
SyncModeReqResp: ctx.Bool(flags.SyncModeReqRespFlag.Name),
356379
SkipSyncStartCheck: ctx.Bool(flags.SkipSyncStartCheck.Name),
357380
SupportsPostFinalizationELSync: engineKind.SupportsPostFinalizationELSync(),
381+
UnsafeOnly: unsafeOnly,
382+
L2FollowSourceEndpoint: l2FollowSourceEndpoint,
358383
}
359384
if ctx.Bool(flags.L2EngineSyncEnabled.Name) {
360385
cfg.SyncMode = sync.ELSync
361386
}
362-
363387
return cfg, nil
364388
}

op-node/service_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package opnode
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/ethereum-optimism/optimism/op-node/flags"
8+
"github.com/ethereum/go-ethereum/log"
9+
"github.com/stretchr/testify/require"
10+
"github.com/urfave/cli/v2"
11+
)
12+
13+
func syncConfigCliApp() *cli.App {
14+
syncConfigFlags := append([]cli.Flag{
15+
flags.L2UnsafeOnly,
16+
flags.SequencerEnabledFlag,
17+
flags.L2EngineSyncEnabled,
18+
flags.SyncModeFlag,
19+
flags.SyncModeReqRespFlag,
20+
flags.L2FollowSource,
21+
flags.L2EngineKind,
22+
flags.SkipSyncStartCheck,
23+
}, flags.P2PFlags("")..., // For p2p.sync.req-resp
24+
)
25+
return &cli.App{
26+
Flags: syncConfigFlags,
27+
Action: func(c *cli.Context) error {
28+
_, err := NewSyncConfig(c, log.New())
29+
return err
30+
},
31+
}
32+
}
33+
34+
func run(args []string) error {
35+
return syncConfigCliApp().Run(append([]string{"test"}, args...))
36+
}
37+
38+
func TestNewSyncConfigDefault(t *testing.T) {
39+
require.NoError(t, run(nil))
40+
}
41+
42+
func TestNewSyncConfig_DerivationDisabled_NoRRSync(t *testing.T) {
43+
err := run([]string{
44+
fmt.Sprintf("--%s=true", flags.L2UnsafeOnly.Name),
45+
// No follow source with no derivation allowed
46+
fmt.Sprintf("--%s=false", flags.SyncModeReqRespFlag.Name),
47+
})
48+
require.NoError(t, err)
49+
}
50+
51+
func TestNewSyncConfig_FollowSourceWithDerivationDisabled(t *testing.T) {
52+
err := run([]string{
53+
fmt.Sprintf("--%s=true", flags.L2UnsafeOnly.Name),
54+
fmt.Sprintf("--%s=http://example", flags.L2FollowSource.Name),
55+
fmt.Sprintf("--%s=false", flags.SyncModeReqRespFlag.Name),
56+
})
57+
require.NoError(t, err)
58+
}
59+
60+
func TestNewSyncConfig_FollowSourceWithDerivationEnabled(t *testing.T) {
61+
err := run([]string{
62+
// unsafe-only defaults in false
63+
fmt.Sprintf("--%s=http://example", flags.L2FollowSource.Name),
64+
})
65+
require.ErrorContains(t, err, "cannot follow external safe/finalized with derivation enabled")
66+
}
67+
68+
func TestNewSyncConfig_VerifierUnsafeOnlyWithRRSyncEnabled(t *testing.T) {
69+
err := run([]string{
70+
// verifier mode is default
71+
fmt.Sprintf("--%s=true", flags.L2UnsafeOnly.Name),
72+
fmt.Sprintf("--%s=true", flags.SyncModeReqRespFlag.Name),
73+
})
74+
require.ErrorContains(t, err, "reaching the unsafe tip would rely solely on RR sync")
75+
}

0 commit comments

Comments
 (0)