Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d340a84
docs: add lite mode design document
karlfloersch Sep 30, 2025
dd13439
test: add sync tester test runner script
Oct 1, 2025
65895da
docs: add comprehensive testing section to lite mode design
Oct 1, 2025
a003344
feat(op-node): add lite mode configuration and CLI flags
Oct 1, 2025
93e4375
feat(op-node): disable derivation in lite mode
Oct 1, 2025
ef2604e
feat(op-node): add LiteModeSync component
Oct 1, 2025
b177d39
feat(op-node): integrate LiteModeSync into Driver
Oct 1, 2025
aa52865
fix(op-node): fix compilation errors in lite mode integration
Oct 1, 2025
6ab4d94
docs(lite-mode): update environment variable names in test documentation
Oct 1, 2025
e1ac4b2
feat(devstack): add lite mode support to test infrastructure
Oct 1, 2025
aa9c015
fix(devstack): add missing os import for lite mode support
Oct 1, 2025
d0b113a
fix(lite-mode): handle missing eth_syncing method gracefully
Oct 1, 2025
513f8c2
fix(lite-mode): remove eth_syncing check and fix close panic
Oct 1, 2025
1857f2c
feat(lite-mode): fix unsafe head initialization and complete implemen…
Oct 1, 2025
dfd2e98
refactor(lite-mode): use FindL2Heads for initialization instead of ma…
Oct 1, 2025
4a00e95
feat(lite-mode): add acceptance tests and fix sync issues
Oct 1, 2025
f9210f2
refactor(lite-mode): move derivation disable to SyncStep for clarity
Oct 1, 2025
250276a
refactor(lite-mode): swap order and move catch-up logic to updateFina…
Oct 1, 2025
4554e4d
refactor(lite-mode): simplify safe head sync with backward-walking al…
Oct 1, 2025
96138c3
refactor(lite-mode): address review feedback
Oct 1, 2025
19eb5ac
feat(lite-mode): optimize block verification with header-only fetches
Oct 1, 2025
37a66f1
fix(lite-mode): use event system instead of direct engine calls
Oct 1, 2025
9a8d214
refactor(test): separate lite mode into dedicated test function
Oct 1, 2025
8a3dd82
refactor(lite-mode): extract block hash lookup into helper function
Oct 2, 2025
466b351
refactor(lite-mode): remove redundant warning when block lookup fails
Oct 2, 2025
d4b3556
fix(lite-mode): return error when remote safe is behind local finalized
Oct 2, 2025
cfaf487
refactor(lite-mode): integrate sync into SyncStep instead of separate…
Oct 2, 2025
d53ffc8
fix(lite-mode): use direct engine method calls instead of events and …
Oct 2, 2025
2bf330e
fix(lite-mode): wait for L1 finalization before checking L2 finalized
Oct 2, 2025
064a4c2
perf(lite-mode): prevent CPU starvation of P2P unsafe block processing
Oct 2, 2025
25f08ce
fix(lite-mode): disable challenger in lite mode tests to prevent flakes
Oct 2, 2025
311da5f
fix(lite-mode): prevent duplicate block insertions causing reorgs
Oct 2, 2025
fb9bb40
chore(lite-mode): remove polling logic and unused documentation
Oct 3, 2025
bc73f3a
chore: remove .gitignore from lite_mode tests
Oct 3, 2025
913bbc6
refactor: rename lite mode to tip mode throughout codebase
Oct 3, 2025
ea139b2
fix: correct tip mode environment variable naming
Oct 3, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/bash
# Script to run the sync tester external EL test
# This test validates op-node syncing against external execution layer endpoints

set -e

# Configuration
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_FILE="${TEST_DIR}/test_run_$(date +%Y%m%d_%H%M%S).log"
TIP_MODE_RPC="${OP_NODE_ROLLUP_TIP_MODE_RPC:-}"

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo "=========================================="
echo "OP Stack Sync Tester - External EL"
echo "=========================================="
echo "Test Directory: ${TEST_DIR}"
echo "Log File: ${LOG_FILE}"
if [ -n "${TIP_MODE_RPC}" ]; then
echo -e "${YELLOW}Tip Mode: ENABLED${NC}"
echo "Tip Mode RPC: ${TIP_MODE_RPC}"
else
echo "Tip Mode: DISABLED (standard derivation)"
fi
echo "=========================================="
echo ""

