本文作者回顾了自己2014年创建的Proof Market,并探讨了使用zkVM构建非托管版本,以及使用计算机语言来编写和检查数学证明来验证算术电路的正确性。文章对比分析了多个用于验证算术电路的框架,包括ACL2、Garden、zk-lean、sp1-lean和Clean,并评估了它们在不同方面的优缺点,以及作者和Claude Code在这些框架上的使用体验。
2014 年,我运营着一个名为 Proof Market 的网站,你可以在上面悬赏比特币来证明数学命题,当你在 Rocq 中证明了这些命题,就可以获得比特币。我想构建一个非托管版本的 Proof Market。为此,zkVM 非常有用。我更希望证明检查 zkVM 能够被形式化验证。我正在验证算术电路,因为我想要一个定理证明人员和定理证明 AI 的市场,这样我就可以轻松地卸载算术电路的验证工作!
当你将一个问题(Proof Market 中需要证明的东西,或者通常情况下需要通过的一些验证)编码为一个求解多项式方程的难题时,密码学家会给你一些简洁的方式来证明这个难题有解(简洁论证)。
简洁论证的接收者将能够检查,在给定一些密码学假设的情况下,以非常高的概率,多项式难题存在一个解。然后接收者也会想确保多项式难题的解对应于原始问题的解(可靠性)。此外,对于简洁论证的发送者来说,原始问题的任何解都可以编码为多项式难题的解(完整性)通常至关重要。
当人们编写多项式难题(算术电路)时,他们通常在脑海中对可靠性和完整性有数学证明。数学证明可以通过白板和讨论来传递,但是(i)这需要发送者和接收者双方付出大量的努力,以及(ii)每个人在检查 40 个案例中的 5 个后都会感到厌烦。他们会习惯一些重复的模式并失去注意力(尽管我有一个假设,即可以通过向理论计算机科学家展示涉及 40 个案例的证明来区分他们和数学家)。有时人们会错过 40 个案例中的第 6 个案例中的一个小错误,这很重要,因为攻击者可以研究该案例以找到实现中的漏洞。
有一些计算机语言可以用来编写和检查数学证明,它们也可以用来推断硬件和软件系统。对于算术电路,软件是用多项式编写的,所以我们又回到了数学。人们已经使用这些语言来检查算术电路的可靠性和完整性。让我比较一下这些语言。
注意
对于密码学家:我在这篇博文中忽略了知识论证。下面提到的任何项目似乎都没有说明证明者可以以多项式时间访问解决方案。这完全是关于解决方案的存在性。
我正在开发 Clean,这是一个以某种模块化方式证明算术电路正确性的框架。Clean 是用 Lean Language(Lean FRO 的 TM)编写的。
我在工作中使用了 Rocq 多年。对于早期时代的 智能合约形式化验证,我使用了 Isabelle。我不时地使用 ACL2 做一些小项目。我曾经与开发这些工具的人交谈过,但我不属于任何工具开发的内部圈子。
这是结果的总结。请注意,级别 0 的示例仅关于公开可用的示例。这些项目有不同的目标。其中两个,acl2-jolt 和 sp1-lean,旨在解决现有 zkVM 实现的棘手方面。其他项目旨在成为通用框架。有些尝试从其他语言导入电路并验证它们。有些旨在成为前端。所有项目都包含可以重用的组件。
| 框架 | 语言 | 示例级别 | 可重现 | Claude Code:IsZero | Claude Code:加权和 |
|---|---|---|---|---|---|
| ACL2 (r1cs) | ACL2 | 级别 2? | 是(3 小时) | 是 | 是 |
| ACL2 (PFCS) | ACL2 | 级别 1 | 是(没有额外时间) | 是(一次性) | 是(冗长) |
| acl2-jolt | ACL2 | 级别 2 | 部分(仅限 Ubuntu) | 不适用 | 不适用 |
| Garden | Rocq | 级别 2 | 是(20 分钟) | 最终可以 | 是(需要干预) |
| zk-lean | Lean | 级别 1 | 是(<10 分钟) | 否 | 不适用 |
| sp1-lean | Lean | 级别 2 | 部分 | 不适用 | 不适用 |
| Clean | Lean | 级别 2 | 不适用(作者) | 不适用(存在) | 否 |
由 ChatGPT 生成的图像
人们使用 ACL2 来验证算术电路。一个例子是 ACL2 书籍中的 页面。该框架位于 ACL2 书籍 中,一些应用似乎是专有的。
R1CS 是一种流行的多项式方程格式。ACL2 (r1cs) 库似乎也可以处理其他类型的多项式方程。
sbcl。我只是做了 brew install sbcl 来安装 sbcl。cd books; make ACL2=/<snip>/acl2-sources/saved_acl2 -j 8 kestrel/ethereum/semaphore/top.cert。make ACL2=/<snip>/acl2-sources/saved_acl2 -j 8 kestrel/ethereum算术电路的形式化是直接且易于理解的。有一个方程列表,其中明确写出了左侧和右侧。
(define make-rel-iszero ()
:returns (def definitionp)
(make-definition
:name 'rel-iszero
:para '(x out)
:body (list
;; Constraint 1: x * x_inv = 1 - out
(make-constraint-equal
:left (expression-mul
(expression-var 'x)
(expression-var 'x-inv))
:right (expression-sub (expression-const 1)
(expression-var 'out)))
;; Constraint 2: out * x = 0
(make-constraint-equal
:left (expression-mul
(expression-var 'out)
(expression-var 'x))
:right (expression-const 0)))))
ACL2 不需要以通常的方式编写策略。你可以给证明器提示,而这只是关于命名相关的引理。这可能对人类以及 LLM 更容易。
ACL2 的安装需要一些反复试验,尽管这已经是我的第三次了。我试图使用安装指南中提到的特定 gcl 版本,但 ./configure 脚本不起作用等等。一些在线文档页面返回 403 错误。与 Lean 安装(安装 VSCode 插件并按 "ok" 几次)相比,这是一个真正的障碍。
在处理 ACL2 (r1cs)(如 ACL2 书籍)方面,Claude Code 比我更厉害。我相信使用 Claude Code 声明和证明正确性定理的速度比我单独完成快 100 倍。
rel-select)PFCS 是一种合理地打包算术电路的方法。
该框架值得更多关注。它缺乏公开可用的高级示例。
有些数据类型可以编码为域元素的向量,允许将这些作为参数可能是一个有趣的方向。
不清楚是我更好还是 Claude Code 更擅长处理 ACL2 (PFCS)。当 Claude Code 即将放弃时,我更好地把握了情况,但我坚持继续。
acl2-jolt使用 ACL2 的另一个项目是在 GitHub 上提供的 acl2-jolt。
acl2-jolt)的地方该项目正在验证一个真实的实现。部分代码库有很好的文档记录。代码库非常统一,并且已证明的语句简短易懂。
acl2-jolt)的剩余期望验证在每个指令处停止。程序执行的概念尚未形式化。大多数用户关心程序的执行,因此电路表示程序执行非常重要。
代码库中存在一些令人困惑的方面。一些子表由 257x257 维度制成,带有 (create-tuple-indices (expt 2 8) (expt 2 8))。其他子表由 256x256 维度制成,带有 (create-tuple-indices (1- (expt 2 8)) (1- (expt 2 8)))。我几乎可以肯定这无关紧要,但类似的事情可能很重要。
Claude Code 在处理 acl2-jolt 方面比我更厉害。我正在用 256x256 表替换 257x257 表,并且 Claude Code 在我理解为什么它们被破坏之前就修复了证明。
Garden 是一个用于验证用 Rocq 编写的算术电路的框架。
A /\ B /\ C,并且它抱怨说 "这令人难以置信地沮丧。" 我很遗憾听到这个。UnOp.inv 的公理(因为逆运算没有定义,只是假设存在,参见 M.v)。UnOp.inv _ 小于 p。mod,比如 H_out: (1 mod p - ((x * (self.inv mod p)) mod p) mod p) mod p = 0coqide 工作,但 VsRocq 不工作,因为它以某种方式没有根据唯一的 _CoqProject 找出 Require Import。这限制了 Claude Code 的 IDE 访问选项。它可以验证用流行的 DSL 编写的电路。
仅可解决的问题:
IsZero 电路看起来像这样
Definition constrain {p} `{Prime p} (arg_fun_0 : IsZero.t) (arg_fun_1 : Felt.t) : M.t unit :=
let var_out : Felt.t := arg_fun_0.(IsZero.out) in
let var_inv : Felt.t := arg_fun_0.(IsZero.inv) in
let var_felt_const_1 : Felt.t := UnOp.from 1 in
let var_felt_const_0 : Felt.t := UnOp.from 0 in
(* Constraint 1: out = 1 - x * inv *)
let var_prod : Felt.t := BinOp.mul arg_fun_1 var_inv in
let var_out_expr : Felt.t := BinOp.sub var_felt_const_1 var_prod in
let* _ : unit := M.AssertEqual var_out var_out_expr in
(* Constraint 2: x * out = 0 *)
let var_prod2 : Felt.t := BinOp.mul arg_fun_1 var_out in
let* _ : unit := M.AssertEqual var_prod2 var_felt_const_0 in
M.Pure tt.
我比 Claude Code 更擅长处理 Garden 代码。
zk-lean 是一个用于验证用 Lean 编写的算术电路的框架。
现在说还为时过早。代码库结合了一些非常基本的例子和一些复杂的机制。我需要看到复杂的机制在工作才能说些什么。
我想会提供更多有趣的例子。Jolt 仓库有 一个导出一些 Lean 定义的功能 用于 zk-lean,因此可能会有一些基于此的努力。
对于我和 Claude Code,关于处理 zk-lean 代码,打成平手。两者都未能理解如何推断语法。我放弃的点是当我看到 FreeM.bind 的定义是 protected 时。我猜我不应该展开定义(展开是用它的定义替换 FreeM.bind 的行为),但除此之外呢?
Succinct 宣布了 Sp1 Hypercube 的形式验证。这是一个值得注意的成就。
iszero 指令并实现它并验证它,但这将不是一个公平的比较。公告 非常透明地说明了团队做了什么以及他们没有做什么。我特别欣赏 "假设和注意事项" 部分。透明度水平是一个值得效仿的例子。
该努力旨在验证所有相关的指令。
如果你熟悉 RISC-V zkVM(将 VM 执行编码为算术电路的方式),那么定义和语句很容易理解。
我感到相当多的定理证明功夫被有针对性地应用来完成这项工作。该 repo 包含很酷的 Lean 技术(使用高级属性和巧妙调整的自动策略调用),使证明脚本的大小保持在,比如,我所期望的朴素方法的 1/5 以下。
公告 没有指定证明工件的 git 提交。公告时最新的提交 不包含 LB 正确性的完整证明。先前的提交 包含关于 LB 指令的定理。
缺少完整性。例如,correct_add 说 "如果约束成立,则会发生一些事情"。我找不到另一个定理说 "如果发生了某些事情,则可以满足约束"。
关于定义,有一些棘手的地方。缓解措施将涉及约束开发框架中的一些设计变更:
Main[6] 和 Main[14] 是 AddChip 的输入,Main[21] 是输出。ByteOperation 相关表)和通用 Air 约束约定的结果是混合的。MemoryAccessInShardTimestamp 仅包含低位,这对于什么目的来说是安全的,等等,这些都不是 repo 中验证的一部分。这些证明看起来很容易受到约束数量等变化的影响。也许编码代理能够更新这类事情。
Clean 是一个以模块化方式证明算术电路正确性的框架,用 Lean 编写。
整个开发都在 Lean 中,所以不会在翻译中丢失任何东西。
Clean 提供了以可组合的方式验证子电路的方法。
将所有内容视为场变量无限磁带上的谓词,这在概念上很简单。
电路语法可以作为前端语言传递(在我看来)。这是 Clean 项目中的一个 IsZeroField.lean(在这个实验之前编写):
def main (x : Var field F) : Circuit F (Var field F) := do
-- When x ≠ 0, we need x_inv such that x * x_inv = 1
-- When x = 0, x_inv can be anything (we use 0)
let xInv ← witness fun env =>
if x.eval env = 0 then 0 else (x.eval env : F)⁻¹
let isZero <== 1 - x*xInv -- If x is zero, isZero is one
isZero * x === 0 -- If x is not zero, isZero is zero
return isZero
必须记住不同类型的电路:Circuit、ElaboratedCircuit、Subcircuit、FormalCircuit、FormalAssertion 和 FormalGeneralCircuit,这很麻烦。
当我使用子电路时,我在目标状态中看到 (something_complicated).output,而不是子电路输出的单个变量。这通常无关紧要,因为我有子电路的规范告诉我关于 (something_complicated).output 的信息。既然我永远不需要查看复杂术语的结构,我可以只看到一个变量,而不是这个。
我比 Claude Code 更擅长使用 Clean。
Coda 和 risc0-lean4 有一些用于算术电路的 Lean 代码。上次更新是在几年前,所以我没有在比较中包括这些项目。
这个领域会发生什么?在短期内,将出现更多框架。有人会想到多项式电路就像 effectful lenses/relation-oriented programming/representation theory。在中期,除了手工制作的优化电路,人们将停止自己编写电路。大多数多项式电路将自动生成。一些形式验证技术将以越来越少的人工干预来使用。评估验证工作的结果仍然是一个挑战。
zkSecurity 为包括零知识证明、MPC、FHE 和共识协议在内的加密系统提供审计、研究和开发服务。
- 原文链接: blog.zksecurity.xyz/post...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!