Quadratic Arithmetic Program
Table of Contents
1. zk-SNARK 和 QAP
零知识证明 zk-SNARK 在区块链中的应用最近几年比较活跃,其中往往用到 QAP(Quadratic Arithmetic Program)。
考虑问题:Verify 怎么验证 Prover 执行了下面代码,但 Prover 在证明过程中并不想泄露自己的输入值。这怎么才能实现呢?
function calc(w, arg1, arg2) if w then return arg1 * arg2 else return arg1 + arg2 end if end function
zk-SNARK 的做法是: 先把程序转换为等价的 QAP,如果 Prover 给出了 QAP 的一个解,则说明 Prover 确实执行了程序(注:QAP 问题本身是公开的,它的求解很困难,但验证某个解是否正确是容易的)。当然,由于 Prover 不想泄露自己的解,所以他给出的解是某种加密的形式,我们还要仔细设计一下验证过程。
本文仅仅介绍如何把程序转换为等价的 QAP,并不会介绍 Prover 如何加密解,也不介绍 Verify 如何验证这个加密解的有效性。
注:QSP/QAP 的概念由 Rosario Gennaro 等在 2012 年的论文 Quadratic Span Programs and Succinct NIZKs without PCPs 中提出。
1.1. QAP 的定义
所谓 QAP 就是 给定一系列的多项式以及一个目标多项式,是否能根据这一系列的多项式,求得它们的一个线性组合,刚好可以整除目标多项式。 即如下描述:
- 给定一系列多项式
,目标多项式 - 求一个线性组合
,使得下式成立
注:如果定义
可记为
这个问题如果给出了一个解
zk-SNARK 的思路就是,将原问题的解转化为一个 QAP 问题的解,然后公开 QAP 问题,这样的话拥有解的人公布自己的 witness(当然是某种加密形式),其他人来验证解是否成立。
1.2. zk-SNARK 中为什么使用 QAP
在 zk-SNARK 中,Verifier 需要“快速地”验证 Prover 的提供的证明是否正确。 QAP 有个特点:验证 QAP 的解是否正确很容易,而求解 QAP 的解则很难。 zk-SNARK 中,把原问题的解转化为一个 QAP 问题的解后,Verifier 就可以“快速地”验证解是否正确了。
2. 转换为 QAP
下面还是以前面提到的程序以例:
function calc(w, arg1, arg2) if w then return arg1 * arg2 else return arg1 + arg2 end if end function
介绍如何把它转换为 QAP。
2.2. 直观的做法
在文章 Zero-Knowledge Proof (zk-SNARK, Pinocchio) 7.8 计算示例 中介绍了把上面例子中程序转换为对应 QAP 的直观做法。
这种直观的做法不适合编程实现,我们需要一种标准的做法,请看下节。
2.3. 标准的做法
利用 R1CS(rank 1 constraint system)实现到 QAP 的转换。
2.3.1. R1CS 的定义
R1CS 是由一系列三向量组
其中
2.3.2. 从多项式到算术电路(Flatten)
把多项式写为
1: sym_1 = arg1 * arg2 2: sym_2 = w * sym_1 3: sym_3 = 1 - w 4: sym_4 = arg1 + arg2 5: sym_5 = sym_3 * sym_4 6: v = sym_2 + sym_5 7: w = w * w
上面的形式即算数电路(Arithmetic circuit),这个过程称为拍平(Flatten)。
为了将所有操作都转成
2.3.3. 从算术电路到 R1CS
有一种简单的方法可以把拍平后的算术电路转移为 R1CS。把电路中涉及的所有变量(包含中间变量)组成的下面向量作为解向量:
注意,上面 解向量
有了这个解向量后,我们可以按下面方法寻找出满足条件的一系列三向量组
针对第 1 行的约束
它满足 R1CS 的要求:
针对第 2 行的约束
它也满足 R1CS 的要求:
针对第 3 行的约束
它也满足 R1CS 的要求:
针对第 4 行的约束
它也满足 R1CS 的要求:
针对第 5 行的约束
它也满足 R1CS 的要求:
针对第 6 行的约束
它也满足 R1CS 的要求:
针对第 7 行的约束
它也满足 R1CS 的要求:
上面这一系列三向量组
A [0, 0, 1, 0, 0, 0, 0, 0, 0, 0] [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] [1, -1, 0, 0, 0, 0, 0, 0, 0, 0] [0, 0, 1, 1, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 1, 0, 0, 0] [0, 0, 0, 0, 0, 1, 0, 0, 1, 0] [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] B [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 1, 0, 0, 0, 0, 0] [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 1, 0, 0] [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] C [0, 0, 0, 0, 1, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 1, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 1, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 1, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 1, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
2.3.4. 从 R1CS 到 QAP
R1CS 是使用向量点积来实现约束,下面介绍如何把这些约束转换为 QAP。
把前面得到的
设
设
使用类似的方法,可以求得:
同样地,我们根据 R1CS 中的矩阵
上面例子中,假设
这个解就是 QAP 的一个解(witness),即
注:Vitalik Buterin 在文章 Quadratic Arithmetic Programs: from Zero to Hero 中介绍了从 R1CS 到 QAP 的转换,且在 https://github.com/ethereum/research/blob/master/zksnark/qap_creator.py 中使用 Python 编写了相应的转换程序。
2.4. 有限域上计算
前面的计算,我们发现会出现浮点数,这样会有舍入误差。在实际中我们会在“有限域”上做算术运算,这样结果都是整数,不存在舍入误差了。
在有限素域
文章 R1CS to QAP (quadratic arithmetic program) with zkSNARKs in Go 中有“在有限域上求 QAP”的例子。