zokrates-基于ethereum的zkp-II-进阶

让我来 step by step 做介绍:

1.编写关于 bid case 的 code

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/// This zksnark is to be used to verify that an operator has choosen
/// the lowest bid that was actually submitted by an approved supplier was choosen
/// by the operator for the work. For this proof of concept, we will structure the
/// proof to use 3 suppliers only.

/// Inputs for the SNARK

/// PUBLIC FIELDS
/// input_of_py: field[3] public keys for each supplier - should be EDDSA public keys
/// * A: Curve point. Public part of the key used to create S.

/// PRIVATE FIELDS
/// input_of_py: field[3] actual bid from each supplier
/// input_of_py: Requires per each EDDSA signatures per supplier
/// * R: Curve point. Hidden version of the per-message nonce.
/// * S: Field element. Signature to be verified.
/// input_of_py: a secret to aide in building out the merkel tree of the results

/// * context: Curve parameters used to create S. Tells you which curve was used.
/// The context can be generated from the babyjubjub curve.

/// Returns for the SNARK
/// return: a merkel tree including...
/// 1. private key of choosen supplier
/// 2. secret
/// 3. A: Curve point. Public part of the key used to create S.

// In the vectors, the supplier order should be kept the same to compare/access (i.e. supplier one has first bid, supplier two second bid, etc.)

//For signatures
import "ecc/babyjubjubParams.code" as context
import "signatures/verifyEddsa.code" as verifyEddsa
//For unpacking bids correctly
import "utils/pack/unpack128.code" as unpack128
import "utils/pack/pack128.code" as pack128
import "utils/pack/nonStrictUnpack256.code" as unpack256
//For unpacking signatures correctly
import "utils/pack/pack128.code" as pack128
//For merkle tree - changed to padded
import "hashes/sha256/512bitPadded.code" as sha256

def main(private field[6] R, \
field[6] A, \
field[3] S, \
private field secret, \
private field bidone, \
private field bidtwo, \
private field bidthree) -> (field[2]):

//Since each message is constructed from the bids, no way to input_of_py a faulty bid

//Reconstruct messages:
//Note: Field element can be a value between 0 and p (p upper limit: 2^254)
//Makes an assumption that every bid will be 64 bit number
//Message in the signature should be 512 bits

field[128] bidoneunpacked = unpack128(bidone)
field[512] bidonemessage = [...[0; 384], ...bidoneunpacked]
field[128] bidtwounpacked = unpack128(bidtwo)
field[512] bidtwomessage = [...[0; 384], ...bidtwounpacked]
field[128] bidthreeunpacked = unpack128(bidthree)
field[512] bidthreemessage = [...[0; 384], ...bidthreeunpacked]

context = context()

// Supplier 1
1 == verifyEddsa(R[0..2], S[0], A[0..2], bidonemessage[0..256], bidonemessage[256..512], context)
// Supplier 2
1 == verifyEddsa(R[2..4], S[1], A[2..4], bidtwomessage[0..256], bidtwomessage[256..512], context)
// Supplier 3
1 == verifyEddsa(R[4..6], S[2], A[4..6], bidthreemessage[0..256], bidthreemessage[256..512], context)

//Lowest bid should correllate with supplier
//To Do: write the simplest implementation to simplify, each of the below would be a range proof
field[3] lowestbidder = [...[0; 3]] //Save supplier and the lowest bid
lowestbidder = if bidone < bidtwo && bidone < bidthree then [A[0], A[1], bidone] else [...[0; 3]] fi
lowestbidder = if bidtwo < bidone && bidtwo < bidthree then [A[2], A[3], bidtwo] else [...[0; 3]] fi
lowestbidder = if bidthree < bidone && bidthree < bidtwo then [A[4], A[5], bidthree] else [...[0; 3]] fi

