7月23日,隐私学院【PrivacyIN】第一期ZK训练营第四课——《ZK Hands-on》如期开讲。本期课堂由资深隐私开发工程师Kelvin Wong讲授,主要介绍Circom电路开发和ZK应用实践。
背景
随着零知识证明(ZKP)技术理论和工程应用的进步,ZK-Dapp已经成为区块链技术的应用热点,但ZKP技术的过高的技术门槛,使得设计和开发一个基于区块链系统的完整ZK-Dapp依然有较高的难度。因此PrivacyIN特别开设实践课程,从理论到实践,帮助开发者快速进入ZK应用开发。
主要课程内容
本期课堂Kelvin Wong主要围绕Circom电路设计和零知识证明应用开发进行展开。
ZK应用介绍
Kelvin老师首先介绍了当前ZK应用发展概况,带领学员了解当前主流的ZK应用和发展方向,将ZK应用主要分为:L2 ZK-Rollup应用、ZK-VM & ZK-EVM、ZK区块链系统、流行的ZK开发框架及编译器、其他热门电路及应用等,并进行分类的介绍和特点分析。
ZK-Rollup主要使用zkSNARKs技术将批量交易线下聚合,再将聚合交易及证明记录到智能合约,借助zkSANRKs其简洁、可验证、零知识性的特点,非常优雅地解决了以太坊等交易处理效率低和手续费昂贵的问题。目前ZK-Rollup涌现了非常多热点应用,包括降低转账手续费的STARKNET、zkSync、LOOPRING等,应用类ZigZog、Curve、zkNFT等。
ZK-VM是基于ZKP来保证程序执行状态转换的安全性和可验证性的可信通用虚拟机,一般ZK-VM都提供类似电路描述语言来实现计算应用。
EVM是运行在以太坊上,用来执行智能合约的虚拟机器,EVM是基于栈架构的,主要组件包括:Stack、Memory、PC、Storage等,EVM使用Gas进行指令计价和约束执行。
ZK-EVM是以ZKP方式执行智能合约或EVM指令的虚拟机,一般可以按照兼容性分为合约兼容ZK-EVM、EVM指令级别ZK-EVM、EVM标准规范级别ZK-EVM。ZK-EVM赋予了EVM执行正确性证明和可验证的特点,主要是用来构建兼容以太坊智能合约的ZK-Rollup方案或区块链系统。
基于ZK的区块链系统,最著名的是ZCash,它是第一个基于zkSNARK技术构建的提供匿名交易的电子货币实现。随着ZK理论和技术的发展,越来越多区块链系统基于ZK技术进行构建,以提供更好的匿名性和安全性。比如monero门罗币也将使用ZK技术构建,其将使用BulletProof协议来实现匿名交易;Mina使用递归零知识证明技术来构建简洁和轻量的区块链系统;Filecoin借助递归零知识证明技术来实现数据存储证明;Aleo则基于ZK构建通用、私密、高效公链系统。
在开放性区块链世界,ZK公链和应用的发展离不开开源社区,开源社区为ZK技术的发展提供了坚实的基础,以Arkworks、Matters Labs、Polygon等为代表社区或公司提供丰富的密码学开发基础库、ZK协议实现、编译器等。比较流行有开发库有:libnark、ZK-Garage/plonk、snarkjs、halo2等;经典的编译器及开发库套件,包括:Circom+Snarkjs、Aleo、ZoKrates、Cairo等。
以电路应用开发的视角来看,有很多热门的应用方向,包括:ZK-ML机器学习、ZK-DID、ZK-NFT、ZK-DAO外包计算等。按照电路用途分类,可以分为:通用性电路库(Circomlib)、专用性电路库(Semphore成员关系证明)、zkEVM-Circuits(EVM构建基础电路)等。
ZK应用开发工作流
一个完整的ZK应用开发是一个完整的软件工程用例构建,本次课堂不按照完整的软件工程工作流来展示应用开发过程,而是将电路逻辑来表示业务逻辑,围绕构建电路电路实现可验证计算的能力。zkSNARKs协议由于其简洁、非交互、高效性的特点,ZK主流应用基本上都使用zkSNARKs构建,课堂中将主要介绍基于zkSNARKs(主要为Plonk、Groth16协议)的应用开发。
zkSNARs应用开发的主要步骤,主要包括:
1.电路构建&编译
主要将计算业务使用电路语言描述,然后编译器翻译为R1CS或Plonkish等风格约束
2.Setup
公共参数、电路参数生成,包括创建prover/verifier各自的keys
3.创建证明proof
证明者(prover)使用自己私有输入witness和Setup得到keys,构建关于电路的证明。
4.验证电路proof
验证者(verifier)使用prover生成的proof,验证“prover使用witness作为输入并正确执行了电路”。
目前比较流行的集成开发套件有Aleo、ZoKrates、Circom+Snarkjs等,基本上采取类似的工作流程,本次课堂则选取“Circom & Snarkjs”进行详细的电路应用开发介绍。
Circom & Snarkjs工作流程
Circom & Snarkjs主要的优点在于有比较丰富的电路开发模板库,Snarkjs功能非常强大,支持Web和验证合约生成,对主流协议Groth16和Plonk支持比较完备。
Circom & Snarkjs的工作流程如下:
1.算数电路设计(Circom语言)
将计算业务转化为统一的算术表达式,然后将算术表达式用Circom语言描述实现。
2.编译电路
将Circom语言表示的电路翻译成低级别的R1CS形式电路(可以生产wasm以及调试信息),其实用circom编译器。
参考命令:circom circuit.circom –r1cs –wasm –sym
3.Snarkjs生成witness文件
参考命令:snarkjs calculatewitness –wasm circuit.wasm –input input.json –witness witness.json
4.可信设置 & 证明生成
可信设置使用MPC接力仪式生成,包括:universal-trusted-setup和电路相关的trusted-setup。
universal-trusted-setup主要为生成通用参数,适用于所有电路,主要命令参考:snarkjs powersoftau new/contribute (仪式参数初始化/接力)
电路相关的trusted-setup,生成适用于指定电路的ZKP计算参数,主要命令参考:
snarkjs groth16/plonk setup (创建proving key和verification key)
snarkjs zkey powersoftau new/contribute (指定电路参数初始化/生成接力)
证明(proof)生成基于可信设置生成的通用参数、指定电路参数以及witness,生成指定电路的计算的证明proof,其主要命令参考:snarkjs plonk/groth16 prove
5.验证证明 & 验证智能合约生成
使用电路和witness生成的proof可以用来进行正确性验证(validate),也可以生成适用于以太坊的验证智能合约(generate verifier)。
参考命令:snarkjs validate/generateverifier (验证/生成验证合约)
Circom电路设计
Circom是一个表示R1CS电路约束的DSL语言,同时也是一个针对该语言的编译器,它是一个强类型检测和静态分析语言,目前主要支持算术电路。Circom通过定义显式电路约束实现电路逻辑,用户设计电路则类似于编写电路信号(线)的赋值和定义约束,然后Circom编译器将这些编写的电路翻译成R1CS约束(也包括WASM、C++等相关语言),提供给snarkjs使用,并借助snarkjs实现完整的ZKP计算流程。
Circom的语法比较简单,主要为支持有限域(finite-field)的电路运算,提供了template(有点类似C++)来定义子电路模块component,提供了function用来定义非电路函数代码块等。
一个完整的Circom电路包括:预编译指令、电路模板实现、main定义,如下定义了一个简单的乘法电路(注:参考官方文档):
预编译,主要指定编译器兼容版本;电路模板实现,主要实现电路逻辑,包括定义输入输出信号(signal),中间信号,构建约束等;main定义,定义入口main组件,其使用电路模板构建的实例。
Circom表示的算术电路定义包括:私有输入、共有输入、共有输出、电路门和线约束,这些元素工作在有限域Fp上,通过构建R1CS的格式的约束实现电路。
Circom的Signal
signal类似电路的wire(或变量),主要分为input(输入)signal、output(输出)signal、默认signal(中间变量),这些signal工作在有限域Fp上 (mod p)。
signal默认为private,可以在main{}中设置input signal为public,output signal总是为public,不能设置为private,默认定义的signal为private。
Circom的表达式
Circom支持常量表达式(只有常量操作或赋值)、线性表达式(只有常量和信号的乘法和加法)、二元表达式,其中常量表达式、线性表达式是二元表达式的特殊形式。二元表达式形式类似于”AB-C”其中A、B、C为线性表达式,比如合法的二元表达式:2x + 3y + 2) * (x+y) + 6x + y – 2。
Circom的Template
Template用来创建通用电路的模板,其他电路Template或main可以通过创建模板实例来使用电路,提供类似模块化的能力,即一个大电路可以由很多个小的电路实例集成构建得到。目前Circom社区提供了很多优质的电路模板,比如circomlib、Semphore等,可以基于这些电路模板构建复杂的电路。
Circom的Constraint
Circom使用constraint定义R1CS电路约束,constraint操作符一般配合二元表达式使用,主要的constraint操作符有:等于约束 (=),赋值等于 (>或或<–)。
Cricom一般推荐使用赋值等于(>或<)进行赋值和约束同时定义,灵活的场景下则将两者分开,比如:除法操作转化为赋值和乘法约束, A/B=C则使用“C<– A/B; C*B===A”表示。
Circom范例学习
课程中以实现简单多项式零知识证明为例,给定一个单变量多项式poly3(x)=1 + 2x + 3x^2 + 4x^3,演示如何进行设计Circom电路实现poly3多项式。
poly3多项式不是二元表达式(最高次数为3),将问题进行拆解,定义平方x2电路模板、x3电路模板分析多项式,然后进行线性组合即实现了poly3多项式。
实现步骤:
1.创建电路文件,添加预处理定义,指定编译器版本;
2.定义平方电路、立方电路模板;平方电路实现,其为一个乘法电路,输入信号相乘,只需添加复制等于约束。
立方电路实现,使用平方电路实例,再乘以输入信号即可,即x3=x*(x2)。
3.定义poly3电路实现模板,其通过创建平方电路和立方电路实例,线性组合实现;
4.定义main,即创建poly3电路实例。
将poly3多项式实现的电路保存为polynominal.circom文件,执行编译命令: circom polynominal.circom –inspect –wasm –c,将输出变量和约束信息;使用snarkjs命令行工具也能够查看编译输出的R1CS信息,如执行命令:snarkjs r1cs info polynominal.r1cs,将可以查看电路线数目、约束个数、私有输入变量、公共输入变量、公共输出变量等信息。
完整ZK- Dapp范例流程演示
给定一个多变量函数,包含3个变量x、y、z,其中x为0或1,定义如下:
如何实现一个完整的零知识证明可验证智能合约?
准备:
● 安装Circom和Snarkjs
● 下载或生成通用的ZK参数
可以从Polygen-Hermez下载已经完成的MPC仪式生成参数。
可以使用Snarkjs进行MPC接力仪式生产ZK参数,如命令:snarkjs contribute等。
开发实现:
1.实现电路
1.1. 将函数转化统一的算术表达式,if分支语句合并
为了简化计算,限制x为0或1,方便分支语句的合并。
1.2.电路拍平flatten,引入新的变量(信号signal),并使用R1CS表示
1.3. R1CS表示的电路转化为circom语言(直接翻译),保存为f.circom文件
2.编译电路编译输出r1cs二进制,snarkjs命令查看电路信息
3.Setup和创建证明&验证keys
4.创建witness和proof
5.生成验证合约verifier和调用数据
6.部署到remix并调用例如,可以将上一步骤生成的verifier合约部署到remix以太坊模拟环境,使用上一步骤的calldata作为输入,调用call接口:
如果输出true则表示计算proof验证成功。
可以看到基于Circom和Snarkjs能够实现一个完整的ZK应用,该开发套件灵活、可编程,提供多协议支持、丰富的基础电路、多语言的支持如C++、JavaScript、Solidity等,尤其Solidity验证合约的生成能力,则进一步增加了Web3-ZK应用开发的友好度。
自由讨论环节,Kelvin老师耐心地为学员解答了一系列电路设计技巧和开发相关的问题。