导语:Rollup 的中心化之踵与去中心化的漫漫长路
Rollup 扩容方案解决了以太坊的吞吐瓶颈,但也引入了一个新的单点故障——序列器(Sequencer / 定序器)。目前大多数 Rollup(Optimism、Arbitrum、Base、zkSync)的序列器都由项目方中心化运营,负责接收 L2 交易、排序、执行并生成批次提交到 L1。这个中心化的角色拥有巨大的权力:审查交易、抢跑用户、提取 MEV。
与此同时,Rollup 的演进并未止步于 L2。L3(Layer3)和应用链(App Chain)正在把模块化区块链的粒度进一步拆分:稳定币项目可能想要自己的执行链,游戏项目可能需要秒级确认且零 gas 的专属环境。理解序列器、L3 与应用链的图景,是把握以太坊扩容路线图下一阶段的关键。
一句话总结:序列器是 Rollup 的"软肋"——当前绝大多数序列器都是中心化的,而去中心化、共享排序、L3 与应用链的探索,正在把以太坊的多层架构推向更深更细的模块化未来。
1. 序列器中心化问题
1.1 序列器的职责与权力
序列器的角色:
用户提交交易 序列器 L1 链上 Rollup 合约
│ │ │
│ ── tx ─────►│ 1. 接收交易并排序 │
│ │ 2. 在本地状态模拟执行 │
│ │ 3. 确认交易(soft confirmation) │
│◄─ receipt ─ │ 4. 构建批次 │
│ │ 5. 压缩发布到 L1(calldata/blob)│─────►
│ │ 6. 提交新的状态根 │─────►
序列器拥有的权力包括:
- 交易排序:决定哪些交易先执行,哪些后执行,哪些被叉掉重排
- 审查:可以拒绝接收特定用户的交易
- 抢跑:看到用户交易后,插入自己的交易获利(MEV)
- 延迟:故意延迟某笔交易的确认
- 软确认:在 L1 提交之前,序列器的确认虽然快但非最终(可被回滚)
1.2 中心化序列器的风险矩阵
| 风险 | 描述 | 后果 |
|---|---|---|
| 审查 | 拒绝处理特定地址的交易 | 用户无法与 L2 交互 |
| MEV 提取 | 序列器利用排序权抢跑/夹击 | 用户滑点增加,资金损失 |
| 活性丧失 | 序列器宕机或离线 | L2 无法处理任何新交易 |
| 状态回滚 | 序列器提交错误状态根 | 正确状态被覆盖,资金风险 |
| 监管合规 | 序列器运营方被强制要求过滤 | 去中心化承诺名存实亡 |
一句话总结:序列器的中心化是 Rollup 的"原罪"——它用单点控制换来了 UX 的极致优化,但也把 Rollup 的安全模型从"密码学保证"降级为"信任运营方"。
2. 去中心化序列器方案
2.1 Espresso Sequencer
Espresso 是一个共享的碎片化排序层,多个 Rollup 可以共用它来去中心化自己的序列器。
Espresso 架构:
Rollup A Rollup B Rollup C (多个 L2 共享同一排序层)
│ │ │
└──────────┴──────────┘
│
┌───────▼────────┐
│ Espresso HotShot │ HotShot 共识协议
│ 共识层 │ 拜占庭容错 + 快速最终性
└───────┬────────┘
│
┌───────▼────────┐
│ Espresso DA 层 │ 数据可用性保证
└───────┬────────┘
│
▼
L1(以太坊或其他 DA 层)
Espresso 的核心设计:
- HotShot 共识:基于 HotStuff 的 BFT 共识协议,支持数千 TPS 的排序吞吐
- 共享安全:所有连接 Rollup 共享同一组验证者,验证者经济激励互通
- 可组合性:不同 Rollup 间的跨 Rollup 原子事务成为可能
- Rollup 主权:Rollup 仍保留执行层和状态转换规则的自主权
2.2 Astria Sequencer
Astria 是一个构建在 Celestia DA 之上的去中心化共享序列器网络:
Astria 架构:
用户交易
│
▼
Astria 序列器网络(基于 Tendermint 共识)
│ 对交易排序,生成"排序区块"
▼
Celestia DA 层
│ 保证排序数据的可用性
▼
L1(以太坊或其他结算层)
│ 验证 Rollup 的状态根和 fraud proof / validity proof
▼
Rollup 执行节点
从 Celestia 拉取排序交易,本地执行,维护状态
Astria 的独特之处在于排序与执行解耦:
- Astria 只负责排序(sequencing),不负责执行
- Rollup 执行节点自己从 DA 层读取交易顺序并执行
- 这意味着不同的rollup 可以对同一笔排序数据采用不同的执行规则
2.3 Metis 去中心化序列器池
Metis 是率先在去中心化序列器方向落地的 L2 项目之一,采用**序列器池(Sequencer Pool)**机制:
Metis 序列器池:
多个序列器节点组成一个轮换池
│
├─ 质押 Metis 代币成为候选序列器
├─ 按质押权重或轮询机制选出当前活跃序列器
├─ 活跃序列器负责排序并提交批次到 L1
└─ 如果活跃序列器作恶或离线,池子投票选出接替者
经济激励:
- 序列器收取交易费(L2 gas 的一部分)
- 作恶被检测会被质押罚没
- 离线会被替换,质押可能被部分罚金
2.4 去中心化序列器方案对比
| 方案 | 共识机制 | DA 层 | 共享性 | 成熟度 |
|---|---|---|---|---|
| Espresso | HotShot BFT | 自建 DA | 共享(多 Rollup) | 测试网阶段 |
| Astria | Tendermint | Celestia | 共享(多 Rollup) | 测试网/早期主网 |
| Metis | 轮询 + 质押投票 | 以太坊 | 专用(Metis 独占) | 主网上线 |
| OP Stack 共享排序 | 自定义 | 以太坊 | 共享(OP Chains) | 开发中 |
一句话总结:去中心化序列器的三种路线——共享排序层(Espresso/Astria)让多个 Rollup 共同使用去中心化基础设施,专用序列器池(Metis)让单个 Rollup 自治轮换——共同的瓶颈在于共识延迟和跨 Rollup 协调的难度。
3. 强制交易与逃生舱
3.1 逃生舱机制(Force Transactions / Escape Hatch)
在去中心化序列器完全成熟之前,Rollup 必须在合约层面保留一个"逃生舱":即使中心化序列器宕机或审查,用户仍然可以直接通过 L1 提交交易到 L2。
// Optimism Bedrock 风格的强制交易入口(概念简化)
contract L1Portal {
// 用户直接向 L1 合约提交 L2 交易
function sendMessageToL2(
address target,
uint256 gasLimit,
bytes calldata data
) external payable {
// 1. 从 L1 msg.value 中扣除足够的 gas 储备
uint256 gasCost = gasLimit * L2_BASE_FEE;
require(msg.value >= gasCost, "Insufficient ETH for gas");
// 2. 生成唯一的消息哈希
bytes32 messageHash = keccak256(abi.encode(
msg.sender, target, gasLimit, data, block.timestamp
));
// 3. 存入消息队列,等待 L2 序列器或服务节点纳入
messageQueue.push(messageHash);
emit MessageSent(msg.sender, target, messageHash, gasLimit, data);
}
// L2 序列器必须定期从 L1 读取此队列,否则证明系统无法闭合
}
3.2 逃生舱的延迟与安全
强制交易的时序(以 Optimism 为例):
时间 0:用户调用 L1Portal.sendMessageToL2()
│
↓ ~L1 出块时间(12 秒)
│
消息写入 L1 区块
│
↓ L2 序列器必须读取 L1 状态
│
序列器将消息纳入 L2 批次
│
↓ L2 内部执行
│
交易在 L2 执行完成
延迟分析:
- 最佳情况:1-2 个 L1 区块(约 15-30 秒)
- 如果序列器离线:消息在 L1 上永久可查询,任何"恢复节点"都可以读取并执行
- 资金安全性:用户可以确信,只要消息上了 L1,最终一定能在 L2 上执行
| Rollup | 逃生舱机制 | 延迟 |
|---|---|---|
| Optimism | L1→L2 Message Passing | ~15 秒(序列器在线) |
| Arbitrum | Retryable Tickets | ~10 分钟 |
| Base | L1→L2 标准桥 | ~15 秒 |
| zkSync | Priority Queue | ~数分钟 |
一句话总结:逃生舱是中心化序列器的"安全绳"——它不解决审查问题,但保证在序列器宕机时用户仍能取回资金;它的延迟虽然长,却给了 Rollup 安全模型一个不可被移除的底线。
4. L3 架构:Validium、隐私链与递归 Rollup
4.1 为什么是 L3
L2 解决了以太坊的吞吐问题,但引入了新的限制:
- L2 的 gas 仍比理论上可能的最低成本高(需要考虑 L1 数据发布成本)
- L2 需要服务所有 dApp,无法为特定用例优化
- L2 的序列器和数据可用性是公共资源
L3(在 L2 之上构建的 Rollup)进一步分层:
模块化堆栈:
L1(以太坊):结算 + 最终性 + 安全经济
│
▼
L2(通用 Rollup):继承 L1 安全,解决吞吐问题
│ 例如:Arbitrum One、Optimism、Base、zkSync Era
▼
L3(应用链 / Validium / 隐私 Rollup):
│ 使用 L2 作为结算层,享受更低的成本
│ 可以高度定制:隐私、游戏引擎、特殊共识
│ 代表:Arbitrum Orbit、OP Stack Superchain 链、Starknet L3
4.2 Validium:链下数据存储
Validium 是 L3 的一种重要形态:它在链下(通常由数据可用性委员会或外部提供商)存储交易数据,只在链上提交状态根。
Rollup vs Validium:
Traditional Rollup:
交易数据 → 压缩 → L1 calldata/blob(永久/半永久)
任何人都可以从 L1 数据重放执行 → 高安全
L1 gas 成本 → TPS 受限于 L1 数据带宽
Validium:
交易数据 → 链下存储(DA 委员会、IPFS、Celestia)
链上只提交零知识证明 + 状态根
数据可用性依赖外部委员会 → 信任假设更强
成本极低,TPS 极高 → 适合高频场景(游戏、支付)
| 维度 | Rollup | Validium |
|---|---|---|
| 数据存储 | L1(calldata/blob) | 链下 |
| 安全性 | L1 等价(无额外信任) | 依赖 DA 委员会/外部层 |
| 成本 | 中低 | 极低 |
| TPS | 数千 | 数万 |
| 数据重放 | 任何人可从 L1 重放 | 需要委员会存活 |
| 适用场景 | DeFi、通用 dApp | 游戏、社交、高频支付 |
4.3 L3 递归证明架构
Starknet L3 / Polygon CDK 递归 Rollup:
L3 交易
│
▼
L3 证明生成(L3 的 zk-prover)
│ 包含 N 笔 L3 交易的有效性证明
▼
L2 验证 L3 证明 + 执行 L2 自身操作
│
▼
L2 证明生成(L2 的 zk-prover,包含 L3 证明)
│ 递归证明:证明"L2 状态正确"且"包含的 L3 证明也正确"
▼
L1 验证 L2 证明
结果:
L1 的一次验证 = 证明了 L2 的所有状态 + L2 中包含的所有 L3 的状态
递归压缩了多层证明为一个简洁证明
一句话总结:L3 是"Rollup 之上的 Rollup"——用 L2 作为起点进一步缩减成本和提升定制性;Validium 用"弱 DA 假设"换取极致性能,适合不需要金融级安全的场景。
5. 应用链(App Chain)与通用 Rollup 的权衡
5.1 应用链的设计动机
为什么要为单个应用建一条链?
通用 Rollup 的限制:
- 状态竞争:N 个 dApp 共享同一状态树,高峰期相互挤占 gas
- 升级耦合:Rollup 的硬分叉/升级会影响所有 dApp
- MEV 分享:通用序列器从所有交易中提取 MEV,dApp 无法独享
- 定制困难:无法修改 opcode gas 表、预编译合约等
应用链的优势:
- 完全控制:自定义虚拟机、自定义 gas 模型、自定义共识
- 收入归属:交易费直接归应用方,不与其他 dApp 分享
- UX 优化:可用账户抽象原生实现、原生 Session Key、零 gas 交易
- 品牌独立:独特的区块浏览器、独特的用户体验
5.2 应用链技术栈对比
| 技术栈 | 特点 | 代表项目 |
|---|---|---|
| Arbitrum Orbit | 基于 Arbitrum Nitro,一键发链 | Cometh, XAI |
| OP Stack | 基于 Optimism Bedrock,Superchain 愿景 | Base, Zora, Worldcoin |
| Polygon CDK | 支持 zkEVM 和 Validium 模式 | Astar, ImmutableX |
| Cosmos SDK + Celestia DA | 主权链 + 共享 DA | dYdX v4, Neutron |
| Avalanche Subnets | 子网独立共识 | DeFi Kingdoms |
| Starknet Stack | Cairo VM + 递归证明 | Madara, Kakarot |
5.3 应用链 vs L3 的关系
应用链 ≈ L3(在以太坊扩容语境下)
细微差别:
- L3 强调"Rollup on Rollup",使用 L2 作为结算层
- 应用链可以是 L2、L3,甚至侧链(不完全继承 L1 安全)
- "应用链"更强调"专用"而非"层级"
趋势:
- 以太坊 L2 正从"通用为王"走向"专用链簇"
- OP Stack 的 Superchain 和 Arbitrum Orbit 都在推动 One-Click Chain Launch
一句话总结:应用链不是 Rollup 的竞争对手,而是通用 Rollup 生态的补充——当 dApp 的规模大到足以支撑独立链的成本时,迁移到自有链可以释放 UX 和经济的全部潜力。
6. 数据可用性采样 DAS
6.1 L3 中的 DA 挑战
L3 和应用链大量使用链下 DA 以降低成本,但如何确保链下数据的可用性?
数据可用性方案演进:
链上 DA(安全性最高):
L1 calldata → 永久存储,但贵
L1 blob(4844)→ 临时存储,便宜 5-10 倍
L2 calldata → 不适用于 L3
链下 DA(成本最低,但有信任假设):
DA 委员会(DAC):N 选 M 机制
Celestia / EigenDA / Avail:模块化 DA,轻节点采样
6.2 数据可用性采样(DAS)
Celestia 是模块化 DA 的标杆。它的 DAS(Data Availability Sampling)机制让轻节点无需下载全部数据即可高概率确认数据完整:
DAS 工作流程:
区块数据(例如 2 MB)
│
▼
Erasure Coding(Reed-Solomon 编码)
将 2 MB 扩展为 4 MB(2 倍冗余)
任意 2 MB 即可恢复原数据
│
▼
数据块分散到网络
│
▼
轻节点随机采样 K 个 chunk(例如 30 个,每个只有几十字节)
│
├─ 如果所有 chunk 都存在 → 高概率数据完整 → 接受区块
└─ 如果某个 chunk 缺失 → 请求更多 chunk 或拒绝区块
安全性分析:
- 攻击者要隐藏 2 MB 数据,必须隐藏超过 2 MB(因为冗余)
- 随机采样的 K 个 chunk 全部"幸运"来自被隐藏部分的概率极低
- K 越大,错误接受的置信度指数下降
一句话总结:DAS 用"随机采样 + 纠删码"的经济学保证替代了"全量下载"的性能消耗,让轻节点也可以独立验证 DA 而不信任任何全节点——这是模块化区块链信任假设最小化的关键使能技术。
7. 排序器 MEV 与 PBS
7.1 L2 中的 MEV
Rollup 序列器的排序权带来了与 L1 相同的 MEV(最大可提取价值)问题:
L2 MEV 类型:
三明治攻击(Sandwich):
用户:买入 Token A
序列器:先买入 → 推高价格 → 用户成交 → 序列器卖出套利
抢跑(Fronto-running):
预言机更新交易 → 序列器提前执行套利交易
审查 MEV(Censorship MEV):
拒绝包含竞争对手的交易
统计排序(Statistical Ordering):
优先处理自家相关交易以最大化利润
7.2 PBS(Proposer-Builder Separation)在 L2 的适用
以太坊 L1 PBS:
Builder 构建区块 → Proposer 选择出价最高的区块
分离了"构建"和"提议"的角色,减少提议者的集权
L2 中的潜在方案:
方案一:共享排序层内置 PBS
Espresso / Astria 等共享排序层:
- 多个 Builder 竞标对交易包的排序权
- 排序共识节点(Proposer)选择最优出价
- 序列器本身成为 PBS 基础设施
方案二:Rollup 内部拍卖
Rollup 运营方开放序列器权拍卖:
- 谁出价高谁获得下一轮的排序权
- 收入与 Rollup 代币持有者分享
- 但需要信任拍卖机制本身
方案三:强制包含 + 延迟排序
交易先提交到 L1(强制包含),排序在之后确定:
- 序列器无法看到完整交易内容后再决定排序
- 类似于加密 mempool(encrypted mempool)
7.3 Base 与 Optimism 的序列器架构
OP Stack(Base / Optimism)序列器架构:
用户交易
│
▼
L2 Sequencer(中心化)
│ - 排序交易
│ - 执行并生成区块
│ - 提供快速 soft finality
│
├──► Batch Inbox(L1 合约)
│ 提交压缩交易批次
│
└──► Output Oracle(L1 合约)
提交输出根(状态根承诺)
未来路线图:
- 阶段 1:中心化序列器(当前)
- 阶段 2:多序列器轮询(Multi-sequencer rotation)
- 阶段 3:去中心化共享排序(OP Stack Superchain 共享)
- 阶段 4:社区治理的排序决策
一句话总结:L2 的 MEV 问题比 L1 更隐蔽但同样严重——序列器的垄断排序权是 MEV 的温床;PBS 和加密 mempool 是以太坊 L2 社区正在探索的方向,但技术和治理挑战仍很大。
8. 总结
- 序列器中心化:当前几乎所有 Rollup 的核心瓶颈,带来了审查、MEV 和活性风险
- 去中心化方案:Espresso(共享 BFT)、Astria(Celestia + Tendermint)、Metis(质押轮换)三条路线并进
- 逃生舱:中心化序列器的安全底线,强制交易保证用户始终能从 L1 与 L2 通信
- L3 与 Validium:Rollup 之上的分层,用信任换性能,游戏和高频场景优先
- 应用链:从"大一统 Rollup"到"千链齐发",OP Stack 和 Arbitrum Orbit 降低了发链门槛
- DAS:让链下 DA 可被轻节点验证,是模块化区块链信任最小化的基础设施
- PBS 与 MEV:序列器的排序权天然产生 MEV,PBS、加密 mempool 和去中心化排序是长期解法
相关阅读
延伸阅读
以下是一个 L3 / 应用链通信桥接架构的伪代码描述,演示 L3 如何通过 L2 与 L1 通信,以及去中心化序列器节点的交互机制。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// ═══════════════════════════════════════════════════════════════
// L3 ↔ L2 通信桥接与去中心化序列器协调(概念实现)
// ═══════════════════════════════════════════════════════════════
// L2 上的桥接合约:接收 L3 消息并转发到 L1
contract L2Bridge {
address public sequencerRegistry; // 去中心化序列器注册表
mapping(bytes32 => bool) public processedMessages; // 防重放
bytes32[] public messageQueue;
event L3ToL1Message(bytes32 indexed messageHash, bytes payload, uint256 timestamp);
// 仅允许注册的序列器提交 L3 批次
modifier onlyRegisteredSequencer() {
require(ISequencerRegistry(sequencerRegistry).isActiveSequencer(msg.sender), "Not sequencer");
_;
}
// 序列器提交 L3 状态根和消息队列
function submitL3Batch(
bytes32 l3StateRoot,
bytes32[] calldata newMessages,
bytes calldata signature
) external onlyRegisteredSequencer {
// 验证序列器签名
require(
ISequencerRegistry(sequencerRegistry).verifySequencerSignature(
msg.sender,
keccak256(abi.encodePacked(l3StateRoot, newMessages)),
signature
),
"Invalid sequencer sig"
);
// 添加到消息队列
for (uint256 i = 0; i < newMessages.length; i++) {
if (!processedMessages[newMessages[i]]) {
processedMessages[newMessages[i]] = true;
messageQueue.push(newMessages[i]);
}
}
emit L3ToL1Message(newMessages[0], "", block.timestamp);
}
// 用户强制包含(逃生舱):绕过序列器直接提交
function forceInclude(bytes calldata l3Transaction) external payable {
require(msg.value >= FORCE_INCLUSION_FEE, "Insufficient fee");
bytes32 hash = keccak256(l3Transaction);
require(!processedMessages[hash], "Already processed");
processedMessages[hash] = true;
messageQueue.push(hash);
}
// 提款到 L1:用户在 L2 燃烧 L3 资产,到 L1 提取
function initiateWithdrawal(address l1Recipient, uint256 amount) external {
// 锁定 L2 上的 L3 桥接资产
// 生成提款证明
// 用户稍后到 L1 合约用证明提取
}
}
// 去中心化序列器注册表
interface ISequencerRegistry {
function isActiveSequencer(address sequencer) external view returns (bool);
function verifySequencerSignature(address sequencer, bytes32 hash, bytes calldata sig)
external view returns (bool);
}
contract SequencerRegistry is ISequencerRegistry {
struct Sequencer {
address addr;
uint256 stakedAmount;
uint256 lastActiveRound;
bool isActive;
}
mapping(address => Sequencer) public sequencers;
address[] public activeSequencerList;
uint256 public currentRound;
uint256 public constant MIN_STAKE = 1000 ether;
uint256 public constant ROUND_DURATION = 100; // 区块数
mapping(uint256 => address) public roundToSequencer;
modifier onlyGovernance() {
require(msg.sender == governance, "Not governance");
_;
}
address public governance;
constructor() {
governance = msg.sender;
}
// 注册成为序列器候选
function register() external payable {
require(msg.value >= MIN_STAKE, "Insufficient stake");
require(sequencers[msg.sender].addr == address(0), "Already registered");
sequencers[msg.sender] = Sequencer({
addr: msg.sender,
stakedAmount: msg.value,
lastActiveRound: 0,
isActive: false
});
}
// 轮询选择活跃序列器
function electNextSequencer() external {
require(block.number >= (currentRound + 1) * ROUND_DURATION, "Too early");
currentRound++;
uint256 index = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), currentRound)))
% activeSequencerList.length;
address selected = activeSequencerList[index];
roundToSequencer[currentRound] = selected;
sequencers[selected].lastActiveRound = currentRound;
}
function isActiveSequencer(address sequencer) external view returns (bool) {
return sequencers[sequencer].isActive;
}
function verifySequencerSignature(address sequencer, bytes32 hash, bytes calldata sig)
external pure returns (bool)
{
// ECDSA 签名验证
return true; // 简化示意
}
// 惩罚作恶的序列器
function slashSequencer(address sequencer, uint256 amount) external onlyGovernance {
Sequencer storage s = sequencers[sequencer];
require(s.stakedAmount >= amount, "Insufficient stake");
s.stakedAmount -= amount;
if (s.stakedAmount < MIN_STAKE) {
s.isActive = false;
}
}
}
// L1 提款验证合约(L3 → L2 → L1 提款路径)
contract L1WithdrawalVerifier {
mapping(bytes32 => bool) public withdrawals;
// 验证 Merkle Proof 证明提款已在 L2 上发起
function verifyAndWithdraw(
address l3Token,
uint256 amount,
address recipient,
bytes32[] calldata merkleProof,
bytes32 l2StateRoot
) external {
bytes32 leaf = keccak256(abi.encodePacked(l3Token, amount, recipient));
require(_verifyMerkleProof(leaf, merkleProof, l2StateRoot), "Invalid proof");
require(!withdrawals[leaf], "Already withdrawn");
withdrawals[leaf] = true;
// 释放 L1 上的对应资产给 recipient
// IERC20(l1Token).transfer(recipient, amount);
}
function _verifyMerkleProof(bytes32 leaf, bytes32[] calldata proof, bytes32 root)
internal
pure
returns (bool)
{
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
return computedHash == root;
}
}
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。