结账
这本书一共赊了 69 条账。绝大多数在各自那一章末尾还清了,剩下八笔还不清——而那八笔,恰好就是这个领域今天还在吵的地方。这一章把账收拢:一张二十三条错误直觉的自查表,一份还不清的清单,以及按方向分岔的继续往下走。
先把主线收回来
把一件事做完,和确认它做对了,是两笔可以分开定价的账。
分开的办法只有一个动作:先把要检查的东西改写成「错一处就错一大片」的形式,然后随机抽查几处。抽查平时不管用(第 3 章:要 99% 的把握得抽 4714 格,比整份记录还长 4.6 倍),而改写让它管用了(第 11 章:同样抽三格,0.29% → 99.9996%)。
剩下的一切都是配件:承诺保证他不能事后改答案(第 4 章),Fiat–Shamir 让裁判可以不在场(第 8 章),Merkle 树让「只打开一格」变便宜(第 13 章),递归让这一切可以叠起来(第 17 章)。
而「零知识」——那个占了名字全部注意力的性质——是这个结构的副产品:验证者只看了 34 格,他当然学不到全貌。
自查表:二十三条错误直觉
每一章都有一条。合上书之后,这张表是你真正带走的东西。
| 章 | ✗ 听起来对的那句话 | ✓ 实际上 |
|---|---|---|
| 1 | 零知识证明是一种加密技术 | 它是关于检查的技术,隐私是附赠的开关。最大的应用(zk-rollup)根本不用那个开关 |
| 2 | 验证者学不到东西,怎么可能被说服 | 他学到了「这句话是真的」,仅此而已。而他看到的记录,他自己就能造 |
| 3 | 抽查不够就多抽几次 | 力气来自错误的扩散率,不是抽的次数。而扩散率默认握在作弊者手里 |
| 4 | 哈希一下就算承诺了 | 哈希只给「改不了」。「藏得住」要靠足够的随机性——不加盐时 28.2 微秒就被反推 |
| 5 | 零知识要重复很多轮,所以慢 | 轮数多是挑战空间小的症状。挑战 2²⁵⁶ 种时,一轮就够 |
| 6 | 抽取器能套出秘密,这协议很危险 | 它需要「同一个押注上两次不同的回答」,真交互做不到。除非实现让 nonce 重复了 |
| 7 | 模拟器能造假记录,系统被攻破了 | 他能伪造历史,不能预测未来。说服力在顺序里,不在内容里 |
| 8 | 挑战是自己算的,多试几次就行 | 每试一次只得到一个均匀随机值。危险的是挑战空间太小的时候 |
| 9 | 我用的是审计过的知名库,没事 | Frozen Heart 命中的就是知名库。而且漏洞常在库和你代码的接缝上 |
| 10 | 电路就是程序的另一种写法 | 没有控制流,展开到最坏情况。成本 = 乘法门的个数,和运行时间无关 |
| 11 | 数据变长 64 倍,要检查的东西更多了 | 增加的是冗余不是信息。冗余买到的是「局部可检查性」 |
| 12 | 概率性验证不够严谨,我要 100% | 2⁻²³⁵ 比你的内存被宇宙射线翻位低六十多个数量级 |
| 13 | Merkle 树就是拿来做完整性校验的 | 真正的价值是局部打开:证明「这一小块属于那个整体」而不出示整体 |
| 14 | zk 让计算变快了 | 它让计算慢了几千倍。快的只有验证者,而且是拿证明者的钱换的 |
| 15 | 可信设置泄露了,我的隐私就没了 | 泄露的是可靠性——后果是凭空造币,不是被看光。而且不会被发现 |
| 16 | STARK 比 SNARK 新,所以更好 | STARK 是 SNARK 的一种。四个旋钮,没有一个方案全好 |
| 17 | 证明套证明会指数爆炸 | 每层电路固定大小,n 层是 O(n),树形合并是 O(log n) |
| 18 | zk-rollup 是隐私方案 | 它的交易数据必须公开,否则没人能重建状态。名字里的 zk 是叫顺了嘴 |
| 19 | 用了零知识,我就匿名了 | 匿名集 = 2^H。协议内泄露为零,而去匿名几乎全部发生在协议之外 |
| 20 | 零知识身份让我完全匿名 | 发证方知道、作废标记可关联、浏览器指纹唯一。它降级了泄露,没有消除 |
| 21 | 有了 zkVM,任何程序都能被证明了 | 「能」和「划算」隔着 10⁴–10⁶ 倍。而且硬件变快不改变这个倍数 |
| 22 | 用了零知识 + 做过审计 = 安全 | 证明只保证「有见证满足那堆约束」。规则是人写的,输入是人给的 |
| 23 | 读完这本书我就懂零知识证明了 | 你拿到的是一组判据和一张地图。实现细节(FRI 的可靠性、置换论证、曲线选择)都在书外 |
还不清的八笔账
这本书努力避免了「讲到魔法处就跳过」。但有八处,我只给了形状没给证明——而其中大部分,这个领域本身也还在往里填。
| # | 欠的是什么 | 状态 |
|---|---|---|
| 1 | 低度测试(FRI)的完整可靠性(第 11 章那个洞) | 协议本身是成熟的,但学界对它最紧的那个可靠性界仍在推进;一部分实用参数依赖猜想而非已证明的界。生产系统用不用猜想界,是一个真实的争论 |
| 2 | 随机预言机模型(第 8 章) | 被证明在理论上不严谨(存在只在该模型下安全的构造),但三十多年没出过因它本身而起的事故。所有非交互系统都站在它上面 |
| 3 | 算术友好哈希的成熟度(第 17 章) | MiMC / Poseidon 这一族不到十年,代数结构简洁——而这正是密码分析最喜欢的下手处。已有同类设计被迫调过参数 |
| 4 | 仪式参与者的独立性(第 15 章) | 「至少一人诚实」的数学是干净的,但「他们真的互相独立吗」是一个社会学问题,没法证明 |
| 5 | 电路是否表达了你的意图(第 22 章) | 形式化验证工具在进步,但覆盖面有限。这是今天事故率最高的一环 |
| 6 | 输入是谁担保的(第 22 章) | 没有技术解,只能把信任往外推一层 |
| 7 | 证明者成本(第 14、21 章) | 仍在 10⁴–10⁶ 倍。近年进步很快,但没有理由相信它会掉到 10² 以下 |
| 8 | 可验证外包还没落地(第 21 章) | 「云厂商真的跑了我要的那个模型吗」——第 1 章那张表里这一行,今天仍然是空的 |
把这八笔和自查表放在一起,就是这门技术今天真实的形状:中间那一段(数学)异常坚固,两头(规格与输入)异常脆弱,而成本仍然高得只有极少数场景付得起。
继续往下走
- Justin Thaler,《Proofs, Arguments, and Zero-Knowledge》。这是今天这个领域唯一一本把交互式证明、PCP、算术化、多项式承诺、SNARK 系统串成一条线的教材,作者长期免费公开。如果只读一样东西,读它。这本书里的每一章,在那本书里都有对应的严格版本。
- GMR 1985,《The Knowledge Complexity of Interactive Proof Systems》。零知识的原始论文,第 7 章那个模拟器就是它提出的。值得读的是它的动机部分——为什么会有人想到用「能不能被模拟」来定义「学到了什么」。
- PCP 定理(Arora–Safra、Arora 等,1998 年整理成型)。第 12 章末尾那句话的来源:任何证明都可以改写成只需随机读常数个比特就能验证的形式。这本书讲的全部东西,是那个定理三十年后的工程化。
- 原始论文,按需读:Groth16(2016)、PlonK(2019)、KZG(2010)、FRI 与 STARK(Ben-Sasson 等,2018)、Bulletproofs(2017)、Nova(2021)。读的顺序建议按第 16 章那张表,先读你要用的那个。
- 最短路径:Circom + snarkjs。写一个几十行的电路(比如「我知道一个数的哈希原像」),编译、生成证明、验证,一个下午能跑通。你会立刻看见「约束数」这个数字,第 10 章那套成本直觉会瞬间变具体。
- 更现代的写法:Noir、Halo2。前者语法更像普通语言,后者是 Zcash 那套,能看到真实生产系统的结构。
- 最省事的路径:RISC Zero 或 SP1。写一个普通的 Rust 函数,直接出证明——第 21 章那个抽象层。先跑通它,再回头看它替你挡掉了多少东西。
- 练手项目建议:(1) 把第 22 章那个数独电路完整写一遍,然后故意漏掉那 8 条约束,亲手造出那份含 12 的证明;(2) 实现第 11 章的低度扩展并测量码距;(3) 用第 13 章那棵 Merkle 树做一个「证明我在名单里」的最小可用身份验证。
- 公开审计报告。去读几份主流 zk 项目的审计报告,专挑 under-constrained 那一类看。你会发现它们的形状和第 22 章那个数独完全一样。
- Frozen Heart 的公开分析(2022)。第 9 章那件事的第一手材料,包括每个受影响项目具体漏掉了哪个参数。
- 以太坊基金会关于 L1-zkEVM 的公开路线图与讨论。它是今天能看到的、最完整的一份「把这套东西部署到几千亿美元系统上要考虑什么」的清单——包括那些不好看的部分。
- 《密语》(密码学)。这本书用到的哈希、椭圆曲线、签名,那本书从头讲起。如果你读到第 5 章时觉得椭圆曲线那部分想再深一点,去那边。反过来,那本书的最后一章用一节讲了零知识,这本书是那一节的展开。
- 《意外》(信息论)。第 19 章那个「匿名集 = 2^H」直接用了那本书的度量。「隐私的单位是比特」这句话,在那边有完整的框架。
- 《抹零》(浮点与数值稳定性)。第 10 章说「域里没有浮点误差」时,对照的正是那本书里的世界。两本书是同一个问题的两面:一个讲近似算术怎么积累误差,一个讲精确算术怎么换来别的代价。
- 《上链》(Web3 开发)。第 18、19 章那些应用的工程上下文在那本书里。
最后一次动手,把这本书的骨架用一段代码复述一遍。下面这四十行是全书的缩微版——它没有零知识、没有 Fiat–Shamir、没有 FRI,但它有那个核心动作:编码、承诺、抽查。
import hashlib, random
P, G = 65537, 3
# ntt / lde 见第 11 章,build / open_at / verify 见第 13 章
def prove(trace, blowup=64):
ext = lde(trace, len(trace) * blowup) # ① 编码:让一处作弊染遍全局
layers = build(ext) # ② 承诺:钉死整张表
return ext, layers, layers[-1][0]
def spot_check(ext, layers, root, k=3):
n = len(ext)
seed = int.from_bytes(hashlib.sha256(root).digest(), 'big') # ③ 挑战:Fiat–Shamir
rnd = random.Random(seed)
for _ in range(k): # ④ 抽查 + 打开
i = rnd.randrange(n)
if not verify(root, i, ext[i], open_at(layers, i)):
return False
return True
# --- 诚实证明者 ---
ext, layers, root = prove(trace)
print('诚实的表,抽 3 格:', spot_check(ext, layers, root))
# --- 作弊者:改一格再编码 ---
fake = trace[:]; fake[700] = (fake[700] + 1) % P
fext, flayers, froot = prove(fake)
same = sum(a == b for a, b in zip(ext, fext))
print('两张编码表相同的格子:%d / %d = %.4f%%' % (same, len(ext), 100*same/len(ext)))
print('作弊被抓住的概率:%.6f%%' % (100 * (1 - (same/len(ext)) ** 3)))
诚实的表,抽 3 格: True 两张编码表相同的格子:1023 / 65536 = 1.5610% 作弊被抓住的概率:99.999620%
把这四十行读三遍。这本书二十三章讲的,就是让这四行注释里的每一个词都站得住。
python3 spotcheck.py
在线跑:python.org/shell。把前面几章的 ntt、lde、build、open_at、verify 拼进同一个文件即可,一共一百来行、零依赖。
- 把那个动作带出去。这本书真正想留给你的不是 zk-SNARK,是遇到任何「所有人都要重做一遍」的结构时,会先问一句:这里能不能改成一个人做、其他人抽查?——CI 里每个人都重跑全量测试、每个服务各自校验同一份配置、每台机器各自重算同一个报表、每个审计员各自复核同一批凭证。
- 把那三个问题带出去。看到任何「可验证」的宣称,问:(1) 验证的是什么陈述?(2) 输入是谁担保的?(3) 一共有几条必须成立的假设?这三个问题在这本书之外一样好用。
- 把「隐私是个数」带出去。不要再问「这个东西匿名吗」,问「我藏在多少人里」。这一个改动会让你对绝大多数隐私产品的判断立刻变准。
- 把「事故不在密码学里」带出去。更一般的形式是:当系统里某一环被做到接近完美时,事故率不会归零,它只会整体搬到别的环上。这句话在这本书之外的适用面,可能比零知识本身还宽。
「读完这本书,我算是懂零知识证明了。」
你懂的是它的形状和一组判据,这已经比绝大多数关于这个话题的讨论走得远——但离「懂」还有距离,而且我想把这个距离说清楚,因为知道自己缺什么本身就是一种资产。
这本书跳过的东西:FRI 折叠的具体协议与它的可靠性论证(第 11 章那个洞我只画了形状);PlonK 的置换论证(怎么保证「同一根线在不同位置取值一致」);查找论证(lookup,今天用来处理位运算的主流手段);椭圆曲线配对的构造;各系统的具体参数选择。这些是从「读懂」到「能写」之间的全部内容。
但这本书没跳过的东西,恰恰是大部分材料会跳过的:抽查在原始形式下为什么没用(第 3 章)、编码那一步为什么不是密码学(第 11 章)、简洁的代价由谁付(第 14 章)、以及证明成立为什么不等于结论为真(第 22 章)。判据是:如果你现在能向别人解释「为什么改一格会变成错六万四千格」,而且能说出这一步跟加密毫无关系,那这本书的目的就达到了。
这一章的一句话
这门技术把「计算是否正确」这一段做到了近乎完美,于是整个系统的脆弱点全部被挤到了两头——规格是人写的,输入是人给的;而它留给你最有用的东西,不是任何一个密码学构造,是那个可以用在任何地方的问题:这里能不能改成一个人做、其他人抽查?
——全书完。
感谢你读完《抽查》。回到目录,或者去把第 11 章那四十行代码跑一遍——那个 1023 是你自己机器上数出来的,不是我说的。这本书从头到尾想让你习惯的,正是这件事。