Antler by Autoflux

Sequencing & Consensus

How blocks are ordered — the sequencer, the P2P layer, batch encoding, and how L1 derivation defines the canonical chain.

Path: consensus-or-sequencing

Third-party documentation. This is independently authored analysis of the public Optimism (OP Stack) codebase — not the official docs, and not reviewed or endorsed by the Optimism (OP Stack) team.

Sequencing & Consensus

Understanding the OP Stack's ordering model is understanding two separate mechanisms that must not be conflated:

  • Sequencing — how new L2 blocks get produced and ordered right now.
  • Consensus / derivation — how the canonical chain is reconstructed from L1, independent of the sequencer.

The sequencer

A single sequencer (one op-node + op-geth pair, controlled by the chain operator) is responsible for ordering user transactions. Users submit transactions to the sequencer's RPC endpoint. The sequencer:

  1. Accepts the transaction and assigns it a sequence number.
  2. Builds a block by calling the engine API (engine_forkchoiceUpdated with payload attributes).
  3. Executes the block in op-geth and broadcasts the completed block to the P2P network so other nodes can sync it optimistically.

Because the sequencer orders transactions and immediately broadcasts the resulting blocks, users get fast confirmation (often sub-second) without waiting for L1. This is the unsafe / "sequencer" view of the chain.

The P2P layer

op-nodes gossip blocks over a libp2p-based network. A block propagated this way is labeled unsafe — it is the sequencer's claim, not yet anchored to L1. Verification happens later, during derivation. The P2P network is a scalability and latency optimization: it lets the network agree on an optimistic head quickly while the slow, authoritative process (L1 derivation) catches up.

Batch encoding

To make blocks reconstructible, op-batcher encodes L2 block data into channels and frames:

  • A channel is a byte stream holding a set of compressed batches. Channels are created, filled, and closed by the batcher.
  • A channel is split into fixed-size frames, each with a channel ID, frame number, and total-frames count. Frames are what actually get included in L1 transactions.
  • The L1 transaction that carries frames is sent to the batch inbox (an L1 address, currently the BatchInbox). Its calldata (or blob data, on post-Ecotone chains) is the raw frame.

Derivation: L1 is the consensus layer

Every op-node runs a derivation pipeline that reconstructs canonical L2 blocks purely from L1:

  1. L1 traversal — read L1 blocks and filter for batch-inbox transactions and SystemConfig updates.
  2. Frame/channel recovery — reassemble frames into channels, decompress them, and extract batches.
  3. Attributes queue — turn each batch into a set of L2 block attributes (transactions + L1 attributes like the origin block and timestamp).
  4. Engine apply — feed the attributes to op-geth via the engine API. op-geth executes the block and returns its hash.

The result is the safe head — the canonical chain as derived from L1. When the sequencer behaves, the unsafe head leads the safe head by some number of blocks (the lag comes from L1 latency and batch submission cadence). If the sequencer ever proposes an invalid batch, the derivation pipeline simply skips it; the P2P-announced version of the chain is overridden.

Consensus guarantees

  • Liveness — as long as batches reach L1, the chain progresses regardless of the sequencer.
  • Safety — a batch is only final once it is included in L1 and its derivation succeeds; the "unsafe" P2P view is never trusted for settlement.
  • No re-orgs from consensus — the safe chain never reverts (only the unsafe head can be discarded if it disagrees with derivation).

Edge cases

  • If the batcher goes down, block production continues via P2P but the chain stops being derivable from L1 — and blocks that never make it into a batch are lost from the canonical chain.
  • Batches that reference an L1 block outside the sequencing window (the max age of L1 origin a batch may use) are invalid and skipped.
  • Channel data is compressed with zlib; a corrupted frame fails the channel and the whole channel's blocks are skipped, not partially applied.

Interface

Interface
go
// Derivation pipeline stages (op-node), from L1 to canonical L2 blocks.
// Each stage consumes the previous one's output; the pipeline is the "consensus".
func Derive(dep *Driver, l1 *L1Source, l2 *L2Source) {
// 1. Fetch L1 blocks, filter for the batch inbox + system config updates
l1Blocks := dep.L1Traversal.NextL1Block(l1)
 
// 2. Extract batch channels from the batch inbox transactions
batches := dep.BatchQueue.NextBatch(l1Blocks)
 
// 3. Decode the batch into L2 block attributes
attrs := dep.AttributesQueue.NextAttributes(batches)
 
// 4. Apply attributes to the execution engine (op-geth)
l2.PayloadAttributes(attrs)
}
 
STATUSinterface