//Now we have the lowest bid, supplier sig verified, and secret --> ready for the merkle tree
//512bitpadded - same merkle tree as ethereum
//Leaf One: lowest bid + secret
//Makes an assumption that every secret will be 64 bit number
field lowest = lowestbidder[2]
field[256] lowestbidunpacked = unpack256(lowest)
field[256] secretunpacked = unpack256(secret)
field[256] leafone = sha256(lowestbidunpacked, secretunpacked)

//Leaf Two: Curve point A: A[0] + A[1]
//We know that A[0] and A[1] will be 64 bit numbers, need to unpack to 256
field[256] a0unpacked = unpack256(A[0])
field[256] a1unpacked = unpack256(A[1])
field[256] leaftwo = sha256(a0unpacked, a1unpacked)

//Build the root of leaf one + leaf two
field[256] root = sha256(leafone, leaftwo)

//Before returning, compress back to field element root - split off 128 bits
res0 = pack128(root[..128])
res1 = pack128(root[128..])

return [res0, res1]

2.编译

zokrates compile -i verifybid.code

生成 out 和 out.code 文件。其中 out 文件为 102M,out.code 文件为 60M,文件 size 较大。

3.设置

zokrates setup

生成 proving.key 和 verification.key 文件。其中 proving.key 为 121M,文件较大。

4.执行程序

zokrates compute-witness -a 10055980565918579776429468085502320403896861311334950973306171539247124720876 9872212025624827705538064352986283580914984702567748676648365783032194062540 15914440638590293656936968551196129378835493705090190454426890248467449623197 7164401624892807842686423552737135338148312648059491863415011813391162897156 17656893400745400645892949581714190787095935883004706460374622673779099869831 13238137875306891162631161684759202257598937454005976664682588856632564854848 14897476871502190904409029696666322856887678969656209656241038339251270171395 16668832459046858928951622951481252834155254151733002984053501254009901876174 14897476871502190904409029696666322856887678969656209656241038339251270171395 16668832459046858928951622951481252834155254151733002984053501254009901876174 14897476871502190904409029696666322856887678969656209656241038339251270171395 16668832459046858928951622951481252834155254151733002984053501254009901876174 6460329147541296150725376868570071708599740324087744825213520062979428542760 8063340131639693693865282091868641282847827816145781069349300386706996098818 21548706238088528999687556989620717323700520797702367873886065001951944889761 45 500 400 300

-a 后面为执行程序的参数,即对应着verifybid.code中的入参 private field[6] R, field[6] A, field[3] S, private field secret, private field bidone, private field bidtwo, private field bidthree

其中的 500,400,300 即为 3 方的报价,此处可在前序的智能合约中编写为下面的逻辑:

party one,输入 500,执行 smart contract,

party two,输入 400,执行 smart contract,

party three,输入 300,执行smart contract,

smart contract 收集到法定报价数量之后,verifier 端收到 event,执行 step5 和 step6。

其余参数内容来自inputs_of_command_paras

5.生成证明

zokrates generate-proof

生成 witness 文件。其中 witness 文件为 82M,文件较大。

6.导出 solidity verifier

zokrates export-verifier

生成Verifier.sol

7.编写合约

编写Bid_operator.sol合约,引用 verifyTx 函数。

8.使用 truffle 部署合约

2_deploy_Verifier.js3_deploy_Bid_operator.js 作为配置文件。

9.测试

test/bid_operator.js 中的a, b, c 和 input 均来自于proof.json

回头来总结一下:

1.本次的 bid case 的目的为验证operator选择了最低的 bid 报价,并且验证了该 bid 为 supplier 所提供;

不能证明 supplier one,two,three 之间互相看不到其他 supplier 的 bid。

2.zokrates默认的证明算法为 G16根据 zokrates 的说明,在使用了签名和 ethereum address 作为proofs 证明的情况下,G16 是安全的。并且 G16 无需引用libsnark,生成 witness 和 proof 的速度大大加快,使用配置文件的 size 大大降低。