#zk-snarks #proofs #cryptography #proof

nova-scotia

将 Circom 电路编译成 Nova zkSNARK 方案的中间件

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

Rust 1K SLoC // 0.0% comments JavaScript 552 SLoC // 0.1% comments

包含 (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 验证器的中间件

Original from Tadashi Moriyama

此仓库提供了必要的中间件,可以将 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 的可定制性(特别是自定义门和查找表),因此在这一点上存在权衡。

如何实现?

Nova Scotia

要自己使用,首先按照 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.rsbitcoin.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编译指南可能也很有用。

image

对有兴趣的贡献者的说明

如果你有兴趣贡献,请查看GitHub问题。如果你有任何问题,可以直接在Telegram上联系我 @nibnalin。

致谢

感谢Srinath Setty/Microsoft Research的原始Nova实现和论文,以及iden3团队的Circom语言

解析和生成功能大量借鉴了其他类似仓库,如plonkitark-circomzkutil等。

我从未去过诺瓦斯科夏。这个仓库被命名为Nova Scotia,因为加密领域已经有了Tornado Cash NovaArbitrum Nova(除了微软的Nova),所以是时候开始给这个术语添加后缀,以最大化围绕它的混淆。

页面顶部的艺术作品由Tadashi Moriyama创作,所有荣誉都归他所有。我只是它的粉丝。:)

依赖项

~19–29MB
~454K SLoC