libsnark源代码,建议想深入零知识证明的小伙伴都读一读。Bellman库主要围绕Groth16算法,libsnark给出了SNARK相关算法的全貌,各种Relation,Language,Proof System。为了更好的生成R1CS电路,libsnark抽象出protoboard和gadget,方便开发者快速搭建电路。
本文中使用的libsnark源代码的最后一个commit如下:
commit 477c9dfd07b280e42369f82f89c08416319e24ae
Author: Madars Virza <madars@mit.edu>
Date: Tue Jun 18 18:43:12 2019 -0400
Document that we also implement the Groth16 proof system.
1. 源代码目录
源代码在libsnark目录下:
common - 定义和实现了一些通用的数据结构,例如默克尔树,稀疏向量等等。
relations - relation描述了“约束”关系。除了我们通常说的R1CS外,还有很多其他约束的描述语言。
reductions - 各种不同描述语言之间的转化。
knowledge_commit - 在multiexp的基础上,引入pair的概念,两个基点一个系数,计算结果称为一个pair。
zk_proof_systems - 零知识证明中的各种证明系统(包括Groth16,GM17等等)。
gadgetlib1/gadgetlib2 - 为了更方便的构建R1CS,libsnark抽象出一层gadget。已有的gadget,可以方便地整合搭建出新的电路。
2. Relation
需要零知识证明的问题都是NP问题。NP问题中有一类问题NPC(NP-complete)问题。所有的NP问题都可以转化为一个NPC问题。只要有一个NPC问题能多项式时间内解决,所有的NP问题都能多项式时间内解决。描述一个NPC问题,有多种方式。描述NPC问题的方式,称为“language”。Relation指的是一个NPC问题和该问题的解的关系。
libsnark库总结了几种描述语言:
-
constraint satisfaction problem类
- R1CS - Rank-1 Constraint System
- USCS - Unitary-Square Constraint System
-
circuit satisfaction problem类
- BACS - Bilinear Arithmetic Circuit Satisfiability
- TBCS - Two-input Boolean Circuit Satisfiability
-
ram computation类
RAM是Random Access Machine的缩写。libsnark总结了两种RAM计算框架:
- tinyRAM
- fooRAM
-
arithmetic program类
- QAP - Quadratic Arithmetic Program(GGPR13)
- SQP - Square Arithmetic Program(GM17)
- SSP - Square Span Program (DFGK14)
先介绍实现各种语言中需要的“variable” (variable.hpp/variable.tcc),再详细介绍R1CS以及QAP语言。
2.1 variable
template<typename FieldT>
class variable {
public:
var_index_t index;
...
};
varible的定义非常简单,描述一个variable,只需要记录一个varible对应的标号就行了。比如对应编号为index的variable,表示的是x_{index}变量。
2.2 linear_term
linear_term描述了一个线性组合中的一项。线性组合中的一项由变量以及对应的系数组成:
template<typename FieldT>
class linear_term {
public:
var_index_t index;
FieldT coeff;
...
};
2.3 linear_combination
linear_combination描述了一个完整的线性组合。一个linear combination由多个linear term组成:
template<typename FieldT>
class linear_combination {
public:
std::vector<linear_term<FieldT> > terms;
...
};
2.4 R1CS
R1CS定义在constraint_satisfaction_problems/r1cs/r1cs.hpp。R1CS约束就是满足以下形式的一个表达式:
< A , X > * < B , X > = < C , X >
X是所有变量组合的向量,A/B/C是和X等长的向量。<,>代表的是点乘。一个R1CS系统由多个R1CS约束组成。
R1CS约束定义为:
template<typename FieldT>
class r1cs_constraint {
public:
linear_combination<FieldT> a, b, c;
...
};
一个R1CS约束,可以由a/b/c三个linear_combination表示。
一个R1CS系统中的所有变量的赋值,又分成两部分:primary input和auxiliary input。primary就是"statement", auxiliary就是“witness”。
template<typename FieldT>
using r1cs_primary_input = std::vector<FieldT>;
template<typename FieldT>
using r1cs_auxiliary_input = std::vector<FieldT>;
一个R1CS系统,包括多个R1CS约束。当然,每个约束的向量的长度是固定的(primary input size + auxiliary input size + 1)。
template<typename FieldT>
class r1cs_constraint_system {
public:
size_t primary_input_size;
size_t auxiliary_input_size;
std::vector<r1cs_constraint<FieldT> > constraints;
...
}
2.5 QAP
QAP定义在arithmetic_programs/qap/qap.hpp。libsnark采用的QAP的公式是:A*B-C=H*Z。
template<typename FieldT>
class qap_instance {
private:
size_t num_variables_;
size_t degree_;
size_t num_inputs_;
public:
std::shared_ptr<libfqfft::evaluation_domain<FieldT> > domain;
std::vector<std::map<size_t, FieldT> > A_in_Lagrange_basis;
std::vector<std::map<size_t, FieldT> > B_in_Lagrange_basis;
std::vector<std::map<size_t, FieldT> > C_in_Lagrange_basis;
}
num_variables_表示QAP电路的变量的个数。num_inputs_表示QAP电路的"statement"对应变量的个数。degree_表示A/B/C中每个多项式的阶的个数(和电路的门的个数相关)。
domain是计算傅立叶变换/反傅立叶变换的引擎,由libfqfft库实现。
何为Lagrange basis?
给定一系列的x和y的对应关系,通过拉格朗日插值的方式,可以确定多项式:
p(x) = y_0l_0(x) + y_1l_1(x) + … + y_nl_n(x)
其中l_0(x), l_1(x), … l_n(x)就称为拉格朗日basis。