0


密码协议形式化分析与可证明安全实验2——使用circom和snarkjs来创建一个零知识snark电路

操作系统: Windows11 X64

Circom 和 snarkjs 安装

概述

由JS和纯Web Assembly实现的zkSNARK,内部实现了Groth16、Plonk和FFLONK(Beta版)三个算法。

snarkjs继承了所有协议需要的组件,包括初始可信设置的NMPC执行过程(用以计算全局的的幂)和计算指定电路的证明。

snarkjs基于nodejs,计算采用circom编译的电路。

snarkjs采用ES模式,可以直接部署到rollup或webpack等项目中。

环境安装

由于circom是基于Rust开发的,因此主机需要有Rust环境。

snarkjs需要node环境。

rust环境安装

Rust是一种系统编程语言,以其安全性、并发性和性能闻名,可以通过官网链接进行下载。

msvc编译器

在Windows中使用rust需要有msvc编译器,本人由于之前安装Visual Studio时已经安装该编译器,故此处省略。

rust下载

在这里插入图片描述
打开压缩包后,会弹出cmd命令行窗口,等待其下载完成。出现以下内容即下载完成。
在这里插入图片描述

检查rust安装是否成功

安装程序会自动配置环境变量,在cmd命令行窗口中输入

rustc --version

,确认其安装成功,出现以下信息即安装成功:
在这里插入图片描述

node环境安装

简单来说 Node.js 就是运行在服务端的 JavaScript。Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。可以通过官网链接下载。

node下载

这里我选择LTS的windows下载包。
在这里插入图片描述
下载完成后,双击安装包开始安装(我是下载完了才写的博客,图时偷来的,大家按照自己的需求安装)
在这里插入图片描述
在这里插入图片描述
这里我的路径是

D:\LenovoSoftstore\NodeJS

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
中间会弹出“你要允许此应用对你的设备进行更改吗?”,选择“是”。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

检查node安装是否成功

接下来检查环境变量:右击此电脑->属性->高级系统设置->环境变量->系统变量->Path
在这里插入图片描述
然后在命令行窗口中输入

node -v

npm -v

查看node是否安装成功,出现以下信息即安装成功(这里我的node已经自动安装了npm,新版的Node.js已自带npm,安装Node.js时会一起安装,npm的作用就是对Node.js依赖的包进行管理,也可以理解为用来安装/卸载Node.js需要装的东西)。
在这里插入图片描述

node环境配置

这里的环境配置主要配置的是npm安装的全局模块所在的路径,以及缓存cache的路径,之所以要配置,是因为以后在执行类似:npm install express [-g] (后面的可选参数-g,g代表global全局安装的意思)的安装语句时,会将安装的模块安装到【C:\Users\用户名\AppData\Roaming\npm】路径中,占C盘空间。

但我希望将全模块所在路径和缓存路径放在我node.js安装的文件夹中,则在我安装的文件夹

D:\LenovoSoftstore\NodeJS

下创建两个文件夹

node_global

node_cache

如下图:
在这里插入图片描述
注意:这两个文件夹需要打开修改权限 很重要不然会报错没有权限

创建完两个空文件夹后,打开cmd命令行窗口,输入

npm config set prefix “D:\LenovoSoftstore\NodeJS\node_global”
npm config set cache “D:\LenovoSoftstore\NodeJS\node_cache”

再次点开环境变量,在系统变量下新建

NODE_PATH


输入

D:\LenovoSoftstore\NodeJS\node_global\node_modules

,将用户变量下的Path修改为

D:\LenovoSoftstore\NodeJS\node_global

不知道为什么我的电脑上这个环境变量配置没有了,就没有图。

还要将需要将npm全局包的目录添加到系统环境变量Path中,否则后面会报错血与泪的教训 。
在这里插入图片描述

测试node安装结果

在cmd命令行输入以下代码,安装两个常用的模块:

npm install express -g
npm install webpack -g

打开存放module的文件夹,发现这两个module已经被安装在该路径下(这里的截图我已经安装了其他module,所以有那么多,理论上只有webpack和express)。
在这里插入图片描述

Circom 安装

由于Circom安装语句中用到了

git

,需要在电脑上安装git需要使用的环境。

git环境安装

从官网链接下载git,选择适配电脑操作系统的安装包。
在这里插入图片描述
下载完成后,直接运行安装包,傻瓜式安装。

打开cmd验证器是否安装成功,输入

git --version

