zkLLVM电路编译器介绍编写电路很难。如果从零开始,那就是难上加难。在尝试了不同的方法之后,我们认为zkLLVM是简化过程,并使之高效的关键。编写电路时的主要问题之一
编写电路很难。编写高效电路更难。如果从零开始,那就是难上加难。最近,我们一直在编写电路,并且是同时在Mina和以太坊之间以及Solana和以太坊之间构建zkbridge。在尝试了不同的方法之后,我们认为zkLLVM是简化过程,并使之高效的关键。 编写电路时的主要问题之一是,要么需要深入了解特定证明系统的工作原理,要么需要了解自定义领域特定语言,这就限制了当代码一旦被实现后的可重用性——自定义DSL(又名Cairo、Noir、Circom等)需要生成整个库和应用程序的生态系统。此外,特定领域的语言通常构建在zkVM之上,这也是导致性能问题的主要原因之一。 无论何时,只要涉及到任何类型的zkVM,就都不可能达到合适的效率。 但如果我们不需要产生一个全新的生态系统来实现一些电路会怎样呢?如果我们能够在不牺牲效率的情况下证明主流语言呢?
zkLLVM是一个基于LLVM的电路编译器,能够证明LLVM支持的任何主流开发语言:C++、Rust、JavaScript/TypeScript 和其他语言。 关键在于它是一个电路编译器,而不是虚拟机。它将高级代码作为输入,将其转换为电路,然后用于证明计算。 电路只是程序的另一种形式 在我们继续往下解释之前,需要先记住电路是由什么组成的。每一个PLONK电路基本上有两个部分:
由于所有数据样本的约束系统都是相同的,而执行跟踪在不同样本之间是不同的,我们就可以预先计算约束系统,然后用它来证明不同数据样本上的计算。 这正是zkLLVM所做的。这个编译器不是像传统的基于 zk 虚拟机的项目那样执行代码,而是将其编译成电路的形式,然后用于证明不同数据样本上的计算。 它快速,便宜,高效。只需将代码转换为电路,并将其交给证明生产者。不再需要自定义虚拟机。 让我们仔细看看它是如何工作的。
以前,它要求开发人员学习一种新的开发语言,深入了解目标协议计算(状态证明、数据聚合、分析等)的代码和/或特定语言的特殊性,并承担目标虚拟机每次更新所带来的无休止的维护成本。
下面是以前的例子:
总的来说,虽然zkVM的概念很有前途,但是与使用虚拟机相关的开销可能会是其一大瓶颈,必须加以解决,才能使其成为实践中的有效解决方案。
一旦我们将虚拟机从管道中移除,我们就可以获得更好的性能和效率。以下是对新管道的简短描述:
zkLLVM是一个独立的工具,它也可作为clang和rustc的替代品,因此我们可以将其用作管道的一部分。它扩展了我们最喜欢的编译器和语言,能够将我们的代码编译成电路。
但这还不是全部。使用zkLLVM甚至不需要设置一个 prover。如果想要证明的算法太大,不能在自己的机器上运行,那我们可以在 https://proof.market 上发布订单,并使用整个社区的算力。
证明是一个简单的JSON文件,可以在任何地方使用。我们可以在自己的应用程序中使用它,也可以在 https://proof.market 上发布它并将其出售给社区。
如果想在去中心化平台上使用zk-proof,显然我们需要以某种方式验证它。我们已经提前考虑了这个问题,并准备了一组可以使用的验证者。它包括:
最后,我们可以从源代码构建一个验证者,并将其集成到我们自己的应用程序或在本地运行它。
C++ SDK和RUST SDK (WIP)包含了很多有用的东西,可以用来构建我们自己的应用程序:
安装工具链:
配置项目(在使用C++管道的情况下使用CMake)
$ cmake -DCMAKE_BUILD_TYPE=Release -DCIRCUIT_ASSEMBLY_OUTPUT=TRUE ..
如常编译电路代码:
$ make circuit_examples -j$(nproc) $ make circuit_examples -j$(nproc)
因此,带有 SDK 的完整管道看起来像下图:
让我们来看看代码是如何逐步转换成电路的。同时,我们将学习一些关于编写电路的基本知识。
每个可证明的计算电路都以入口点函数开始,用[[circuit]]属性标记。该函数接受一些参数并返回结果。函数体代表一个算法,该算法将被编译成一个电路,进一步用于生成证明。
让我们以一些简单的算术逻辑代码为例,详细介绍它的编译过程:
zkLLVM编译C++代码:
#include <nil/crypto3/algebra/curves/pallas.hpp>
using namespace nil::crypto3::algebra::curves;
[[circuit]] typename pallas::base_field_type::value_type hello_world_example(
typename pallas::base_field_type::value_type a,
typename pallas::base_field_type::value_type b) {
typename pallas::base_field_type::value_type c = (a + b)*a + b*(a-b)*(a+b);
return c;
}
此 C++ 将转换为以下中间电路表示:
define dso_local __zkllvm_field_pallas_base @_Z19hello_world_exampleu26__zkllvm_field_pallas_baseu26__zkllvm_field_pallas_base(__zkllvm_field_pallas_base %0, __zkllvm_field_pallas_base %1) local_unnamed_addr #5 {
%3 = add __zkllvm_field_pallas_base %0, %1
%4 = mul __zkllvm_field_pallas_base %3, %0
%5 = sub __zkllvm_field_pallas_base %0, %1
%6 = mul __zkllvm_field_pallas_base %1, %5
%7 = add __zkllvm_field_pallas_base %0, %1
%8 = mul __zkllvm_field_pallas_base %6, %7
%9 = add __zkllvm_field_pallas_base %4, %8
ret __zkllvm_field_pallas_base %9
}
稍后将转换为以下 PLONK约束:
constraint0: (W0 + W1 - W3 = 0)
constraint1: (W3 * W0 - W4 = 0)
constraint3: (W0 - W1 - W5 = 0)
constraint4: (W1 * W5 - W6 = 0)
constraint5: (W0 + W1 - W7 = 0)
constraint6: (W6 * W7 - W8 = 0)
constraint7: (W4 + W8 - W9 = 0)
gate0: ({0}, constraint0, constraint1, constraint2, constraint3, constraint4, constraint5, constraint6, constraint7)
把它放到 https://proof.market 上并获得证明。
circuit_name: hello_world_example,
constraints_amount: 8,
gates_amount: 1,
rows_amount: 1,
publc_inputs_amount: 2,
proof:
我们可以在 Ethereum/StarkNet/Solanet/任何地方进行验证。
$verified: true
Source:https://blog.nil.foundation/2023/02/02/circuit-compiler.html
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!