7.2 ZK 虚拟机:把通用计算搬进证明 本节摘要:为每个业务手写电路不可持续,通用路线是证明一台虚拟机的执行——ZK-EVM 是其中最著名的形态。本节讲清兼容性与证明成本的梯度、字节码级与语言级两条路线的取舍,以及哈希、存储访问两大瓶颈的工程解法。承接 7.1 的研究主脉,通往 7.3 的抗量子迁移。 从手写电路到证明一台机器 电路开发的现实门槛在 6 章现场已经反复出现:谓词清单要按 4.4 的成本表精打细算,每改一行业务逻辑都要重走一遍约束设计与审计。通用路线的反问是:既然所有程序最终都跑在某种指令集上,为什么不证明指令解释器本身执行正确?
本节摘要:为每个业务手写电路不可持续,通用路线是证明一台虚拟机的执行——ZK-EVM 是其中最著名的形态。本节讲清兼容性与证明成本的梯度、字节码级与语言级两条路线的取舍,以及哈希、存储访问两大瓶颈的工程解法。承接 7.1 的研究主脉,通往 7.3 的抗量子迁移。
电路开发的现实门槛在 6 章现场已经反复出现:谓词清单要按 4.4 的成本表精打细算,每改一行业务逻辑都要重走一遍约束设计与审计。通用路线的反问是:既然所有程序最终都跑在某种指令集上,为什么不证明指令解释器本身执行正确?把虚拟机的每一步状态转移编码成约束(AIR 方言天然适配这种逐行轨迹),任何跑在这台虚拟机上的程序就自动获得了证明能力——开发者写普通代码,证明系统证明"这段代码在这些输入下的执行结果是如此如此"。
ZK-EVM 就是这个思路在以太坊虚拟机上的落地:证明"这批交易在给定状态下按 EVM 规则执行,得到新状态"。它直接收割以太坊既有生态——工具、钱包、合约一字不改。通用 ZK 虚拟机(基于 RISC-V 等通用指令集)则更广撒网:任意语言的任意程序都能进证明。
"兼容"是分级的。完全对齐主链的字节码与共识细节(包括哈希函数、区块结构),证明成本最高;放宽到工具链兼容、或进一步放宽到语言级兼容(用自己的虚拟机替换 EVM,只保证源码语言相通),证明成本逐级下降。行业惯用类型编号来标定位置:
| 类型 | 兼容程度 | 证明成本 | 典型取舍 |
|---|---|---|---|
| 一型:完全等价 | 字节码与共识全对齐 | 最高 | 想接主链全部细节,证明器压力大 |
| 二型:字节码等价 | 字节码级一致,结构微调 | 高 | 生态兼容与成本的主流折中 |
| 三型:近等价 | 个别预编译需改造 | 中高 | 迁移期形态 |
| 四型:语言级 | 高级语言编译到自定义指令集 | 较低 | 放弃字节码兼容换证明速度 |
这张表没有最优行,只有业务定位:金融类应用要主链级安全对齐,一型二型;新应用不在乎存量生态,四型的证明速度红利真金白银。
通用证明的成本大头集中在两处。哈希:虚拟机与以太生态大量使用 keccak,而它在算术电路里是出了名的贵——解法组合拳是 7.1 的查找论证(把 keccak 的查表性质直证)加上在虚拟机内部改用对电路友好的哈希(如 Poseidon 类),对外接口再转回 keccak。存储访问:虚拟机随时要读写状态,而证明系统偏爱线性顺序计算——解法是把随机访问改造成"对可验证数据结构的访问证明":状态组织成一棵承诺树,每次读写附一份成员性与更新正确性的证明,访问模式由此变得可抽查。跑一个微型演示,感受"随机访问改造成承诺访问"的成本差异:
# 随机访问的两种证明成本对比(约束数示意模型) def naive_random_access(trace_len, accesses): # 朴素思路:为每次访问验证"内存槽内容来自正确时刻" # 电路里等价于对整条轨迹做逐槽比对:成本 = 访问数 × 轨迹长 return accesses * trace_len def commitment_access(trace_len, accesses, log_fanout=4): # 承诺思路:状态存进高度为 log_fanout 的承诺树 # 每次访问只需验证一条树路径:成本 = 访问数 × 树高 × 每层常数 import math height = max(1, math.ceil(math.log(max(trace_len, 2), log_fanout))) return accesses * height * 3 # 每层约三条约束做哈希与方向校验 for trace, acc in [(4096, 64), (65536, 512)]: naive = naive_random_access(trace, acc) commit = commitment_access(trace, acc) print(f"轨迹 {trace:>6} 访问 {acc:>4}:朴素 {naive:>10,} vs 承诺 {commit:>6} 条约束" f"({naive/commit:>7.1f} 倍差距)") # 典型输出: # 轨迹 4096 访问 64:朴素 262,144 vs 承诺 1152 条约束( 227.6 倍差距) # 轨迹 65536 访问 512:朴素 33,554,432 vs 承诺 12288 条约束( 2730.7 倍差距)
数量级差距解释了为什么 ZK 虚拟机的架构设计围绕承诺数据结构展开,也解释了为什么虚拟机内部的"指令集选择"(比如内存模型与字宽)本质都是证明成本问题。
把一台生产级 ZK 虚拟机竖着切开,通常能看到四层流水线,各层独立生成证明、再由聚合层收拢:
分层的价值与 7.1 的折叠主脉直接相连:各层用最适合的方言(状态层爱承诺树、CPU 层爱 AIR、专用层爱查找),聚合层把异构证明收成一份——分层不是为了好看,是为了让每一层都能独立并行、独立优化、独立审计。
给工程团队的三条判断:业务已有主链存量生态且强依赖,选字节码级兼容的类型,忍受证明成本;业务新建且证明速度是生命线,选语言级或通用虚拟机,把生态成本自己扛;无论哪种,先验证目标工具链的证明器吞吐与审计记录两项硬指标——虚拟机的架构漂亮不等于证明器跑得快,跑得快不等于被审过。7.4 节的工具链全景会给出具体的评估清单。
**问:虚拟机证明会不会永远比专用电路慢?**相对会,差距在收敛。指令解释层的开销是常数倍的(一条业务指令对应若干条解释器约束),随着指令集裁剪、查找论证与专用芯片的叠加,常数倍在持续缩小。工程判断标准很简单:业务迭代速度带来的收益若超过证明成本差,通用方案就是对的;对成本极度敏感且逻辑稳定的场景,专用电路仍占优。
**问:既有合约迁移到 ZK 虚拟机要改代码吗?**按兼容档位定。字节码级兼容方案理论上零改动,工程中仍会遇到细微语义差(个别操作码的边界行为、gas 计价的偏差),需要回归测试全量覆盖;语言级方案要求重编译加适配,存量生态越深,迁移账单越长。评估时把"语义差的已知清单"当作方案的诚信指标——不敢公开差异列表的方案,差异只会出现在生产环境。
**问:证明器吞吐怎么压测才算数?**用真实交易的分布而非玩具用例:把主网或自有业务的交易直方图采样进测试集,压出"单位时间可证明的批量规模"与"尾部交易(大内存、深递归)的处理时延"两条曲线。吞吐数据只有在尾部场景下才暴露真实水位——营销数字通常只报均值。