许多待证明的问题可以表示为多项式函数的形式,而一个多项式函数可以转化为一个算术电路,已经有文献证明算术电路可以转换成QAP问题,基于QAP问题可以构造zkSNARKs,所以基于QAP问题构造zkSNARKs的第一步是将待证明问题转换成QAP问题,本文主要讲解如何将待证明问题转换成QAP问题。
QAP定义:
给一系列多项式以及一个目标多项式,是否能根据这一系列的多项式,求得它们的一个线性组合,刚好可以整除目标多项式,如下描述,给定一个解 { a i } i = 0 − > n \{a_i\}_{i=0->n} {ai}i=0−>n,那么验证很简单,直接除t(x)即可,但是想求解就很难:
-
给定一系列多项式 { l i ( x ) , r i ( x ) , o i ( x ) } i = 0 − > n \{l_i(x),r_i(x),o_i(x)\}_{i=0->n} {li(x),ri(x),oi(x)}i=0−>n ,以及目标 t ( x ) t(x) t(x)
-
求一个线性组合 { a i } i = 0 − > n \{a_i\}_{i=0->n} {ai}i=0−>n,使得:
∑ i = 0 n { ( a i ∗ l i ( x ) ) ( a i ∗ r i ( x ) ) − ( a i ∗ o i ( x ) ) } = t ( x ) h ( x ) \sum_{i=0}^{n}\{(a_i*l_i(x))(a_i*r_i(x))-(a_i*o_i(x))\}=t(x)h(x) i=0∑n{(ai∗li(x))(ai∗ri(x))−(ai∗oi(x))}=t(x)h(x)
下面以 a*(b+2) = c 为例,讲解QAP的构造过程
1.多项式拍成电路,首先将多项式的所有操作转化为算术电路形式,即 x ( o p ) y = z x(op)y =z x(op)y=z,根据算术电路可以画出相应的电路图
sum1 = b + 2
c = a * sum1
2.电路转换成R1CS,R1CS描述了向量组之间的约束关系,把算术电路变量包括中间变量组成一组向量 :s = [one,c,a,b,sum1]T,s即为R1CS中的解向量,其中one表示数字1,对上面的电路进行转换:
(b+2) * 1 = sum1
x = [2 0 0 1 0]
y = [1 0 0 0 0]
z = [0 0 0 0 1]
a * sum1 = c
x = [0 0 1 0 0]
y = [0 0 0 0 1]
z = [0 1 0 0 0]
由上可得X 、Y 、Z向量
3.R1CS to QAP,我们已经得到X 、Y 、Z向量,满足:
X
⋅
S
∗
Y
⋅
S
−
Z
⋅
S
=
0
X\cdot S*Y\cdot S - Z\cdot S =0
X⋅S∗Y⋅S−Z⋅S=0
代入如x=1、2 ,即t(x) =(x-1)(x-2)由拉格朗日差值定理可以计算出 li(x),ri(x),oi(x)
由(1,2) (2,0) 得 l0(x) = 4 - 2x , 取有限域GF(11)上的多项式计算,则l0(x) = 4 + 9x,下同
由(1,0) (2,0) 得 l1(x) = 0
由(1,0) (2,1) 得 l2(x) = 10 +x
由(1,1) (2,0) 得 l3(x) = 2 + 10x
由(1,0) (2,0) 得 l4(x) = 0
—————————————
由(1,1) (2,0) 得 r0(x) = 2 + 10x
由(1,0) (2,0) 得 r1(x) = 0
由(1,0) (2,0) 得 r2(x) = 0
由(1,0) (2,0) 得 r3(x) = 0
由(1,0) (2,1) 得 r4(x) = 10 + x
—————————————
由(1,0) (2,0) 得 o0(x) = 0
由(1,0) (2,1) 得 o1(x) = 10 +x
由(1,0) (2,0) 得 o2(x) = 0
由(1,0) (2,0) 得 o3(x) = 0
由(1,1) (2,0) 得 o1(x) = 2 + 10x
—————————————
由上可得li(x)、ri(x)、oi(x)
下面进行验证:
a*(b+2) = c,取 a = 3 、b=2 ,得sum1=4、c=12
s = [1,12, 3,2,4],t(x) = (x-1)(x-2) = 2 + 8x + x2
L(x) = 5 +10x ,R(x) = 9 + 3x , O(x) = 7+ 8x
P(x) = L(x)R(X) - O(x) = 5 + 9x + 8x2
h(x) = P(x)/t(x) = 8
Coding 验证
package main
import (
"fmt"
"github.com/arnaucube/go-snark/circuitcompiler"
"github.com/arnaucube/go-snark/fields"
"github.com/arnaucube/go-snark/r1csqap"
"math/big"
"strings"
)
func main() {
str := `
func main(private a, private b,public c):
s1 = b + 2
c = a * s1
`
fmt.Printf("code of the circuit: %s\n", str)
parser := circuitcompiler.NewParser(strings.NewReader(str))
circuit, _ := parser.Parse()
fmt.Println("\ncode to R1CS")
a, b, c := circuit.GenerateR1CS()
fmt.Printf("a: %s\n", a)
fmt.Printf("b: %s\n", b)
fmt.Printf("c: %d\n\n", c)
fmt.Println("\nR1CS to QAP")
r, _ := new(big.Int).SetString("11", 10)
pf := r1csqap.NewPolynomialField(fields.NewFq(r))
alphas, betas, gammas, _ := pf.R1CSToQAP(a, b, c)
fmt.Printf("As: %d\n\n", alphas)
fmt.Printf("Bs: %d\n\n", betas)
fmt.Printf("Cs: %d\n\n", gammas)
// witness
privateVal := []*big.Int{big.NewInt(int64(3)), big.NewInt(int64(2))}
publicVal := []*big.Int{big.NewInt(int64(12))}
w, _ := circuit.CalculateWitness(privateVal, publicVal)
fmt.Println("\nWitness: ", w)
ax, bx, cx, px := pf.CombinePolynomials(w, alphas, betas, gammas)
fmt.Printf("\nax: %d\n", ax)
fmt.Printf("\nbx: %d\n", bx)
fmt.Printf("\ncx: %d\n", cx)
fmt.Printf("\npx: %d\n", px)
}
why R1CS -> QAP ?
上述构造过程中需要将R1CS约束转换成QAP问题,但是 why R1CS -> QAP ?
我们知道R1CS约束矩阵是一个m行矩阵,m代表电路中门电路的数量,如前面例子中,下面三个矩阵就是原问题对应电路的R1CS约束矩阵,每个矩阵包含了两个行向量,代表了电路中的两个电路门
此时如果我们想证明我们知道原始计算的一个解,那么就需要证明X,Y,Z矩阵中的每个行向量与解向量S的内积之后的值是符合R1CS约束的:
在大规模电路下,存在成千上万个电路门,R1CS验证效率会十分低下,于是我们把向量的内积计算转化为多项式的形式,多项式具有良好的性质,可以通过一次计算来验证所有约束的正确性,比如 prover 声称他知道一些 verifier 也知道的多项式,通过一次抽样验证就行
对于约束矩阵X,我们假设存在这样一个多项式l1(x) ,经过点(1,2)、(2,0), 通过拉格朗日插值定理 ,可以计算出
l 1 ( x ) = 4 + 9 x l_1(x) = 4+9x l1(x)=4+9x
同理我们可以得到 l i ( x ) 、 r i ( x ) 、 o i ( x ) l_i(x)、r_i(x)、o_i(x) li(x)、ri(x)、oi(x),然后可以用多项式去表示R1CS约束:
( 1 ∗ l 1 ( x ) + 12 ∗ l 2 ( x ) + 3 ∗ l 3 ( x ) + 2 ∗ l 4 ( x ) + 4 ∗ l 5 ( x ) ) (1*l_1(x) +12*l_2(x) +3*l_3(x)+2*l_4(x)+4*l_5(x)) (1∗l1(x)+12∗l2(x)+3∗l3(x)+2∗l4(x)+4∗l5(x))
∗ ( 1 ∗ r 1 ( x ) + 12 ∗ r 2 ( x ) + 3 ∗ r 3 ( x ) + 2 ∗ r 4 ( x ) + 4 ∗ r 5 ( x ) ) * (1*r_1(x) +12*r_2(x) +3*r_3(x)+2*r_4(x)+4*r_5(x)) ∗(1∗r1(x)+12∗r2(x)+3∗r3(x)+2∗r4(x)+4∗r5(x))
− ( 1 ∗ o 1 ( x ) + 12 ∗ o 2 ( x ) + 3 ∗ o 3 ( x ) + 2 ∗ o 4 ( x ) + 4 ∗ o 5 ( x ) ) - (1*o_1(x) +12*o_2(x) +3*o_3(x)+2*o_4(x)+4*o_5(x)) −(1∗o1(x)+12∗o2(x)+3∗o3(x)+2∗o4(x)+4∗o5(x))
= 0 = 0 =0
令上式左边等于 p ( x ) p(x) p(x)
当x=1时,即验证第一个门电路( p ( 1 ) = 0 p(1)=0 p(1)=0)
当x=2时,即验证第二个门电路 ( p ( 2 ) = 0 p(2)=0 p(2)=0 )
根据多项式的因式分解:一个多项式只要它有解,就可以将它分解成线性多项式
所以
p
(
x
)
p(x)
p(x)可以 表示成 :
p
(
x
)
=
(
x
−
1
)
(
x
−
2
)
.
.
.
=
t
(
x
)
h
(
x
)
,
t
(
x
)
=
(
x
−
1
)
(
x
−
2
)
p(x) = (x-1)(x-2)... = t(x)h(x) ,t(x) = (x-1)(x-2)
p(x)=(x−1)(x−2)...=t(x)h(x),t(x)=(x−1)(x−2)