,如果如下图所示,返回了版本信息,就说明git安装成功了。
在这里插入图片描述

Circom安装

在命令行输入

git clone https://github.com/iden3/circom.git # 从该网站拷贝该资源
cargo build --release #进入进入circom目录后执行该命令
cargo install --path circom # 编译完成后执行安装命令,等待安装完毕即可

之后可以用以下命令查看circom版本:

circom --help

如下图所示,返回了版本信息,就说明circom安装成功了。
在这里插入图片描述

snarkjs安装

在命令行窗口中输入

npm install -g snarkjs@latest

安装snarkjs的module

安装完成后可以使用

snarkjs --help

查看帮助
在这里插入图片描述
至此,circom和snarkjs都安装完成。

构建电路与证明

准备工作——电路部分

首先准备好一个电路文件

circuit.circom

,具体如下:

pragma circom 2.1.4;# 指定编译该电路用到的circom版本为2.1.4
template Multiplier(){# 电路包含三个变量,其中a,b为输入,输出为c,该电路可以证明Prover知道两个数字a和b,其乘积为c
    signal input a;
    signal input b;
    signal output c;
    c <== a*b;# <== 符号有两个含义:将值与变量 c 相关联,然后是施加约束 a*b}
component main = Multiplier();# 将该电路声明为Multiplier模板,并用main组件将该模板实例化

将上述代码保存为

 .circom

文件,放在对应目录下

之后执行下列命令编译电路:

circom circuit.circom --r1cs --wasm --sym

在这里插入图片描述
这里的三个参数可以生成三个文件:

  • r1cs:生成基于R1CS约束系统的电路,得到一个.r1cs的二进制文件
  • wasm:生成一个circuit_js的目录(目录名取决于电路名),目录内包含一个wasm文件和其他用 于生成witness的相关文件
  • sym:生成一个.sym的符号文件,用于调试和打印约束系统

编译电路后,可以用snarkjs的相关命令来查看电路,这里用ri参数可以查看电路信息(或者用r1cs info也可以)

snarkjs ri circuit.r1cs

可以看到下列输出:
在这里插入图片描述
输出表示电路采用的曲线为bn-128,包含4个导线,其中2个私有输入,一个输出,输入输出之间包含一
个约束关系

或者使用rp参数查看电路中的约束(或者用print r1cs也可以)

snarkjs rp circuit.r1cs

得到下列输出:
在这里插入图片描述
这里忽略掉前面的系数,得到的约束就是

     a 
    
   
     ∗ 
    
   
     b 
    
   
     − 
    
   
     c 
    
   
     = 
    
   
     0 
    
   
  
    a*b-c=0 
   
  
a∗b−c=0

到此为止,电路就编译好了,接下来需要给电路特定的输入,也就是构建witness,这里就需要用到前面
生成的wasm文件

snarkjs采用json文件的方式将witness输入电路,需要准备一个

input.json 

文件,如下:

{"a":"3","b":"11"}#注意这里的输入均为字符串,因为JS无法处理大于2^53的整数,所以转换为字符串形式处理

将这个json文件放到刚刚生成的

circuit_js

下面,此时目录里面应该有四个文件,分别是之前的wasm文
件和三个json文件
在这里插入图片描述
然后在该目录下执行下列命令:

node generate_witness.js circuit.wasm input.json witness.wtns

这样就得到了

 witness.wtns

的证据文件,之后将利用这个文件来生成对应的snark证明

准备工作 ——可信设置部分

证明生成需要两个文件,一个是前面构造电路时生成的r1cs文件

 circuit.r1cs

,另一个是证据文件

witness.wtns

接下来以Groth16为例来生成证明

Groth16需要为每个电路都执行一次可信设置,因此在证明生成之前还需要针对电路完成一些准备工
作,Plonk和FFlonk无需为每个电路都执行可信设置

Groth16的可信设置由两部分组成:

  •                                     τ                                  \tau                     τ的幂次:这一部分与电路无关
    
  • phase 2:这一部分取决于电路

首先来生成

     τ 
    
   
  
    \tau 
   
  
τ的幂次,命令如下:
snarkjs powersoftau new bn128 12 pot12_0000.ptau -v

利用

new

命令创建

     τ 
    
   
  
    \tau 
   
  
τ的幂次,这里有几个参数说明一下:
  • 第一个参数为指定需要的曲线,snarkjs支持bn128和bls12-381两种曲线
  • 第二个参数为约束参数,表示可信设置所支持的最大约束的数量,这里的12表示可信设置最多支持 2 12 = 4096 2^{12}=4096 212=4096个约束,这个参数的最大值为28,也即snarkjs可以生成具有至多 2 28 ≈ 2.68 ∗ 1 0 8 2^{28}\approx2.68*10^8 228≈2.68∗108个约束的电路在这里插入图片描述在这里插入图片描述

生成之后会在目录下得到一个

pot12_0000.ptau

文件

接下来需要用

contribute

命令,使用新的贡献来创建一个ptau文件:

snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="Firstcontribution"-v

这里会提示输入一些随机文本来为贡献提供额外的熵源,这里随便输就行,比如输入snowolf
在这里插入图片描述

contribute

命令会将截止至现在的所有ptau文件作为输入(截止至目前为止的ptau文件为上面新建的

pot12_0000.ptau

),同时输出一个新的ptau文件

pot12_0001.ptau

,该文件中包含新贡献者执行的计算

这个命令还有一个name参数,可以输入任何内容(可以理解为执行contribute命令的一些注释),在后续验证ptau文件时这些信息会作为辅助内容输出

如果不想在contribute命令执行过程中输入文本(懒狗行为),可以使用

-e

参数来讲命令变为非交互式的,就可以不用输入相关信息了

比如在第一次contribute的基础上利用

-e

参数再执行一次contribute:

snarkjs powersoftau contribute pot12_0001.ptau pot12_0002.ptau --name="Secondcontribution"-v -e="some random text"

在这里插入图片描述
更新完成之后,用

verify

命令验证截止至目前为止的ptau(只执行了一次更新,所以验证0001这个ptau文件)

snarkjs powersoftau verify pot12_0001.ptau

在这里插入图片描述
这里只是生成

     τ 
    
   
  
    \tau 
   
  
τ的幂,还没有执行phase 2的命令,因此此时运行后系统会输出如下提示

在这里插入图片描述
不过验证通过了,就没什么问题

这里注意一点,每当新的zk-snark需要执行可信设置时,只需要对最新的ptau文件执行verify命令,即可验证截止到目前为止的整个挑战和相应链

接下来是利用beacon命令创建一个ptau文件,这个ptau文件会以随机beacon的形式对ptau文件完成贡献,可信设置的第一阶段需要对其应用一个随机beacon

引入beacon的命令如下

snarkjs powersoftau beacon pot12_0001.ptau pot12_beacon.ptau 1cbf6603d6ff9ba4e1d15d0fd83be3a80bca470b6a43a7f9055204e860298f99 10-n="FinalBeacon"

在这里插入图片描述
这里利用了以太坊第16668892个区块的Hash值作为beacon值,后面的参数10表示以该beacon值作为输入,计算

      2 
     
    
      10 
     
    
   
  
    2^{10} 
   
  
210次Hash

接下来是可信设置的第二部分,也即phase 2

phase 2用到的命令为 prepare phase2 ,该命令会用到前面输出的

pot12_beacon.ptau

文件,并基于该文件计算基于

     τ 
    
   
     , 
    
   
     α 
    
   
     ∗ 
    
   
     β 
    
   
     , 
    
   
     β 
    
   
     ∗ 
    
   
     τ 
    
   
  
    \tau,\alpha*\beta,\beta*\tau 
   
  
τ,α∗β,β∗τ的拉氏插值多项式,命令如下:
snarkjs powersoftau prepare phase2 pot12_beacon.ptau pot12_final.ptau -v

输入命令之后,电脑会算一阵子,计算的时间取决于曲线、第一阶段中允许的约束数量(约束数量越大,计算时间越长)
在这里插入图片描述
生成完最终ptau文件后,别忘了用

verify

命令验证一下

snarkjs powersoftau verify pot12_final.ptau

在这里插入图片描述

密钥生成

准备完毕之后,接下来是根据电路构建证明与验证密钥

首先需要用电路和上面的最终ptau文件生成一个zkey文件,该文件是一个零知识密钥文件,包含phase 2的所有贡献,以及用于证明和验证的密钥,利用这个zkey文件可以验证其是否属于特定的电路

snarkjs groth16 setup circuit.r1cs pot12_final.ptau circuit_0000.zkey

在这里插入图片描述
这里得到的

circuit_0000.zkey

文件不包含任何贡献,不能用于最终电路的证明,因此需要执行前面的贡献更新步骤

接下来使用

zkey

命令来对zkey文件进行贡献更新(前面用的是 powersoftau 命令,基本流程是一样的)

snarkjs zkey contribute circuit_0000.zkey circuit_0001.zkey --name="First Contributor Name"-v #这里依然输入snowolf
snarkjs zkey contribute circuit_0001.zkey circuit_0002.zkey --name="Second contribution Name"-v -e="Another random entropy"
snarkjs zkey verify circuit.r1cs pot12_final.ptau circuit_0002.zkey # 更新完贡献记得验证一下

在这里插入图片描述
在这里插入图片描述
然后引入随机beacon值并验证

snarkjs zkey beacon circuit_0002.zkey circuit_final.zkey 1cbf6603d6ff9ba4e1d15d0fd83be3a80bca470b6a43a7f9055204e860298f99 10-n="FinalBeacon phase2"
snarkjs zkey verify circuit.r1cs pot12_final.ptau circuit_final.zkey #同样引入beacon后验证一下

在这里插入图片描述
之后基于该zkey文件,导出一个验证密钥,导出的密钥为json格式

snarkjs zkey export verificationkey circuit_final.zkey verification_key.json

在这里插入图片描述

证明生成

接下来利用电路和证明密钥来构建证明

snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json

这里需要注意,证据文件wtns是在circuit_js目录下面的,要么指定目录,要么把这个文件移出来
命令执行完毕后,可以得到两个json文件,一个是

public.json

,也就是statement
在这里插入图片描述
我们前面给的witness分别是3和11,因此

     3 
    
   
     ∗ 
    
   
     11 
    
   
     = 
    
   
     33 
    
   
  
    3*11=33 
   
  
3∗11=33正确

另一个是

proof.json

,里面包含用于验证的元素
在这里插入图片描述

验证

利用上述两个json文件可以验证证明

snarkjs groth16 verify verification_key.json public.json proof.json 1

在这里插入图片描述

模拟执行

最后是将验证密钥导出为Solidity智能合约,就可以将这个合约发布到链上了

snarkjs zkey export solidityverifier circuit_final.zkey verifier.sol

在这里插入图片描述

snarkjs给了一个模拟验证执行的命令,利用上面的两个json文件可以模拟Verifier在remix中的验证过程

snarkjs zkey export soliditycalldata public.json proof.json

在这里插入图片描述
输出是三个群元素和公共输入,均编码为HEX形式

这里Verifier会调用verifyProof函数来对上述这堆东西进行验证,这里可以把上面的那个智能合约导入到remix来模拟一下,这里给一个网址:http://remix.ethereum.org/
在这里插入图片描述

进去之后,先把刚刚导出的sol文件上传,然后点击左边第三个,编译我们刚刚上传的智能合约,等待编译完成
在这里插入图片描述
之后点左侧第四个部署并运行智能合约,这里选择Verifier的合约,然后下面有一个已部署的智能合约,把上面得到的那一串数字粘贴到verifyProof里面,点一下call来调用智能合约
在这里插入图片描述
右侧主界面可以看到正在调用,如果验证通过的话decode output这里会输出true
在这里插入图片描述

引用参考

https://blog.csdn.net/ziqibit/article/details/129699607?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170416194616800192262481%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=170416194616800192262481&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allbaidu_landing_v2~default-4-129699607-null-null.142v99control&utm_term=rust%E7%8E%AF%E5%A2%83%E5%AE%89%E8%A3%85&spm=1018.2226.3001.4187
https://blog.csdn.net/yll_358918552/article/details/134495283?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170416194616800192262481%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=170416194616800192262481&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allbaidu_landing_v2~default-6-134495283-null-null.142v99control&utm_term=rust%E7%8E%AF%E5%A2%83%E5%AE%89%E8%A3%85&spm=1018.2226.3001.4187
https://blog.csdn.net/SunBigBoy/article/details/129621613?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170417546116800186551455%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=170417546116800186551455&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-1-129621613-null-null.142v99control&utm_term=node%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE&spm=1018.2226.3001.4187
https://blog.csdn.net/caohongxing/article/details/122763833?ops_request_misc=&request_id=&biz_id=102&utm_term=windows%E8%83%BD%E7%94%A8git&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-122763833.142v99control&spm=1018.2226.3001.4187


本文转载自: https://blog.csdn.net/Tereya/article/details/135460988
版权归原作者 Tereya 所有, 如有侵权,请联系我们删除。

“密码协议形式化分析与可证明安全实验2——使用circom和snarkjs来创建一个零知识snark电路”的评论:

还没有评论