一、SnarkJS相关基础
github: https://github.com/iden3/snarkjs
iden3: https://iden3.io/
什么是SnarkJS
snarkjs是zkSARNK方案的 javascript 实现库,支持原始的8points协议和 Groth协议。利用snarkjs你可以进行可信设置、生成证据并验证证据。
snarkjs使用iden3自己的circom编译器对DSL定义的电路进行编译。
像iden3的SnarkJS这样的新工具首次启用了浏览器内高效的ZK验证和验证。
1. 什么是circom
circom下载地址:https://github.com/iden3/circom
circom是iden3设计的zk-snark电路描述DSL,circom npm包含了使用该DSL描述的电路的编译器。
2. 零知识证明的基本概念
【强烈推荐】zk-snark新手入门教程【circom/snarkjs】
参考URL: https://zhuanlan.zhihu.com/p/143519030
1、算术电路:零知识证明核心
零知识程序和其他程序的实现不太一样。首先,你要解决的问题需要先转化成多项式,再进一步转化成电路。
例如,多项式x³ + x +5 可以表示成如下的电路:
sym_1 = x * x // sym_1 = x²
sym_2 = sym_1 * x // sym_2 = x³
y = sym_2 + x // y = x³ + x
~out = y + 5
**Circom编译器将逻辑转换为电路。通常我们不需要自己设计基础电路。**如果你需要一个哈希函数或签名函数,可以在circomlib (https://github.com/iden3/circomlib) 找到。
2、证据的生成与验证:零知识证明的流程
在运行零知识证明程序之前,我们需要创建一个可信的设置,这需要 一个电路以及一些随机数。一旦设置完成就会生成一个证明密钥和一个验证密钥,分别用于生成证据和执行验证。
一旦创建了证明/验证密钥对,就可以生成证据了。
有两种类型的输入:公开输入和私有输入。例如,A向B转账但是不希望公开账户余额,那么A的账户余额就是私有输入,也被称为见证(Witness)。公开输入可以是A和B的地址或者转账金额,这完全取决于你的具体设计。
接下来证明人就可以利用证明密钥、公开输入和见证来生成证据:
最后一步是验证。验证方使用公开输入、证据和验证密钥来验证证据。
公开输入、见证(私有输入)、证明密钥、验证密钥、电路、证据这些基本概念以及相互之间的关系,就是我们继续下面的教程之前需要理解的零知识证明的基本概念。
3、Circom基本概念:算术电路语言
首先我们先了解下Circom的语法。Circom的语法类似javascript和C,提供一些基本的数据类型和操作,例如for、while、>>、array等。
让我们看一个具体的实例。
假设x、y是保密的(即witness),我们不想暴露x和y的具体值,但是希望证明 (x * y) + z == out,其中z,out是公开输入。我们假设out = 30, z = 10, 那么显然 (x*y) = 20,但是这不会暴露x和y的具体值。
circom提供了如下这些关键字用于描述算术电路:
- signal:信号变量,要转换为电路的变量,可以是private或public
- template:模板,用于函数定义,就像Solidity中的function或golang中的func
- component:组件变量,可以把组件变量想象成对象,而信号变量是对象的公共成员
Circom也提供了一些操作符用于操作信号变量:
- <==, ==>:这两个操作符用于连接信号变量,同时定义约束
- ←, →:这些操作符为信号变量赋值,但不会生成约束条件
- ===:这个操作符用来定义约束
二、如何使用snarkJS
https://github.com/iden3/snarkjs
1. 准备工作
安装 node v14
首先,请确保您已安装最新版本的Node.js。 尽管v12之后的任何版本都可以正常运行,但我们建议您安装v14或更高版本。
如果不确定安装的是哪个版本的Node,可以运行:
node -v
node 下载地址:https://nodejs.org/zh-cn/download/
安装snarkjs and circom
安装circom and snarkjs, 运行:
npm install -g circom@latest
npm install -g snarkjs@latest
snarkjs help命令
要查看所有snarkjs命令的列表以及它们的输入和输出说明,请运行:
snarkjs --help
您还可以在特定命令中使用–help选项:
snarkjs groth16 prove --help
您还可以在特定命令中使用–help选项:
snarkjs groth16 prove --help
大多数命令都有一个替代的较短别名(您可以使用–help来发现)。
例如,也可以使用以下命令调用前一个命令:
snarkjs g16p --help
2. 官方demo:用circom和snarkjs实现零知识证明应用的全流程
官方文档:https://docs.circom.io/1.-an-introduction/getting-started
官方参考:https://github.com/iden3/circom/blob/master/TUTORIAL.md
参考: https://cloud.tencent.com/developer/article/1638822
经过验证https://docs.circom.io/1.-an-introduction/getting-started 似乎比github 更新,所以推荐 参考 https://docs.circom.io/1.-an-introduction/getting-started
让我们创建一个电路,去证明你能够因式分解一个数字!
创建一个电路目录
创建一个 factor 目录,教程里的所有文件都将放在这个下面
mkdir factor
cd factor
在真实的电路中,您可能需要创建一个 git 仓库,其中包含circuits目录和一个包含所有测试的test目录,以及用于构建所有电路的脚本。
1.使用下面的内容创建一个 circuit.circom 文件:
template Multiplier() {
signal private input a;
signal private input b;
signal output c;
c <== a*b;
}
component main = Multiplier();
这个电路有2个 private 输入信号,名为 a 和 b ,还有一个输出 c.
电路做的事情是让强制信号 c 为 a*b 的值。
在声明 Multiplier 模板之后, 我们使用名为main的组件实例化它。
注意:编译电路时,必须始终有一个名为main的组件。
编译电路
现在,我们准备编译电路。运行以下命令:
circom circuit.circom --r1cs --wasm --sym
将电路编译为名为circuit.rlcs的文件
-
r1cs选项将生成circuit.r1cs(电路的r1cs约束系统,采用二进制格式)。
-
wasm选项将生成circuit.wasm(用于生成见证的wasm代码)。
-
sym选项将生成circuit.sym(调试或如果要以注释模式打印约束系统所需的符号文件)。
现在电路已经编译好了,我们将继续使用snarkjs。
我们随时可以通过输入snarkjs --help 来访问snarkjs的帮助
查看电路有关的信息
要显示电路的信息,可以运行:
root@VM-0-2-ubuntu:~/factor# snarkjs info -r circuit.r1cs
[INFO] snarkJS: Curve: bn-128
[INFO] snarkJS: # of Wires: 4
[INFO] snarkJS: # of Constraints: 1
[INFO] snarkJS: # of Private Inputs: 2
[INFO] snarkJS: # of Public Inputs: 0
[INFO] snarkJS: # of Labels: 4
[INFO] snarkJS: # of Outputs: 1
root@VM-0-2-ubuntu:~/factor#
打印约束(Print the constraints)
root@VM-0-2-ubuntu:~/factor# snarkjs r1cs print circuit.r1cs circuit.sym
[INFO] snarkJS: [ 21888242871839275222246405745257275088548364400416034343698204186575808495616main.a ] * [ main.b ] - [ 21888242871839275222246405745257275088548364400416034343698204186575808495616main.c ] = 0
我的第一个零知识证明demo
Now that the circuit is compiled, we will use snarkJS to generate and validate zk-SNARK proofs. More specifically, we will prove that we are able to factor the number 33. That is, we will show that we know two integers a and b such that when we multiply them, it results in 33.
现在,电路已编译完毕,我们将使用snarkJS生成并验证zk-SNARK证明。 更具体地说,我们将证明我们能够分解数字33。也就是说,我们将证明我们知道两个整数a和b,以便将它们相乘时得出33。