你在 L2 上有笔钱,想提回 L1。系统告诉你:请等七天。
这七天不是在做技术处理,也不是效率问题。它是这套方案安全性的组成部分——把它缩短,安全性就真的下降了。
这一讲讲的就是这七天在保护什么。
一、核心思路
第 26 讲的洞察是:重复执行是为了验证,不是为了计算。 Optimistic Rollup 的回答很直接:
⭐ 那就默认不验证。只有当有人提出异议时,才验证一次。
乐观假设:提交的状态转移是正确的
⭐ 但给出一个【挑战窗口】,期间任何人可以提交"欺诈证明"
⟹ 若挑战成功,作恶者的保证金被没收,错误状态被回滚
⟹ 若无人挑战,状态最终确定
打个比方
这是报税的模式。
税务局不会逐份核算每个人的账——那太贵了。它的做法是:先默认你报的是真的,但保留一个追溯期,期间一旦被查出造假,罚款远高于你偷的税。
于是绝大多数时候没有人在算账,而系统依然是可信的——因为造假的期望收益是负的。
⚠️ 注意这里的安全模型和之前所有内容都不同,而这个转变值得停一下:
之前:安全性来自"所有人都验证"
⭐ 现在:安全性来自"至少有一个诚实的人在盯着,并且他能把证明送上 L1"
⟹ 这叫【1-of-N 诚实假设】
⭐ 1-of-N 是一个很弱的假设(比"多数诚实"弱得多),但它有两个前提必须成立——第六节会说明这两个前提在现实中并不总是满足。
二、完整流程
用户 ──▶ 排序器(Sequencer)
│
│ ① 排序 + 执行,⭐ 立即给出"软确认"(几百毫秒)
│
├──▶ ② 把【压缩后的交易数据】发到 L1 这一步是安全的根基
│
└──▶ ③ 提交新的状态根(不附带任何证明)
│
│ ④ 挑战期(约 7 天)
│ 任何人可提交欺诈证明
▼
⑤ 期满,状态在 L1 上最终确定,提款生效
⭐ 第 ② 步是整个方案的地基,值得单独强调:
交易数据必须发到 L1 —— 不是状态,是【原始交易数据】。
⚠️ 为什么必须是数据而不是状态:
① 有了数据,任何人都能【独立重新计算】出正确的状态
⟹ 于是能发现排序器作弊,并生成欺诈证明
② 有了数据,即使排序器永远消失,
用户也能自己重建状态、证明自己的余额、单方面提款
⟹ 这正是第 26 讲那条 L2 定义的技术实现。
三、欺诈证明:交互式二分查找
难题:Alice(排序器)说"执行完这批交易后状态是 X",Bob 说"不对,是 Y"。
L1 怎么判断谁对?
❌ 朴素方案:在 L1 上把整批交易重跑一遍
⚠️ 这完全违背了 Rollup 的初衷——
如果 L1 要重跑,那扩容就没发生。
⭐ 真正的方案:不要重跑全部,只重跑分歧的那一步。
二分定位
假设这批交易共有 100 万个执行步骤。
第 1 轮:L1 问双方——"第 50 万步之后,状态根是什么?"
Alice 答 A₅₀ₒₖ,Bob 答 B₅₀ₒₖ
⭐ 若不同 ⟹ 分歧在前一半;若相同 ⟹ 分歧在后一半
第 2 轮:在有分歧的那一半里再二分……
约 log₂(100 万) ≈ 20 轮之后,
分歧被定位到【某一条具体的指令】:
"第 314159 步执行前状态是 S,Alice 说执行后是 S_a,Bob 说是 S_b"
⟹ 此时 L1 只需要【执行这一条指令】,就能判定谁在撒谎。
⭐ 这就是全部魔法:把"在 L1 上执行一百万步"压缩成"在 L1 上执行一步 + 20 轮问答"。
单步验证的实现
要在 L1 上执行"L2 的一条指令",需要 L2 的执行环境能被 L1 精确模拟。实际做法是:
把 L2 的执行编译成一个【简单的、确定性的指令集】
(Arbitrum 用 WASM 的一个子集,Optimism 用 MIPS)
⭐ 然后在 L1 上用 Solidity 实现这个指令集的【单步解释器】
⟹ 只需要几百行代码,且只在争议时才被调用
注意这个设计的经济性:那个单步解释器可能一次都不会被真正执行——它的价值在于「存在」本身构成威慑。
四、挑战期为什么是 7 天
⭐ 这就是那个追溯期。它的长度不是拍脑袋的,也不是技术处理时间——它必须长到:即使 L1 被审查、挑战者被挤出去一段时间,他仍然来得及把证据递上去。下面算的就是这个。
这是最常被追问的数字。答案是:它不是技术需求,是安全边际。
挑战期必须长于"⭐ 最坏情况下,一个诚实的挑战者仍能把证明送上 L1 的时间"。
最坏情况包括什么:
① L1 严重拥堵,Gas 价格高到挑战者被挤出
② 攻击者【主动审查】,买通区块构建者不打包挑战交易
③ 挑战者所在地区的网络被切断
④ 挑战者需要时间发现问题、准备证明
⭐ 7 天的实质是:假设攻击者最多能持续审查 L1 若干天,7 天留出了足够冗余。
⚠️ 代价是真实的:
从 L2 提款到 L1,必须等 7 天。
⟹ ⭐ 于是催生了【流动性桥】:
第三方立刻给你钱,自己去等那 7 天,收取手续费。
但这引入了全新的信任假设——你现在信任的是这个桥,
而不是 Rollup 的密码学保证。
这是一个反复出现的模式:一个安全机制带来的不便,会被市场用「引入新的信任」来消除。 用户体验改善了,而大多数用户并不知道自己的安全模型已经变了。
五、排序器中心化
绝大多数 Rollup 目前只有一个排序器,通常由项目方运营。它能做什么:
✅ 它不能偷钱 —— ⭐ 状态转移的正确性由欺诈证明保证
但它可以:
① 审查:拒绝处理某些用户的交易
② 提取 MEV:它完全掌握排序权(第 34 讲)
③ 宕机:整条 L2 停止服务
强制包含:单方面退出的技术保障
⭐ 正是为了让第 26 讲那条 L2 定义成立,Rollup 必须提供一条绕过排序器的路:
用户可以【直接向 L1 上的 Rollup 合约】提交交易,
⭐ 排序器必须在 N 个区块内把它包含进 L2,
否则任何人都可以强制推进 L2 的状态。
⟹ 排序器审查你 ⟹ 你走 L1 强制通道
⟹ 排序器彻底消失 ⟹ 你用 L1 上的数据自己重建状态并提款
⭐ 判断一个 Rollup 是否真的是 L2,就看这条通道是否存在且真的可用。
去中心化排序器
方向包括:多个排序器轮流出块、基于共享排序层、以及基于 L1 的排序(based rollup)——直接用 L1 的提议者来排序,从而完全继承 L1 的抗审查性。代价是失去"几百毫秒软确认"这一 UX 优势。
六、欺诈证明的活性问题
回到第一节那个 1-of-N 假设。它需要两个前提:
① ⭐ 必须真的有人在监督
运行一个"挑战者"节点需要成本:跑一个完整的 L2 节点,
持续重算所有状态,而且绝大多数时候什么也不会发生。
② 挑战者必须愿意并且能够支付 L1 的 Gas
一次完整的挑战(20 轮交互 + 单步执行)在 L1 上很贵。
所以协议必须提供经济激励:作恶者的保证金,一部分给挑战者。
⚠️ 但一个更尴尬的现实是:
在相当长的时间里,多数 Optimistic Rollup 的欺诈证明【并未真正启用】。
⭐ 它们靠一个"安全委员会"(多签)来处理争议——
这被业内直白地称为"训练轮(training wheels)"。
⟹ 在这个阶段,它们的安全性实际上等于那个多签,
而不是等于 L1。
⭐ 所以评估一个 Rollup,要问的是:
① 欺诈证明启用了吗?是不是【任何人】都能提交,还是只有白名单?
② 安全委员会能做什么?能不能绕过挑战期直接改状态根?
③ 强制包含通道真的能用吗?(有没有人实测过)
④ 升级有没有时间锁?(第 24 讲的四个问题在这里同样适用)
七、成本结构
⭐ 一笔 L2 交易的成本 ≈ 【分摊的 L1 数据成本】+ 【L2 执行成本】
↑ 长期占主导
所以优化的方向全部在数据上:
① 批处理:一批越大,每笔分摊的固定成本越低
② ⭐ 压缩:
签名可以聚合或省略(用有效性证明时)
nonce、gasPrice 等字段可以省
常用地址用索引代替(20 字节 → 3 字节)
③ 用 blob 代替 calldata(EIP-4844)——第 29 讲
⭐ EIP-4844 之所以让 L2 费用下降一个数量级,正是因为它直击了成本结构里占主导的那一项。
八、一个常见误解
“Optimistic Rollup 比 ZK Rollup 慢。”
⭐ 不准确。要区分三种"确认":
软确认(排序器给出): ⭐ 两者都是几百毫秒,没有差别
L1 数据上链: 两者都是几分钟,没有差别
提款到 L1 最终生效: OP 要 7 天,ZK 只要证明生成时间(几十分钟)
⭐ 所以差别只在最后一项——而它只影响"跨链提款",不影响 L2 内部的任何操作。 日常使用中,用户感受不到区别。
九、Go:二分挑战协议
package fraud
// Claim 是对某个执行步骤之后状态的声明。
type Claim struct {
Step uint64
StateRoot [32]byte
}
// Challenge 维护一次交互式二分争议。
// ⭐ 它把"在 L1 重跑一百万步"压缩成"20 轮问答 + 在 L1 执行一步"。
type Challenge struct {
Lo, Hi uint64 // 当前分歧区间 [Lo, Hi]
LoRoot [32]byte // 双方在 Lo 处【一致】的状态根
HiAsserter [32]byte // 断言者声称的 Hi 处状态根
HiChallenger [32]byte // 挑战者声称的 Hi 处状态根
Turn int // 0 = 等断言者回答,1 = 等挑战者回答
}
func NewChallenge(steps uint64, start, assertEnd, challengeEnd [32]byte) *Challenge {
return &Challenge{
Lo: 0, Hi: steps,
LoRoot: start,
HiAsserter: assertEnd,
HiChallenger: challengeEnd,
}
}
// Mid 返回当前区间的中点——下一个要问的步骤。
func (c *Challenge) Mid() uint64 { return c.Lo + (c.Hi-c.Lo)/2 }
// Bisect 收敛区间:
// 双方各自给出中点处的状态根,分歧一定落在其中一半。
func (c *Challenge) Bisect(midAsserter, midChallenger [32]byte) {
mid := c.Mid()
if midAsserter != midChallenger {
// 中点已经不同 ⟹ 分歧在前半段
c.Hi = mid
c.HiAsserter = midAsserter
c.HiChallenger = midChallenger
} else {
// 中点相同 ⟹ 前半段没问题,分歧在后半段
c.Lo = mid
c.LoRoot = midAsserter
}
}
// Resolved 判断是否已收敛到单步。
// 此时 L1 只需执行这一条指令即可判定谁在撒谎。
func (c *Challenge) Resolved() bool { return c.Hi-c.Lo == 1 }
// Rounds 返回收敛到单步需要的轮数:⌈log₂(步数)⌉。
// 一百万步只要 20 轮,四百万步 22 轮——这就是二分的威力。
// ⚠️ 注意是【向上】取整:步数不是 2 的幂时,还需多一轮才能把区间收到 1。
func Rounds(steps uint64) int {
n := 0
for steps > 1 {
steps = (steps + 1) / 2
n++
}
return n
}
// Resolve 是最终裁决:在 L1 上执行【那一条指令】。
// stepFn 是用 Solidity 实现的单步解释器(Arbitrum 用 WASM 子集,
// Optimism 用 MIPS)。它可能永远不会被真正调用——
// 它的价值在于"存在"本身构成威慑。
func (c *Challenge) Resolve(stepFn func(pre [32]byte) [32]byte) (asserterWins bool) {
if !c.Resolved() {
panic("fraud: 尚未收敛到单步")
}
actual := stepFn(c.LoRoot)
return actual == c.HiAsserter
}
// ChallengePeriod 返回挑战期应该设多长。
// 它不是技术需求,是安全边际:
// 必须长于"最坏情况下诚实挑战者仍能把交易送上 L1 的时间"。
func ChallengePeriod(maxCensorshipDays, safetyMarginDays int) int {
return maxCensorshipDays + safetyMarginDays
}
十、本讲小结
- 核心思路:默认相信,有异议再证明。 安全模型从"所有人都验证"变成 1-of-N 诚实假设——只需要一个诚实的人在盯着,并且他能把证明送上 L1。
- ⭐⭐ 交易数据必须发到 L1,而且是原始数据而非状态。因为有了数据,任何人都能独立重算并发现作弊,也能在排序器消失后自行重建状态提款。这是 L2 定义的技术实现。
- ⭐⭐ 欺诈证明的关键是交互式二分:一百万步的分歧,约 20 轮问答就能定位到单条指令,L1 只需执行这一步。把"在 L1 重跑整批"压缩成"在 L1 跑一步"。
- 那个单步解释器可能永远不会被调用——它的价值在于"存在"本身构成威慑。
- 7 天挑战期不是技术需求,是安全边际:必须长于"最坏情况下诚实挑战者仍能送达 L1 的时间"(L1 拥堵、主动审查、网络切断)。
- 流动性桥消除了 7 天的不便,但也替换了安全模型——这是一个反复出现的模式:安全机制的不便被市场用"引入新信任"消除,而多数用户不知道自己的安全模型已经变了。
- 排序器不能偷钱,但能审查、提取 MEV、宕机。 强制包含通道是"单方面退出"的技术保障——判断一个 Rollup 是不是真 L2,就看这条通道是否存在且可用。
- 1-of-N 假设需要两个前提:真的有人在监督(要成本,且绝大多数时候白跑)、挑战者付得起 L1 Gas。而现实是,很长时间里多数 OP Rollup 的欺诈证明并未真正启用,靠多签"训练轮"——那个阶段它们的安全性等于那个多签,不等于 L1。
- 成本以 L1 数据费为主导,所以优化全在压缩和 blob 上——这正是 EIP-4844 让 L2 费用降一个数量级的原因。
- “OP 比 ZK 慢"不准确:软确认和数据上链两者相同,差别只在跨链提款,不影响 L2 内部任何操作。
思考题
- 1-of-N 诚实假设比"多数诚实"弱在哪、强在哪?它需要哪两个额外前提?
- 为什么必须把原始交易数据发到 L1,而不是只发状态根?分别说明数据缺失时会失去什么能力。
- 一批交易有 400 万个执行步骤,二分需要多少轮?如果改成四分(每轮问三个点),轮数变成多少?各自的链上成本如何权衡?
- 单步解释器为什么要用 MIPS 或 WASM 子集,而不是直接用 EVM?
- 如果挑战期改成 1 小时,攻击者需要做到什么才能偷走资金?改成 30 天有什么代价?
- 流动性桥让提款变成即时。请写出用户在这两种方式下各自信任的是什么。
- 排序器审查你的交易。请写出你的完整应对步骤,并指出每一步依赖协议的哪个机制。
- 一个 Rollup 说"我们已上线欺诈证明”。请列出你还要追问的四个问题。
- 为什么欺诈证明的活性需要经济激励?设计一套激励方案,说明保证金多大合适。
- 一个用户抱怨"L2 转账也要等 7 天"。请指出他混淆了什么。