Technical Design

RollupDriver interface, L2 staged sync pipeline, chain config, and implementation phases

circle-info

Status: Design phase. This document describes the intended implementation. See L1/L2 Node for the problem statement and deployment modes.

RollupDriver Interface

The central abstraction. Generalizes what polygon/sync.Service does for Bor.

// rollup/driver.go
type Driver interface {
    node.Lifecycle                    // Start() / Stop()
    Type() DriverType                 // "optimistic", "zk", "based", "native", "consensus"
}

Each Driver owns its sub-services and drives L2 execution through the existing ExecutionClient interface:

Driver
  ├── DerivationPipeline   — converts L1 data into L2 blocks (optimistic, zk, based)
  ├── Bridge               — cross-chain message passing (deposits, withdrawals)
  ├── Sequencer            — produces L2 blocks (sequencer mode only)
  ├── Prover               — generates validity proofs (zk only)
  ├── L1DataSource         — reads L1 state (direct in combined mode, RPC in l2-only)
  └── L2Caplin             — embedded CL instance for consensus rollups

The Ethereum struct in backend.go gains optional L2 fields while keeping all existing L1 fields unchanged:

type Ethereum struct {
    // ... existing L1 fields unchanged ...

    rollupDriver   rollup.Driver           // nil for pure L1
    l1DataSource   rollup.L1DataSource     // direct or RPC-based
    l2ChainDB      kv.TemporalRwDB         // separate L2 database
    l2ExecModule   *execmodule.ExecModule  // separate L2 execution pipeline
}

Polygon/Bor as the First Driver

The existing Polygon integration wraps into the Driver interface without rewriting:

This proves the abstraction works and provides a migration path.

L1DataSource

The key optimization for combined mode. Abstracts L1 data access across node modes:

Implementation
Mode
Mechanism

DirectL1DataSource

Combined

Reads directly from L1's kv.TemporalRwDB — zero serialization, follows node/direct/ adapter pattern

RPCL1DataSource

L2-only

JSON-RPC calls to external L1

L1 finality notifications trigger L2 derivation via the existing shards.Notifications mechanism (same as RPC daemon state-change notifications).

L2 Staged Sync Pipeline

A L2DefaultStages factory runs alongside the existing DefaultStages in execution/stagedsync/default_stages.go:

Stage
Source
Notes

Snapshots

L2 snapshot files

Same mechanism as L1, different files

Derivation

L1 data via L1DataSource

New stage — rollup-type-specific; converts L1 data into L2 block headers and bodies

Headers

Derivation output

Reused stage, different data source

BlockHashes

Reused

Bodies

Derivation output

Reused stage, different data source

BridgeEvents

L2 state

New stage — processes deposit/withdrawal events; generalizes the existing Polygon bridge stage

Senders

Reused

Execution

L2 rules engine

Reused, different rules

StateCommit

L2 execution output

New stage — posts state roots/proofs to L1 settlement contract (sequencer mode only)

TxLookup

Reused

Finish

Reused

New stage IDs added to execution/stagedsync/stages/:

Chain Configuration Extension

Extend execution/chain/chain_config.go with an optional RollupConfig:

Native Rollups: Special Case

Native rollups are architecturally distinct from other types — they use the L1's own EVM to verify L2 state transitions via an EXECUTE precompile. There is no separate derivation pipeline, no separate state database, and no separate execution module.

The EXECUTE precompile is added to execution/vm/ and enabled via chain config when RollupConfig.Type == "native". A node running a native rollup looks identical to a standard L1 node.

RPC Multi-Chain Routing

In combined mode, the RPC server routes by chain ID:

  • Requests include a chainId field or HTTP header

  • Default chain ID = L1 (backwards compatible)

  • L2 chain ID routes to the L2's ExecModule, database, and tx pool

  • Single port, single server — no separate endpoints

Implementation Phases

Phase
Scope

1

rollup.Driver, L1DataSource, Bridge, DerivationPipeline interfaces; wrap existing Polygon as BorDriver; generalize Ethereum.Start() dispatch

2

Extend datadir.Dirs with L2 paths; second kv.TemporalRwDB; second ExecModule; L2DefaultStages factory

3

DirectL1DataSource for combined mode; RPC-based L1DataSource for L2-only; L1→L2 notification; CLI flags; chain-ID routing in RPC

4

Based rollup driver — L1-sequenced derivation, bridge stage, based consensus rules; first concrete rollup targeting combined mode

5

Optimistic rollup driver — OP Stack compatible derivation, fraud proof integration, batch submission

6

Consensus rollup driver — second Caplin instance, configurable YAML, BLS aggregate signatures to L1 settlement contract, genesis tooling, embedded validator client

6b

Liquid staking contracts (external Solidity — LST token, operator registry, delegation manager, reward distributor)

7

Native rollup driver — EXECUTE precompile in execution/vm/

8

ZK rollup driver — prover integration interface, state commitment with validity proofs

Key Files

File
Role

node/eth/backend.go:1553–1584

Start() dispatch — generalize with Driver interface

execution/stagedsync/default_stages.go

Add L2DefaultStages factory

execution/chain/chain_config.go

Add RollupConfig

polygon/sync/service.go

Reference implementation for Driver pattern

node/direct/execution_client.go

Reference pattern for DirectL1DataSource

node/ethconfig/config.go

Add RollupMode config

db/datadir/dirs.go

Extend with L2 paths

cl/clparams/config.go

All consensus timing parameters (consensus rollup)

cmd/caplin/caplin1/run.go

Pattern for launching second Caplin instance

Last updated