Channels

Taken from the OP Stack specs, Channels are a set of sequencer batches (for any L2 blocks) compressed together.

Where Channels fit in the OP Stack

L2 transactions are grouped into what are called sequencer batches. In order to obtain a better compression ratio when posting these L2 transactions to the data availability layer, sequencer batches are compressed together into what is called a Channel. This ultimately reduces data availability costs. As previously noted in the Frame section, Channels may not "fit" in a single batcher transaction, posting the data to the data availability layer. In order to accommodate large Channels, a tertiary Frame data type breaks the Channel up into multiple Frames where a batcher transaction then consists of one or multiple Frames.

Contents of a Channel

A Channel is comprised of the following items.

  • A ChannelId which is a 16 byte long identifier for the channel. Notice, Frames also contain a ChannelId, which is the identical to this identifier, since frames "belong" to a given channel.
  • A BlockInfo that marks the L1 block at which the channel is "opened" at.
  • The estimated size of the channel (as a usize) used to drop the channel if there is a data overflow.
  • A boolean if the channel is "closed". This indicates if the last frame has been buffered, and added to the channel.
  • A u16 indicating the highest frame number within the channel.
  • The frame number of the last frame (where is_last set to true).
  • A mapping from Frame number to the Frame itself.
  • A BlockInfo for highest L1 inclusion block that a frame was included in.

Channel Encoding

Channel encoding is even more straightforward than that of a Frame. Simply, a Channel is the concatenated list of encoded Frames.

Since each Frame contains the ChannelId that corresponds to the given Channel, constructing a Channel is as simple as calling the Channel::add_frame method for each of its Frames.

Once the Channel has ingested all of it's Frames, it will be marked as "ready", with the Channel::is_ready method returning true.

The Channel Type

As discussed above, the Channel type is expected to be populated with Frames using its Channel::add_frame method. Below we demonstrate constructing a minimal Channel using a few frames.

#![allow(unused)]
fn main() {
use op_alloy_protocol::{Channel, Frame};

// Construct a channel at the given L1 block.
let id = [0xee; 16];
let block = BlockInfo::default();
let mut channel = Channel::new(id, block);

// The channel will consist of 3 frames.
let frame_0 = Frame { id: [0xee; 16], number: 0, ..Default::default() };
let frame_1 = Frame { id: [0xee; 16], number: 1, ..Default::default() };
let frame_2 = Frame { id: [0xee; 16], number: 2, is_last: true, ..Default::default() };

// Add the frames to the channel.
channel.add_frame(frame_0);
channel.add_frame(frame_1);
channel.add_frame(frame_2);

// Since the last frame was ingested,
// the channel should be ready.
assert!(channel.is_ready());
}

There are a few rules when adding a Frame to a Channel.

Notice, Frames can be added out-of-order so long as the Channel is still open, and the frame hasn't already been added.