6 个版本 (3 个破坏性)
0.5.0 | 2023 年 7 月 28 日 |
---|---|
0.4.0 | 2023 年 6 月 4 日 |
0.3.0 | 2023 年 4 月 1 日 |
0.2.2 | 2023 年 3 月 22 日 |
#10 in #zk-snark
1MB
1.5K SLoC
包含 (Mach-o exe, 240KB) examples/toy/pasta/toy_cpp/toy, (Mach-o exe, 170KB) examples/toy/bn254/toy_cpp/main.o, (Mach-o exe, 240KB) examples/toy/bn254/toy_cpp/toy, (Mach-o exe, 170KB) examples/toy/pasta/toy_cpp/main.o, (Mach-o exe, 37KB) examples/toy/bn254/toy_cpp/fr_asm.o, (Mach-o exe, 37KB) examples/toy/pasta/toy_cpp/fr_asm.o 以及更多.
新斯科舍
将 Circom 电路编译成 Nova 验证器的中间件
此仓库提供了必要的中间件,可以将 Circom 编译器的输出(R1CS 约束和生成的见证)用于 Nova 作为验证器。
为什么?
由于 Nova 是递归 SNARK 的前沿技术,Circom 是 ZK 开发工具的前沿技术,因此进行这样的操作是非常有意义的。由于 Nova 使用 ~R1CS 算术化,这主要只是将 Circom 输出解析成 Nova 可以使用的东西的问题。
正如 Justin Drake 所说,我认为正确看待 Nova 的方式是将它视为具有大量重复结构的 zkSNARK 预处理器 — Nova 可以将检查 N 个实例的问题的成本(以 R1CS 约束的数量衡量)缩小到 ~一个实例的同一个问题。这是干净而神奇的做法,非常适合这样一个世界:我们使用 Nova 的输出,然后在“真实”的 zkSNARK(如 PLONK/groth16/Spartan)中进行验证,以获得一个真正完全最小化的证明(其大小甚至小于一个实例)。值得注意的是,这种模式已在类似 zkEVMs 的设置中使用,但使用的是 STARK 证明而不是 Nova 证明。我认为,与 STARK 相比,Nova(特别是折叠方案之类的特定方案)更适合我们想要的预处理层属性:快速压缩、最小化加密假设和低递归开销。[^1]
Nova Scotia 包含了丰富的 示例,以及一个 浏览器使用示例。我们将在下文中更详细地描述验证/验证的工作流程。
[^1]: 但目前,Nova/R1CS 缺乏 STARKS 的可定制性(特别是自定义门和查找表),因此在这一点上存在权衡。
如何实现?
要自己使用,首先按照 Circom 文档 中描述的方式安装 Circom。
在 Circom 中编写 Nova Step 电路
要在 Circom 中编写 Nova Scotia 电路,我们在递归的一步抽象上操作。我们编写一个电路,该电路接受一个公共输入列表(这些必须在 Nova-Scotia 接口中命名为 step_in
)并输出相同数量的公共输出(命名为 step_out
)。然后,这些公共输出将被路由到递归的下一步作为 step_in
,这个过程将持续到我们到达递归迭代的末尾。在步骤电路中,除了公共输入外,Circom 电路还可以输入额外的私有输入(使用 Circom 可以接受的任何名称/JSON 结构)。在我们的 Rust 适配器中,我们将对这些私有输入的管道进行测量。
准备好了,使用以下命令编译您的电路: circom [文件].circom --r1cs --sym --c --prime vesta
用于 vesta 曲线。在相应文件夹中运行 make
命令来编译 C++ 证人生成器。或者,您可以使用 circom [文件].circom --r1cs --sym --wasm --prime vesta
来编译 WASM 证人生成器。我们稍后会使用 R1CS 文件和证人生成器二进制文件(C++ 二进制文件或 WASM),请注意它们的文件路径。您可以通过运行证人生成来独立测试这些步骤电路,如 Circom 文档 中所述。
由于 Nova 在椭圆曲线的周期上运行,您必须通过特性和 Circom 编译命令指定曲线。目前,Nova Scotia 支持由 Nova 上游在 provider 和 Circom 的 --prime
标志支持的任何周期。您可以在示例目录中查看 Pasta (pallas/vesta) 和 bn254/grumpkin 曲线的示例电路。
Nova Scotia 的 Rust 适配器
开始一个新的 Rust 项目,并将 Nova Scotia 添加到您的依赖项中。然后,您可以使用 Nova 开始使用您的 Circom 步骤电路。首先定义 Circom 输出路径和加载 R1CS 文件。
// The cycle of curves we use, can be any cycle supported by Nova
type G1 = pasta_curves::pallas::Point;
type G2 = pasta_curves::vesta::Point;
let circuit_file = root.join("examples/bitcoin/circom/bitcoin_benchmark.r1cs");
let witness_generator_file =
root.join("examples/bitcoin/circom/bitcoin_benchmark_cpp/bitcoin_benchmark");
let r1cs = load_r1cs::<G1, G2>(&circuit_file); // loads R1CS file into memory
Circom支持使用C++和WASM生成证人,因此您可以通过传递witness_generator_file
来选择使用哪一种,无论是生成的C++二进制文件还是Circom的WASM输出(circuit.wasm
文件)。如果您使用WASM,我们假设您已经在您的系统上安装了兼容版本的node
。注意,对于本地证明,我们建议使用C++证人生成器以获得性能(除非在M1/M2 Mac上,因为不支持)。对于浏览器内的证明/验证,您必须使用WASM证人生成器。我们将在README中的后续部分描述浏览器内的证明和验证工作流程。
然后,使用create_public_params
函数创建公共参数(CRS)
let pp = create_public_params::<G1, G2>(r1cs.clone());
现在,在递归的每一步构建Circom证人生成器的输入。这是您Circom输入的JSON输入的HashMap表示。例如,在bitcoin示例中,private_inputs
是一个HashMap
列表,其中每个HashMap包含递归步骤验证的区块头和区块哈希,而公共输入step_in
是链中的上一个区块哈希。
为了实例化这个递归,我们使用Nova Scotia的create_recursive_circuit
let recursive_snark = create_recursive_circuit(
FileLocation::PathBuf(witness_generator_file),
r1cs,
private_inputs,
start_public_input.to_vec(),
&pp,
).unwrap();
验证是通过Nova定义的verify
函数完成的,该函数还接受Nova Scotia将初始化为[F<G2>::zero()]
的二级输入,因此只需传递即可
println!("Verifying a RecursiveSNARK...");
let start = Instant::now();
let res = recursive_snark.verify(
&pp,
iteration_count,
&start_public_input.clone(),
&[F<G2>::zero()],
);
println!(
"RecursiveSNARK::verify: {:?}, took {:?}",
res,
start.elapsed()
);
let verifier_time = start.elapsed();
assert!(res.is_ok());
有关正确示例和更多详细信息,请参阅下面的toy.rs
和bitcoin.rs
示例
toy.rs
toy.rs是一个非常简单的玩具步骤电路,用于测试目的。最好先看看它的Circom代码和Nova中实例化它的Rust代码。它是一个简单的斐波那契变种,并在每一步添加一个私有输入。
bitcoin.rs
bitcoin.rs是一个更复杂的示例,使用Nova创建比特币链证明的证明者。Nova可以以仅一个区块证明工作的成本压缩整个比特币链的验证。由于这个构造(因为它在约150k约束中运行散列和其他位操作以验证每个区块),Circom电路更复杂。这也对基准测试很有用,因为您可以在递归步骤中验证的区块数量上进行操作。以下是一些简单的基准测试,用于120个区块的证明和验证的不同递归配置
递归步骤数量 | 每步验证的区块数量 | 证明时间 | 验证时间(未压缩) |
---|---|---|---|
120 | 1 | 55.38秒 | 214.43毫秒 |
60 | 2 | 49.05秒 | 434.96毫秒 |
40 | 3 | 42.08秒 | 509.03毫秒 |
30 | 4 | 45.40秒 | 923.23毫秒 |
24 | 5 | 48.43秒 | 991.89毫秒 |
请注意,验证时间是线性增长的,而证明时间随着递归步骤的减少而减少。在实践中,您将使用Nova的输出作为另一个SNARK方案(如Plonk/groth16)的输入,以获得完整的简洁性。
此外,这些数据是在我的(不太出色)笔记本电脑上测量的,所以你应期待在更强大的机器上获得更好的性能,尤其是在Nova支持底层的GPU加速MSMs进行证明的情况下。
浏览器内的证明和验证
Nova Scotia还支持在浏览器中证明和验证证明,以及证明和公共参数的序列化。我们在browser-test
文件夹中提供了一个使用Rust编译为WASM的浏览器内证明示例。文件夹中的test-client
是一个Create React App,展示了浏览器内的证明和验证。如果你对此类用法感兴趣,请查看文件夹以了解它们是如何工作的。查看halo2 WASM编译指南可能也很有用。
对有兴趣的贡献者的说明
如果你有兴趣贡献,请查看GitHub问题。如果你有任何问题,可以直接在Telegram上联系我 @nibnalin。
致谢
感谢Srinath Setty/Microsoft Research的原始Nova实现和论文,以及iden3团队的Circom语言。
解析和生成功能大量借鉴了其他类似仓库,如plonkit、ark-circom、zkutil等。
我从未去过诺瓦斯科夏。这个仓库被命名为Nova Scotia,因为加密领域已经有了Tornado Cash Nova和Arbitrum Nova(除了微软的Nova),所以是时候开始给这个术语添加后缀,以最大化围绕它的混淆。
页面顶部的艺术作品由Tadashi Moriyama创作,所有荣誉都归他所有。我只是它的粉丝。:)
依赖项
~19–29MB
~454K SLoC