在 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. 1-of-N 诚实假设比"多数诚实"弱在哪、强在哪?它需要哪两个额外前提?
  2. 为什么必须把原始交易数据发到 L1,而不是只发状态根?分别说明数据缺失时会失去什么能力。
  3. 一批交易有 400 万个执行步骤,二分需要多少轮?如果改成四分(每轮问三个点),轮数变成多少?各自的链上成本如何权衡?
  4. 单步解释器为什么要用 MIPS 或 WASM 子集,而不是直接用 EVM?
  5. 如果挑战期改成 1 小时,攻击者需要做到什么才能偷走资金?改成 30 天有什么代价?
  6. 流动性桥让提款变成即时。请写出用户在这两种方式下各自信任的是什么。
  7. 排序器审查你的交易。请写出你的完整应对步骤,并指出每一步依赖协议的哪个机制。
  8. 一个 Rollup 说"我们已上线欺诈证明”。请列出你还要追问的四个问题。
  9. 为什么欺诈证明的活性需要经济激励?设计一套激励方案,说明保证金多大合适。
  10. 一个用户抱怨"L2 转账也要等 7 天"。请指出他混淆了什么。