cd "${TEST_DIR}"

# Run the test
echo "Starting test..."
CIRCLECI_PARAMETERS_SYNC_TEST_OP_NODE_DISPATCH=true \
TAILSCALE_NETWORKING=true \
NETWORK_PRESET=op-sepolia \
GOMAXPROCS=5 \
OP_NODE_ROLLUP_TIP_MODE_RPC="${TIP_MODE_RPC}" \
go test -run '^TestSyncTesterExtEL$' -v -count=1 2>&1 | tee "${LOG_FILE}"

# Check exit code
EXIT_CODE=${PIPESTATUS[0]}

echo ""
echo "=========================================="
if [ ${EXIT_CODE} -eq 0 ]; then
echo -e "${GREEN}TEST PASSED ✓${NC}"
else
echo -e "${RED}TEST FAILED ✗${NC}"
fi
echo "Exit Code: ${EXIT_CODE}"
echo "Log File: ${LOG_FILE}"
echo "=========================================="

exit ${EXIT_CODE}
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,10 @@ var (
L1ELEndpoint: "https://ci-mainnet-l1.optimism.io",
},
}
L2CLSyncMode = getSyncMode("L2_CL_SYNCMODE")
)

func getSyncMode(envVar string) sync.Mode {
if value := os.Getenv(envVar); value == sync.ELSyncString {
return sync.ELSync
}
return sync.CLSync
}

