0%

《区块链》零知识证明

零知识证明是指不提供具体的答案来证明我有这个答案。我面前有一扇门,而且我有钥匙,我不必要使用钥匙打开这扇门就可以证明我有钥匙能够打开这扇门。

假设有一扇用密码上门的锁,小明想要向小红证明,小明知道这个锁的密码,但同时又不告诉小明密码是什么,这个怎么办呢?

其实很简单是不是?小明只需要先让小红进屋等着,然后小红通过门上的猫眼看着小明在门外开锁,因为是透过猫眼,所以小红可以确定不是别人开锁,但同时无法看到小明输入的密码。

当门被小明打开的时候,小红就可以确信,小明确实是知道这个门的密码的。

但重要的是,同时小红并不知道密码到底是什么。在这个简单的故事中,小红对小明进行了一次“零知识验证”(zk-proof)

零知识证明的重要意义在于在保护隐私的情况下,还能证明一些事情

给定一个方程 x^3 + x + 5 = 35,被验证着能使用他手里解计算出x^3 + x + 5的值是35,使得这个方程成立,那就说明这个被验证者有解

上面的密码锁相当于这个方程式左边,锁被打开就是方程式左边结果是35,两边相等成立

zk-snack算法

由于算法非常复杂,没有强大的数学功底很难理解。我也没有尝试去理解,在我看来,对于我们这些非数学家、非科学家,这种事情没有必要花功夫在上面,就好比你没有必要去理解为什么能量守恒一样(当然如果你非要去理解,建议看看相关论文)

但是怎么使用我们还是要掌握的,毕竟这也是一项技能,以后没准能用上

下面以一个例子讲讲怎么使用

给出一个方程式:x^3 + x + 5 = 35,很明显答案是3,怎么不暴露答案3的情况下向B证明A知道3这个答案?

过程是这样:

  1. A使用这个方程式左边x^3 + x + 5以及他手里的答案,生成一个证据proof
  2. A交给B这个proof,B使用方程式左边x^3 + x + 5以及方程式右边35来验证这个proof对不对,对的话说明A确实有这个答案

下面看下Golang中怎么实现的,github.com/arnaucube/go-snark

A这边生成proof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 使用电路代码实现方程式左边
flatCode := `
func exp3(private a):
b = a * a
c = a * b
return c

func main(private s0, public s1):
s3 = exp3(s0)
s4 = s3 + s0
s5 = s4 + 5
equals(s1, s5)
out = 1 * 1
`

// 编译方程式左边
parser := circuitcompiler.NewParser(strings.NewReader(flatCode))
circuit, err := parser.Parse()
assert.Nil(t, err)
fmt.Println(circuit)


b3 := big.NewInt(int64(3))
privateInputs := []*big.Int{b3} // 这是答案
b35 := big.NewInt(int64(35))
publicSignals := []*big.Int{b35} // 这是方程式右边

// witness
w, err := circuit.CalculateWitness(privateInputs, publicSignals) // 方程式左边x^3 + x + 5以及他手里的答案,生成witness(proof的一部分)
assert.Nil(t, err)
fmt.Println("witness", w)

// flat code to R1CS
fmt.Println("generating R1CS from flat code")
a, b, c := circuit.GenerateR1CS() // 使用方程式左边生成各种数据


alphas, betas, gammas, _ := snark.Utils.PF.R1CSToQAP(a, b, c) // 使用方程式左边生成各种数据


ax, bx, cx, px := Utils.PF.CombinePolynomials(w, alphas, betas, gammas) // 使用方程式左边以及witness生成各种数据

// calculate trusted setup
setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas)


proof, err := GenerateProofs(*circuit, setup, w, px) // 生成proof

B这边验证proof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 使用电路代码实现方程式左边
flatCode := `
func exp3(private a):
b = a * a
c = a * b
return c

func main(private s0, public s1):
s3 = exp3(s0)
s4 = s3 + s0
s5 = s4 + 5
equals(s1, s5)
out = 1 * 1
`

// 编译方程式左边
parser := circuitcompiler.NewParser(strings.NewReader(flatCode))
circuit, err := parser.Parse()
assert.Nil(t, err)
fmt.Println(circuit)

b35 := big.NewInt(int64(35))
publicSignals := []*big.Int{b35} // 这是方程式右边

// witness
w := "A交给B的witness"
proof := "A交给B的proof"

// flat code to R1CS
fmt.Println("generating R1CS from flat code")
a, b, c := circuit.GenerateR1CS() // 使用方程式左边生成各种数据


alphas, betas, gammas, _ := snark.Utils.PF.R1CSToQAP(a, b, c) // 使用方程式左边生成各种数据


// calculate trusted setup
setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas)


b35Verif := big.NewInt(int64(35))
publicSignalsVerif := []*big.Int{b35Verif}
assert.True(t, VerifyProof(*circuit, setup, proof, publicSignalsVerif, true)) // 验证

最终B是不知道A手里的答案的,但是B知道A有这个答案

RSA 算法就是零知识证明的简单但非常重要的应用

参考文章

  1. 众目睽睽下隐身,zk-SNARK黑科技如何保护区块链隐私
  2. 是男人就掏出来看看



微信关注我,及时接收最新技术文章