Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
416 changes: 416 additions & 0 deletions LITE_MODE_RPC.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func setupOrchestrator(gt *testing.T, t devtest.T, blocksToSync uint64) (*sysgo.
l.Info("L2_CL_SYNCMODE", "value", L2CLSyncMode)

// Setup orchestrator
logger := testlog.Logger(gt, log.LevelInfo)
logger := testlog.Logger(gt, log.LevelDebug)
onFail := func(now bool) {
if now {
gt.FailNow()
Expand Down
12 changes: 12 additions & 0 deletions op-devstack/sysgo/l2_cl_opnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/hex"
"flag"
"fmt"
"os"
"sync"
"time"

Expand Down Expand Up @@ -296,6 +297,17 @@ func WithOpNode(l2CLID stack.L2CLNodeID, l1CLID stack.L1CLNodeID, l1ELID stack.L
IgnoreMissingPectraBlobSchedule: false,
ExperimentalOPStackAPI: true,
}
// Minimal plumbing for experimental lite mode RPC flag in tests:
// allow configuring via OP_NODE_LITE_MODE_RPC env var to simulate CLI flag.
if v := os.Getenv("OP_NODE_LITE_MODE_RPC"); v != "" {
nodeCfg.LiteModeRPC = v
// Also wire through driver for guards/poller
nodeCfg.Driver.LiteModeRPC = v
if nodeCfg.Driver.LiteModePollInterval == 0 {
nodeCfg.Driver.LiteModePollInterval = time.Second * 2
}
logger.Info("LITE_MODE_RPC test env detected; enabling lite mode", "endpoint", v)
}
if cfg.SafeDBPath != "" {
nodeCfg.SafeDBPath = cfg.SafeDBPath
}
Expand Down
5 changes: 5 additions & 0 deletions op-node/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ type Config struct {

// Experimental. Enables new opstack RPC namespace. Used by op-test-sequencer.
ExperimentalOPStackAPI bool

// Experimental: If non-empty, enables lite mode via external RPC
LiteModeRPC string
// Poll interval for external lite mode updates
LiteModePollInterval time.Duration
}

// ConductorRPCFunc retrieves the endpoint. The RPC may not immediately be available.
Expand Down
16 changes: 16 additions & 0 deletions op-node/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,20 @@ var (
EnvVars: prefixEnvVars("SAFEDB_PATH"),
Category: OperationsCategory,
}
// Experimental: Lite mode RPC feature flag (minimal plumbing)
LiteModeRPC = &cli.StringFlag{
Name: "lite-mode-rpc",
Usage: "External L2 RPC endpoint to source lite-mode heads (experimental)",
EnvVars: prefixEnvVars("LITE_MODE_RPC"),
Category: RollupCategory,
}
LiteModePollInterval = &cli.DurationFlag{
Name: "lite-mode-poll-interval",
Usage: "Polling interval for external lite-mode RPC updates",
EnvVars: prefixEnvVars("LITE_MODE_POLL_INTERVAL"),
Value: time.Second * 2,
Category: RollupCategory,
}
/* Deprecated Flags */
L2EngineSyncEnabled = &cli.BoolFlag{
Name: "l2.engine-sync",
Expand Down Expand Up @@ -456,6 +470,8 @@ var optionalFlags = []cli.Flag{
ConductorRpcFlag,
ConductorRpcTimeoutFlag,
SafeDBPath,
LiteModeRPC,
LiteModePollInterval,
L2EngineKind,
L2EngineRpcTimeout,
InteropRPCAddr,
Expand Down
37 changes: 37 additions & 0 deletions op-node/litemode/DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## Lite Mode Poller Main Loop (Design)

This component advances the local safe/finalized labels from an external L2 RPC.

Inputs:
- Endpoint: `--lite-mode-rpc` (also `OP_NODE_LITE_MODE_RPC`)
- Poll interval: `--lite-mode-poll-interval`

Responsibilities:
- Read external safe/finalized heads periodically
- Apply finalized first, then safe
- Advance safe head strictly along parent links; on mismatch walk backwards to find a connecting ancestor

Main loop:
1. Every `Interval`:
- Fetch `finalizedHeadNum` using `eth_getBlockByNumber("finalized", false)`
- Fetch `safeHeadNum` using `eth_getBlockByNumber("safe", false)`
- If present, apply finalized by number
- If present, advance safe to `safeHeadNum`

Advance safe to N:
- Let `local` be the current local safe head
- While `local.Number < N`:
- Fetch `b = eth_getBlockByNumber(local.Number+1)`
- If `b.ParentHash == local.Hash`: apply `b` as safe
- Else (mismatch): walk backwards from `b.Number-1` until a block `pb` with `pb.ParentHash == local.Hash` is found
- If found: set `b = pb` and apply as safe (repeat)
- If none found before reaching `local.Number`: stop (wait for next tick)

Applying labels:
- Finalized: `SetFinalizedHead(ext)` then `TryUpdateEngine`
- Safe: set both local-safe and cross-safe to `ext`, then `TryUpdateEngine`

Notes:
- The loop intentionally stops on data gaps or mismatches and waits for the next tick.
- Unsafe behavior is unchanged; only safe/finalized labels are adjusted.

Loading