func TestSyncTesterExtEL(gt *testing.T) {
// runSyncTest contains the shared test logic for all sync modes
func runSyncTest(gt *testing.T, syncMode sync.Mode, tipModeRPC string) {
t := devtest.SerialT(gt)

if os.Getenv("CIRCLECI_PIPELINE_SCHEDULE_NAME") != "build_daily" && os.Getenv("CIRCLECI_PARAMETERS_SYNC_TEST_OP_NODE_DISPATCH") != "true" {
Expand All @@ -92,10 +85,10 @@ func TestSyncTesterExtEL(gt *testing.T) {
l := t.Logger()
require := t.Require()
blocksToSync := uint64(20)
sys, target := setupSystem(gt, t, blocksToSync)
sys, target := setupSystem(gt, t, blocksToSync, syncMode, tipModeRPC)

attempts := 500
if L2CLSyncMode == sync.ELSync {
if syncMode == sync.ELSync {
// After EL Sync is finished, the FCU state will advance to target immediately so less attempts
attempts = 5
// Signal L2CL for triggering EL Sync
Expand Down Expand Up @@ -125,10 +118,28 @@ func TestSyncTesterExtEL(gt *testing.T) {
l.Info("SyncTester ExtEL test completed successfully", "l2cl_chain_id", sys.L2CL.ID().ChainID(), "l2cl_sync_status", l2CLSyncStatus)
}

// TestSyncTesterExtEL tests op-node syncing in CL or EL sync mode
func TestSyncTesterExtEL(gt *testing.T) {
syncMode := sync.CLSync
if value := os.Getenv("L2_CL_SYNCMODE"); value == sync.ELSyncString {
syncMode = sync.ELSync
}
runSyncTest(gt, syncMode, "")
}

// TestSyncTesterExtELTipMode tests op-node syncing in tip mode (RPC-based sync)
func TestSyncTesterExtELTipMode(gt *testing.T) {
tipModeRPC := os.Getenv("OP_NODE_ROLLUP_TIP_MODE_RPC")
if tipModeRPC == "" {
gt.Skip("OP_NODE_ROLLUP_TIP_MODE_RPC not set, skipping tip mode test")
}
runSyncTest(gt, sync.CLSync, tipModeRPC)
}

// setupSystem initializes the system for the test and returns the system and the target block number of the session
func setupSystem(gt *testing.T, t devtest.T, blocksToSync uint64) (*presets.MinimalExternalEL, uint64) {
func setupSystem(gt *testing.T, t devtest.T, blocksToSync uint64, syncMode sync.Mode, tipModeRPC string) (*presets.MinimalExternalEL, uint64) {
// Initialize orchestrator
orch, target := setupOrchestrator(gt, t, blocksToSync)
orch, target := setupOrchestrator(gt, t, blocksToSync, syncMode, tipModeRPC)
system := shim.NewSystem(t)
orch.Hydrate(system)

Expand All @@ -154,7 +165,7 @@ func setupSystem(gt *testing.T, t devtest.T, blocksToSync uint64) (*presets.Mini
}

// setupOrchestrator initializes and configures the orchestrator for the test and returns the orchestrator and the target block number of the session
func setupOrchestrator(gt *testing.T, t devtest.T, blocksToSync uint64) (*sysgo.Orchestrator, uint64) {
func setupOrchestrator(gt *testing.T, t devtest.T, blocksToSync uint64, syncMode sync.Mode, tipModeRPC string) (*sysgo.Orchestrator, uint64) {
l := t.Logger()
ctx := t.Ctx()
require := t.Require()
Expand Down Expand Up @@ -185,7 +196,8 @@ func setupOrchestrator(gt *testing.T, t devtest.T, blocksToSync uint64) (*sysgo.
l.Info("L1_CL_BEACON_ENDPOINT", "value", config.L1CLBeaconEndpoint)
l.Info("L1_EL_ENDPOINT", "value", config.L1ELEndpoint)
l.Info("TAILSCALE_NETWORKING", "value", os.Getenv("TAILSCALE_NETWORKING"))
l.Info("L2_CL_SYNCMODE", "value", L2CLSyncMode)
l.Info("L2_CL_SYNCMODE", "value", syncMode)
l.Info("TIP_MODE_RPC", "value", tipModeRPC)

// Setup orchestrator
logger := testlog.Logger(gt, log.LevelInfo)
Expand All @@ -212,8 +224,16 @@ func setupOrchestrator(gt *testing.T, t devtest.T, blocksToSync uint64) (*sysgo.
target := initial + blocksToSync
l.Info("LATEST_BLOCK", "latest_block", latestBlock.NumberU64(), "session_initial_block", initial, "target_block", target)

// Set tip mode environment variable if provided
// The op-node will automatically pick this up during initialization
if tipModeRPC != "" {
os.Setenv("OP_NODE_ROLLUP_TIP_MODE", "true")
os.Setenv("OP_NODE_ROLLUP_TIP_MODE_RPC", tipModeRPC)
}

opt := presets.WithExternalELWithSuperchainRegistry(config)
if L2CLSyncMode == sync.ELSync {

if syncMode == sync.ELSync {
chainCfg := chaincfg.ChainByName(config.L2NetworkName)
if chainCfg == nil {
panic(fmt.Sprintf("network %s not found in superchain registry", config.L2NetworkName))
Expand Down
127 changes: 127 additions & 0 deletions op-acceptance-tests/tests/tip_mode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Tip Mode Acceptance Tests

This directory contains acceptance tests for the tip mode feature in op-node.

## What is Tip Mode?

Tip mode is a new sync mode where the op-node sources safe/finalized heads from a remote RPC endpoint instead of deriving them from L1. It's designed for resource-constrained nodes that want to sync quickly without performing L1 derivation.

### Key Characteristics

- **Disables L1 derivation pipeline** for safe head progression
- **Polls a remote RPC endpoint** for safe/finalized blocks
- **Imports blocks from remote** and promotes them locally
- **CL sync (P2P gossip)** still handles unsafe blocks
- **Lower resource requirements** compared to full derivation

## Test Structure

### Test Files

- `init_test.go` - Test setup and configuration
- `tip_mode_test.go` - Test cases for tip mode functionality

### Test Cases

1. **TestTipModeBasicSync** - Verifies basic safe head synchronization
- Tests that tip mode verifier syncs safe heads from sequencer
- Ensures safe head progression matches the sequencer

2. **TestTipModeFinalizedSync** - Verifies finalized head synchronization
- Tests that tip mode verifier syncs finalized heads from sequencer
- Ensures finalized head progression matches the sequencer

3. **TestTipModeUnsafeViaP2P** - Verifies P2P gossip still works
- Tests that unsafe blocks are still received via P2P
- Ensures CL sync remains functional in tip mode

4. **TestTipModeContinuousSync** - Verifies continuous operation
- Tests that tip mode continues to sync over multiple rounds
- Ensures the verifier stays in sync with the sequencer

## Implementation Details

### Configuration

The tip mode tests use a custom system preset that:

1. Creates a standard single-chain multi-node setup (sequencer + verifier)
2. Configures the verifier to run in tip mode
3. Sets the verifier's remote RPC to the sequencer's RPC endpoint
4. Maintains P2P connections for unsafe block sync

### Code Organization

#### Preset Layer (`op-devstack/presets/`)

- **`tip_mode.go`** - Defines the `TipMode` preset and `NewTipMode()` constructor
- **`cl_config.go`** - Contains `WithTipMode()` option for configuring tip mode

#### System Layer (`op-devstack/sysgo/`)

- **`system_tip_mode.go`** - Defines `TipModeSystem()` that creates the test infrastructure
- **`l2_cl.go`** - Extended `L2CLConfig` with `TipModeEnabled` and `TipModeRemoteRPC` fields
- **`l2_cl_opnode.go`** - Modified to use tip mode config from `L2CLConfig`

### How It Works

1. **System Creation**: `TipModeSystem()` creates a minimal system with sequencer
2. **Dynamic Configuration**: After sequencer is created, retrieves its RPC URL
3. **Verifier Setup**: Creates verifier CL node with tip mode enabled, pointing to sequencer RPC
4. **P2P Setup**: Connects nodes via P2P for unsafe block gossip
5. **Test Execution**: Tests verify sync behavior across different safety levels

## Running the Tests

```bash
# Run all tip mode tests
go test ./op-acceptance-tests/tests/tip_mode/...

# Run a specific test
go test ./op-acceptance-tests/tests/tip_mode/ -run TestTipModeBasicSync

# Run with verbose output
go test ./op-acceptance-tests/tests/tip_mode/... -v
```

## Design Decisions

### Why AfterDeploy Hook?

The system uses `stack.AfterDeploy()` to configure the tip mode verifier because:
- The sequencer must be created first to get its RPC endpoint
- The verifier needs the sequencer's RPC URL for tip mode configuration
- This ensures proper ordering of node creation and configuration

### Why Keep P2P Connections?

Even in tip mode, P2P connections are maintained because:
- Unsafe blocks are still received via P2P gossip
- This ensures the node can participate in consensus layer sync
- It provides a more complete syncing experience

### Configuration Approach

The implementation supports two configuration methods:
1. **Programmatic**: Via `L2CLConfig` fields (preferred for tests)
2. **Environment Variables**: Via `OP_NODE_ROLLUP_TIP_MODE*` (backward compatible)

This dual approach ensures:
- Clean, testable code with explicit configuration
- Backward compatibility with existing environment-based setups

## Future Enhancements

Potential improvements to these tests:

1. **Reorg Testing** - Verify behavior during chain reorganizations
2. **Connection Failure** - Test recovery when remote RPC is unavailable
3. **Performance Metrics** - Measure sync speed vs. full derivation
4. **Multiple Verifiers** - Test multiple tip mode nodes syncing from same source
5. **Mixed Mode** - Test systems with both tip and full derivation verifiers

## Related Files

- `/root/optimism-2/op-node/rollup/driver/tip_mode.go` - Core tip mode implementation
- `/root/optimism-2/op-node/rollup/driver/driver.go` - Integration with driver
- `/root/optimism-2/op-node/rollup/sync/config.go` - Sync configuration
16 changes: 16 additions & 0 deletions op-acceptance-tests/tests/tip_mode/init_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package tip_mode

import (
"testing"

"github.com/ethereum-optimism/optimism/op-devstack/compat"
"github.com/ethereum-optimism/optimism/op-devstack/presets"
)

func TestMain(m *testing.M) {
presets.DoMain(m,
presets.WithTipModeSystem(),
presets.WithConsensusLayerSync(),
presets.WithCompatibleTypes(compat.SysGo),
)
}
Loading