Compare commits
1034 Commits
zcash-2.0.
...
paper
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aabbc856c7 | ||
|
|
d9ac7bd19b | ||
|
|
0f0d84e9ca | ||
|
|
49459d030f | ||
|
|
cafbe61eba | ||
|
|
fc247fc898 | ||
|
|
46e88a5e69 | ||
|
|
5e1a2f9d3f | ||
|
|
9379eec1b8 | ||
|
|
4f0f10a53c | ||
|
|
be0ee9eb82 | ||
|
|
b52e4aac3a | ||
|
|
c49aaceb5d | ||
|
|
f337eb1f5c | ||
|
|
805aa45f9e | ||
|
|
05a9f3360f | ||
|
|
31d887c943 | ||
|
|
d7f78db121 | ||
|
|
d4bae99cb7 | ||
|
|
2d30c29d06 | ||
|
|
76e0f658c1 | ||
|
|
3a3008caf9 | ||
|
|
8a210ec271 | ||
|
|
2064d1c801 | ||
|
|
ee32f7facb | ||
|
|
903c02b991 | ||
|
|
1f38523fff | ||
|
|
865275e2a2 | ||
|
|
18aceea225 | ||
|
|
0aa127d7b1 | ||
|
|
8626cc7f26 | ||
|
|
eed7e8199f | ||
|
|
c3f9e2d73c | ||
|
|
cca1670248 | ||
|
|
f1619f896c | ||
|
|
edcd884fe8 | ||
|
|
583a04b4de | ||
|
|
573510115d | ||
|
|
8ad33e50a6 | ||
|
|
fb8c73c950 | ||
|
|
9ea0427678 | ||
|
|
dd112813a1 | ||
|
|
0907adf608 | ||
|
|
e2c131fdc3 | ||
|
|
9059f53873 | ||
|
|
26be46573e | ||
|
|
7820548ea3 | ||
|
|
d5ed684701 | ||
|
|
e6a8630b35 | ||
|
|
cd326f2b6a | ||
|
|
c24024b8e1 | ||
|
|
7eb4a6d1d9 | ||
|
|
141821d3c1 | ||
|
|
219391ac92 | ||
|
|
67d700feab | ||
|
|
ac983b7612 | ||
|
|
8d6f882510 | ||
|
|
8d967c8609 | ||
|
|
d9d50b98ab | ||
|
|
88e02329eb | ||
|
|
60eac4e8b7 | ||
|
|
4ae238ea1f | ||
|
|
8d7f27ffb9 | ||
|
|
2ecdbc2036 | ||
|
|
bb39037798 | ||
|
|
7be66ad8f8 | ||
|
|
7fda177da8 | ||
|
|
d1bc61800c | ||
|
|
de5943aea4 | ||
|
|
8651bb41ce | ||
|
|
9cb8c0b3c4 | ||
|
|
7181d603be | ||
|
|
5d03619466 | ||
|
|
7722b1a50b | ||
|
|
0028048800 | ||
|
|
720ee6476f | ||
|
|
5823a695e2 | ||
|
|
bc35a62e10 | ||
|
|
40d22ea2cc | ||
|
|
ac4acfa26e | ||
|
|
4d290e7a32 | ||
|
|
b479981689 | ||
|
|
885e09a82f | ||
|
|
fab9160b26 | ||
|
|
5a177eea27 | ||
|
|
3425eabda2 | ||
|
|
70857b0eed | ||
|
|
42b7f328fb | ||
|
|
cd1083e086 | ||
|
|
71c6188bf8 | ||
|
|
acad37924b | ||
|
|
481e43689c | ||
|
|
f24ec04340 | ||
|
|
b9bfc07146 | ||
|
|
3082593fcc | ||
|
|
6b7a3dec9c | ||
|
|
e701687b69 | ||
|
|
29b8d7a756 | ||
|
|
cb818ecbe3 | ||
|
|
9d4412103b | ||
|
|
84dc3bf73c | ||
|
|
2cd8a7f4d2 | ||
|
|
274f860202 | ||
|
|
e9f94119bc | ||
|
|
b44653e686 | ||
|
|
7fa51e38c0 | ||
|
|
1e2bc7f65c | ||
|
|
2bbd25b36b | ||
|
|
a3b85b8fe6 | ||
|
|
b66ac11775 | ||
|
|
e746f7b6f9 | ||
|
|
3b9dfc1e0b | ||
|
|
fb9e9bb12f | ||
|
|
2b71121681 | ||
|
|
f899ecfce5 | ||
|
|
591b1fc28f | ||
|
|
af7e263bcc | ||
|
|
a57dc7f478 | ||
|
|
7844394364 | ||
|
|
98731c8374 | ||
|
|
b0ba7fe4d2 | ||
|
|
d6bc2fe0af | ||
|
|
8b08528bb0 | ||
|
|
25558893ab | ||
|
|
68cada53cf | ||
|
|
aa37783c37 | ||
|
|
216f0f62a0 | ||
|
|
80d339848f | ||
|
|
b872e9fc49 | ||
|
|
39971891d5 | ||
|
|
2399d9f3e3 | ||
|
|
212b6e67d0 | ||
|
|
6de1cb1397 | ||
|
|
93563c3c65 | ||
|
|
524055bbb4 | ||
|
|
730d2cbc7e | ||
|
|
7476e4ea28 | ||
|
|
c68e15e4f3 | ||
|
|
b42477a0bf | ||
|
|
4c9ebcce9a | ||
|
|
da0d0a669a | ||
|
|
443f45f430 | ||
|
|
e7b5329e95 | ||
|
|
43efdf992b | ||
|
|
4ad3988e43 | ||
|
|
7f3036d2c8 | ||
|
|
08806cc109 | ||
|
|
1eb4fb91f6 | ||
|
|
7879b63321 | ||
|
|
ce2416623f | ||
|
|
1fbf38280e | ||
|
|
d9a0b9c83f | ||
|
|
fb6fef4e9c | ||
|
|
38aa09b6a1 | ||
|
|
4cbc0451c1 | ||
|
|
1d02363752 | ||
|
|
30660e1d1c | ||
|
|
d2da9cfcb0 | ||
|
|
450087e280 | ||
|
|
9377b78b35 | ||
|
|
39a73c8eda | ||
|
|
3701c2b442 | ||
|
|
4dff8055bf | ||
|
|
caab37c336 | ||
|
|
e391594e9e | ||
|
|
15b4c37ab0 | ||
|
|
3efb7f9146 | ||
|
|
d56758d426 | ||
|
|
f3533e291f | ||
|
|
e0c5ef22bc | ||
|
|
ace929c5ba | ||
|
|
bb0a769162 | ||
|
|
68ba93f5fd | ||
|
|
6240c02208 | ||
|
|
be18eb240c | ||
|
|
9080b4ccf3 | ||
|
|
f5dfe07355 | ||
|
|
2ee7b108af | ||
|
|
2edcc12e8e | ||
|
|
2b92493a45 | ||
|
|
abd03928b4 | ||
|
|
cc2a41d86c | ||
|
|
804f4cba67 | ||
|
|
7ee61c4f94 | ||
|
|
43496857c9 | ||
|
|
4835be05b0 | ||
|
|
15633ad434 | ||
|
|
414d651c9c | ||
|
|
8541b2bde4 | ||
|
|
28dcc1c346 | ||
|
|
b397a9c405 | ||
|
|
0a3b0a9341 | ||
|
|
89dad572ef | ||
|
|
79cba2e500 | ||
|
|
4ec651d172 | ||
|
|
f5c1381c4c | ||
|
|
a0c33945ab | ||
|
|
96b130e034 | ||
|
|
636f3e3751 | ||
|
|
b73f49c968 | ||
|
|
a5c4d51652 | ||
|
|
c87122561f | ||
|
|
9470610b75 | ||
|
|
0afa122970 | ||
|
|
0bfd1d6b0d | ||
|
|
ad403f1cca | ||
|
|
a9d2ce7150 | ||
|
|
6d9deefb93 | ||
|
|
49763d1c01 | ||
|
|
de053e1d8f | ||
|
|
03524ba7d0 | ||
|
|
265c6e5303 | ||
|
|
c4f8f8ea04 | ||
|
|
5d0aa521c2 | ||
|
|
bef3e7e336 | ||
|
|
2b6fbfd2d1 | ||
|
|
a46ace4c2d | ||
|
|
942a976ef5 | ||
|
|
5d2f84a154 | ||
|
|
6b36cb5a51 | ||
|
|
d6f6b50ecd | ||
|
|
abbd43ff57 | ||
|
|
86142d044c | ||
|
|
73ee19239c | ||
|
|
b19b40ccf0 | ||
|
|
872ac5af7b | ||
|
|
efac432128 | ||
|
|
b03b4cf958 | ||
|
|
d8c04e8143 | ||
|
|
f432983f09 | ||
|
|
bce88797f4 | ||
|
|
771aa867c1 | ||
|
|
615c4f662e | ||
|
|
49f20e6735 | ||
|
|
d58b33fb78 | ||
|
|
4c49f09515 | ||
|
|
68983dc0c1 | ||
|
|
fa04929891 | ||
|
|
c05446d2ae | ||
|
|
0da4921055 | ||
|
|
c00b65bb78 | ||
|
|
ee9c88ecb0 | ||
|
|
f9f17b291f | ||
|
|
563961e606 | ||
|
|
0c7eb84d36 | ||
|
|
7c1d4d9a5b | ||
|
|
9016548698 | ||
|
|
fe93f2ff6b | ||
|
|
91541675e2 | ||
|
|
3a8efd9e67 | ||
|
|
d65fe2cda9 | ||
|
|
d78c94b2a2 | ||
|
|
ad33798244 | ||
|
|
e7d67364cc | ||
|
|
ba3705cb5e | ||
|
|
5e706d5d84 | ||
|
|
894421f3b5 | ||
|
|
4014f08105 | ||
|
|
5a479363ff | ||
|
|
724e822d2d | ||
|
|
27337f1bb0 | ||
|
|
bd8eea97b7 | ||
|
|
7ad0452c01 | ||
|
|
3a09eef6b3 | ||
|
|
767d73f777 | ||
|
|
5c6d856714 | ||
|
|
f316c1b439 | ||
|
|
261ad90d33 | ||
|
|
d63fa334ff | ||
|
|
53182aa08e | ||
|
|
fec961777c | ||
|
|
37531ed747 | ||
|
|
76795a9014 | ||
|
|
c28ae31c71 | ||
|
|
573ffc4e06 | ||
|
|
9807a5c1cc | ||
|
|
07c690cf73 | ||
|
|
a5f25c5058 | ||
|
|
4b021fcf4d | ||
|
|
09882c6d08 | ||
|
|
b35a819a09 | ||
|
|
7809711a81 | ||
|
|
f523ac285d | ||
|
|
cc0fc98c22 | ||
|
|
b0d8747697 | ||
|
|
f1b6e88f9f | ||
|
|
e12d315ab9 | ||
|
|
fc3dd8198b | ||
|
|
4991e53f48 | ||
|
|
e59738b4ee | ||
|
|
7b11d64cf9 | ||
|
|
66c31be6c5 | ||
|
|
fec7e07764 | ||
|
|
845babc3c2 | ||
|
|
d4b6c0e1a2 | ||
|
|
c794cdc680 | ||
|
|
ae364f3ae7 | ||
|
|
081937162c | ||
|
|
40f768ed60 | ||
|
|
6f0080ba72 | ||
|
|
7461f8936d | ||
|
|
a7c5993597 | ||
|
|
272be62212 | ||
|
|
81c58172c3 | ||
|
|
9a4f6812f1 | ||
|
|
bc7ea564d3 | ||
|
|
3584485516 | ||
|
|
1b865ecfdf | ||
|
|
5fb9b86ba0 | ||
|
|
b8af749b40 | ||
|
|
61c633db1e | ||
|
|
2ae5804a67 | ||
|
|
7ea6d10480 | ||
|
|
f931562431 | ||
|
|
81786c24c0 | ||
|
|
13933d8c51 | ||
|
|
3e35dd215c | ||
|
|
3211536324 | ||
|
|
ec321382e1 | ||
|
|
a12b8053b0 | ||
|
|
2f1cae62b1 | ||
|
|
52ea437e11 | ||
|
|
4a6c9ec425 | ||
|
|
a28d94ff2e | ||
|
|
1760b275a7 | ||
|
|
3a73f946c5 | ||
|
|
e6663212ff | ||
|
|
7c07914bfd | ||
|
|
3891fe57d4 | ||
|
|
4255b44b21 | ||
|
|
59ed258c7f | ||
|
|
ab60b8804a | ||
|
|
0ea4408d46 | ||
|
|
fa50d551c8 | ||
|
|
17f6bbcc67 | ||
|
|
532299d46e | ||
|
|
4b61120cd0 | ||
|
|
54ef63bace | ||
|
|
a1cd9dfbac | ||
|
|
c26188a0bb | ||
|
|
dab3c002b7 | ||
|
|
1862354ea6 | ||
|
|
01618038bf | ||
|
|
05f098e893 | ||
|
|
0255dca16e | ||
|
|
5a48d179b8 | ||
|
|
7f60f0f881 | ||
|
|
c4e14ad0b1 | ||
|
|
6f9083b5ab | ||
|
|
b0913afdd7 | ||
|
|
8f7adec0d9 | ||
|
|
60d344a0a7 | ||
|
|
83e1af104e | ||
|
|
5728bda2c1 | ||
|
|
ccf75c39c1 | ||
|
|
6149166ccb | ||
|
|
adfc88926b | ||
|
|
4606a0cefb | ||
|
|
a7e22b3550 | ||
|
|
ce6e2a5825 | ||
|
|
7a6642b221 | ||
|
|
9e758dc7d9 | ||
|
|
8361674efc | ||
|
|
5e3409ea85 | ||
|
|
504c3eaeae | ||
|
|
07dbfbef59 | ||
|
|
79006ecbdf | ||
|
|
e67560b154 | ||
|
|
b9cea33804 | ||
|
|
263bbe1207 | ||
|
|
98d7621135 | ||
|
|
9b4186705a | ||
|
|
70a7069058 | ||
|
|
667d6101c9 | ||
|
|
bf74915053 | ||
|
|
f4059a5faa | ||
|
|
8f3f95ee08 | ||
|
|
785f22ca5a | ||
|
|
d1ce6749fe | ||
|
|
999dcbfcab | ||
|
|
91c6b0b3f0 | ||
|
|
a3a9ee2682 | ||
|
|
dd9c9ffa3f | ||
|
|
17f60a0354 | ||
|
|
81b2b1b554 | ||
|
|
f25a8a557e | ||
|
|
fae919ec1c | ||
|
|
3b6f5e3d5e | ||
|
|
b65aae9bc1 | ||
|
|
fdb6e208db | ||
|
|
060977fe54 | ||
|
|
9d80be62f9 | ||
|
|
6846ac5f9e | ||
|
|
75bede4bc8 | ||
|
|
d4fce58019 | ||
|
|
23aa869bf4 | ||
|
|
6d03b5c1db | ||
|
|
0ee1e81f5d | ||
|
|
2d97ccb7b8 | ||
|
|
c5642f9f5e | ||
|
|
c2d0a7d048 | ||
|
|
437b66d4ee | ||
|
|
34f762cea2 | ||
|
|
edf7bc144d | ||
|
|
34658c4bd3 | ||
|
|
6dcb4040af | ||
|
|
8c5cd4e4f6 | ||
|
|
e17e4b1346 | ||
|
|
9086dd9afb | ||
|
|
899d852c39 | ||
|
|
566db65a91 | ||
|
|
2b1583d75f | ||
|
|
247f3fb038 | ||
|
|
484330ebd9 | ||
|
|
8e098d4d72 | ||
|
|
6996853168 | ||
|
|
757316d355 | ||
|
|
70caa7d4b0 | ||
|
|
65bbe7daed | ||
|
|
9b455a12cc | ||
|
|
d7ba310294 | ||
|
|
6c99d71d4f | ||
|
|
3501365950 | ||
|
|
663f9d619d | ||
|
|
b856d23069 | ||
|
|
4289843852 | ||
|
|
670bb277e9 | ||
|
|
a1664c6bbc | ||
|
|
20d5cdc571 | ||
|
|
e21be37042 | ||
|
|
e4187f07ff | ||
|
|
3d39706aee | ||
|
|
012d43bc8c | ||
|
|
34ca75cceb | ||
|
|
80db0ae2f9 | ||
|
|
9ae5a9d624 | ||
|
|
2d43e3be7c | ||
|
|
09a20aacfe | ||
|
|
6b43b2d0d6 | ||
|
|
c57a31e82e | ||
|
|
63be3c9470 | ||
|
|
482bef87f0 | ||
|
|
b1ce3905d6 | ||
|
|
af9f9c17ee | ||
|
|
07955092f3 | ||
|
|
00983c48cd | ||
|
|
538de482f3 | ||
|
|
2e408957db | ||
|
|
22ccd1bceb | ||
|
|
76cd0d92bb | ||
|
|
88746e76fa | ||
|
|
794cf79db2 | ||
|
|
9f7e5fa3ce | ||
|
|
fe16e4a412 | ||
|
|
f65c37ea01 | ||
|
|
d5b6b9aa1e | ||
|
|
1a1c77536d | ||
|
|
c03871779a | ||
|
|
4113df4780 | ||
|
|
813cf16ff2 | ||
|
|
e378229bdd | ||
|
|
922ffe6002 | ||
|
|
e1841806c5 | ||
|
|
eb2db2e667 | ||
|
|
8be50c9531 | ||
|
|
c9b23dfdef | ||
|
|
e25b614573 | ||
|
|
7ff32b04d6 | ||
|
|
d707ebd321 | ||
|
|
9b06205ed6 | ||
|
|
61ce4dd3d6 | ||
|
|
9282c7da29 | ||
|
|
2d2e4aad86 | ||
|
|
cc183efda4 | ||
|
|
1f11c404dc | ||
|
|
0c81695731 | ||
|
|
2d8b1fe504 | ||
|
|
06da3b9ac8 | ||
|
|
bbec1b841d | ||
|
|
041671f642 | ||
|
|
e490b79907 | ||
|
|
91ff2c71cf | ||
|
|
0b6bbfb444 | ||
|
|
c0b953b8f7 | ||
|
|
17aa1f55e4 | ||
|
|
e1c6232dd7 | ||
|
|
6e0364cd42 | ||
|
|
6526b9fb53 | ||
|
|
a9059c9372 | ||
|
|
051027cb6d | ||
|
|
9f748554d0 | ||
|
|
e924247e73 | ||
|
|
ad16ba6a35 | ||
|
|
176470ef41 | ||
|
|
0ec85d362d | ||
|
|
d7257ce010 | ||
|
|
6671234ad1 | ||
|
|
770c4bef25 | ||
|
|
e27fc674f5 | ||
|
|
ac8b27a5ba | ||
|
|
a4e86be665 | ||
|
|
80130f5656 | ||
|
|
599079fafe | ||
|
|
39f978bb4f | ||
|
|
9c9607e47a | ||
|
|
342d97ac15 | ||
|
|
e47cafd691 | ||
|
|
3b31854de7 | ||
|
|
fea07c52de | ||
|
|
f5d6091c93 | ||
|
|
e87fb329f9 | ||
|
|
0e1d440287 | ||
|
|
2fdfa4b671 | ||
|
|
8db848139b | ||
|
|
1b8da6b12d | ||
|
|
21084bde20 | ||
|
|
b4d41b689c | ||
|
|
b70d6e66fc | ||
|
|
99c03c5708 | ||
|
|
95b498af33 | ||
|
|
821810cd82 | ||
|
|
9cb8accf09 | ||
|
|
a57d2773ab | ||
|
|
cae9715a8e | ||
|
|
3e43cae526 | ||
|
|
c2862a4382 | ||
|
|
df7bfce0be | ||
|
|
1e3f7739a3 | ||
|
|
4272cfa5b0 | ||
|
|
276e09f1fb | ||
|
|
3e8f2f8202 | ||
|
|
718b25c949 | ||
|
|
3d41ee5aba | ||
|
|
7dfc50e763 | ||
|
|
ef56fabf7b | ||
|
|
2ffa94f714 | ||
|
|
183a64b08e | ||
|
|
fa8103764a | ||
|
|
c5b883f91e | ||
|
|
4752a91781 | ||
|
|
cc5b835102 | ||
|
|
94cacc6152 | ||
|
|
661558e0c8 | ||
|
|
526676ecfc | ||
|
|
2067360930 | ||
|
|
0eb9f5040b | ||
|
|
69ce66ae6c | ||
|
|
002173e187 | ||
|
|
06a152734c | ||
|
|
c49590bab7 | ||
|
|
bb22a167af | ||
|
|
defdf8df52 | ||
|
|
a9d8079c2a | ||
|
|
d9d711ebb7 | ||
|
|
1db099f1cc | ||
|
|
44b601b0a9 | ||
|
|
729138a31e | ||
|
|
f6d9ec8faf | ||
|
|
91a8dc8284 | ||
|
|
4c984595e5 | ||
|
|
623dbd0d74 | ||
|
|
b0b754ba04 | ||
|
|
5a48059a14 | ||
|
|
29a9161981 | ||
|
|
58cb06ee92 | ||
|
|
8201a3933f | ||
|
|
1a3a2bec74 | ||
|
|
428f463555 | ||
|
|
755fc7aba8 | ||
|
|
6abfcca25a | ||
|
|
89f47ef5c2 | ||
|
|
eb409fa3cf | ||
|
|
2ff318eecb | ||
|
|
f491e02b56 | ||
|
|
87c62e2248 | ||
|
|
09b6e6f921 | ||
|
|
da5f1d3e37 | ||
|
|
e4143a4bbc | ||
|
|
97bdd1655f | ||
|
|
2a28b61468 | ||
|
|
98bab6877a | ||
|
|
7beeb52730 | ||
|
|
36cd38e239 | ||
|
|
3b8246f3eb | ||
|
|
5687acfaf8 | ||
|
|
347666705c | ||
|
|
b27dc2914b | ||
|
|
6e08a5a22b | ||
|
|
e94dbf2523 | ||
|
|
4eab1fc68a | ||
|
|
f54feda94f | ||
|
|
e4175d81e9 | ||
|
|
840c23bd23 | ||
|
|
45e4ca38e4 | ||
|
|
916dbce2df | ||
|
|
0f230a70b9 | ||
|
|
e554b473dd | ||
|
|
10c5010fd9 | ||
|
|
96b2d3e41a | ||
|
|
d14f2af8fa | ||
|
|
45c73dbd8f | ||
|
|
6e57ef0c4a | ||
|
|
6ec7272586 | ||
|
|
c4fd8856ae | ||
|
|
dbe897d365 | ||
|
|
2d12b9a858 | ||
|
|
fcaddaa356 | ||
|
|
bcc8379a7f | ||
|
|
4cf5a534ec | ||
|
|
1026e92194 | ||
|
|
9f24b68b3a | ||
|
|
3704b2422e | ||
|
|
9418001d0e | ||
|
|
470c9572eb | ||
|
|
7ee29503ec | ||
|
|
97bead9977 | ||
|
|
cfd378685f | ||
|
|
def5de3f16 | ||
|
|
d715e812bd | ||
|
|
4de908b1e5 | ||
|
|
162a3877e5 | ||
|
|
ac13cb05bc | ||
|
|
75c5269d3b | ||
|
|
36a6b5fd90 | ||
|
|
abca61401e | ||
|
|
4f0a553fbb | ||
|
|
51bb5f0f70 | ||
|
|
e6397507ca | ||
|
|
601e8e38f8 | ||
|
|
f9e58c01ce | ||
|
|
b14c9f8d68 | ||
|
|
219d03cc11 | ||
|
|
8b6f113052 | ||
|
|
d09c4d6ce8 | ||
|
|
6d01e78711 | ||
|
|
13b03a0a1a | ||
|
|
00ee962429 | ||
|
|
8b2f231e2f | ||
|
|
7e05feb90b | ||
|
|
c09292672a | ||
|
|
a7d704cd10 | ||
|
|
39f1152e16 | ||
|
|
21625d69e0 | ||
|
|
ca202ef304 | ||
|
|
52eb59766b | ||
|
|
827e85547e | ||
|
|
3c07a2d9b3 | ||
|
|
661f318eb7 | ||
|
|
2c69abe268 | ||
|
|
c1784f0fdf | ||
|
|
390f2c129b | ||
|
|
b6e1b52a44 | ||
|
|
db28ff7ba1 | ||
|
|
9998400117 | ||
|
|
512a394b30 | ||
|
|
3fbbd933cf | ||
|
|
d6d86737c8 | ||
|
|
f61cc88a71 | ||
|
|
b6ef12b077 | ||
|
|
896b144a7d | ||
|
|
c7c8d3c039 | ||
|
|
25a8050df8 | ||
|
|
b998190f9e | ||
|
|
0242ed35ab | ||
|
|
2d4be07560 | ||
|
|
ef85173df5 | ||
|
|
b0b3514fa7 | ||
|
|
1f65d4b353 | ||
|
|
1f2bb62657 | ||
|
|
c31092ce7f | ||
|
|
c253e51e3d | ||
|
|
63c6830429 | ||
|
|
7a9879eb54 | ||
|
|
f155c01cf5 | ||
|
|
b831942501 | ||
|
|
d21ff08176 | ||
|
|
058801bdfc | ||
|
|
2e846844e7 | ||
|
|
7bb630a4b1 | ||
|
|
c6cf0c718c | ||
|
|
b45a37febb | ||
|
|
69010d1502 | ||
|
|
8cbcd7739c | ||
|
|
3971ecd375 | ||
|
|
543f5cd49c | ||
|
|
3e15751fd1 | ||
|
|
e52befb58e | ||
|
|
51c35a9bcf | ||
|
|
33feb3763f | ||
|
|
ef7ade8c2c | ||
|
|
92d2c13285 | ||
|
|
c9cacc7467 | ||
|
|
6fea7e382d | ||
|
|
f5370057bd | ||
|
|
5f8747a0f4 | ||
|
|
86f652fa9f | ||
|
|
f7815f6e49 | ||
|
|
ab1487025f | ||
|
|
57687cf70f | ||
|
|
4f4a2d63db | ||
|
|
27674bf8ff | ||
|
|
3346fba915 | ||
|
|
4441a0da41 | ||
|
|
23d17b9042 | ||
|
|
7a7774d8af | ||
|
|
8c37212645 | ||
|
|
a45986ff97 | ||
|
|
0d56c7a902 | ||
|
|
69abd0391f | ||
|
|
97585a30ad | ||
|
|
c812805b31 | ||
|
|
ba7298de3f | ||
|
|
4b6623cf44 | ||
|
|
53083f4290 | ||
|
|
d51a5b0122 | ||
|
|
da717f4472 | ||
|
|
1a89b3a486 | ||
|
|
e8480a2b2c | ||
|
|
c221bc9126 | ||
|
|
a1c749e6a0 | ||
|
|
88bdff6ce9 | ||
|
|
4fa73efc1e | ||
|
|
c89d47bb07 | ||
|
|
1610bcfbcf | ||
|
|
6f66fd3f9d | ||
|
|
b37d9b11cb | ||
|
|
6e80c12365 | ||
|
|
d779f31ccd | ||
|
|
39175a0c2a | ||
|
|
5118fd5008 | ||
|
|
ec7323159c | ||
|
|
1df7fbeeff | ||
|
|
8d633db82b | ||
|
|
c091e274ee | ||
|
|
2f95a9094a | ||
|
|
814cfef769 | ||
|
|
683aa93b44 | ||
|
|
749d6ceed4 | ||
|
|
cb53708767 | ||
|
|
541fda7580 | ||
|
|
b971bdedda | ||
|
|
7b6e13bcb9 | ||
|
|
a0fcf717c8 | ||
|
|
bce9f5d639 | ||
|
|
2338174244 | ||
|
|
137b410196 | ||
|
|
821d22261e | ||
|
|
52829af6d8 | ||
|
|
c8cc190781 | ||
|
|
73e73d7c8c | ||
|
|
edc4adc32c | ||
|
|
69833e5162 | ||
|
|
55b8f7a575 | ||
|
|
f2c74a4b98 | ||
|
|
d49c0b0b0d | ||
|
|
67f2cea200 | ||
|
|
ba3ef9c84c | ||
|
|
9d49a60f48 | ||
|
|
f00e8a8292 | ||
|
|
55598e4d4f | ||
|
|
bfda59f80b | ||
|
|
7d590491bd | ||
|
|
42514e7c47 | ||
|
|
7211d98c1e | ||
|
|
6841763ae7 | ||
|
|
849f330441 | ||
|
|
e9c9618ef4 | ||
|
|
07f2e553a7 | ||
|
|
8e3bef80a4 | ||
|
|
e1ab3be3cc | ||
|
|
87548f3d1d | ||
|
|
041060e5ca | ||
|
|
031ae638ce | ||
|
|
49cc4e05d9 | ||
|
|
1e56289f19 | ||
|
|
d143d3230a | ||
|
|
635bd8104e | ||
|
|
7d6a57661b | ||
|
|
eb8803f9eb | ||
|
|
068fbbc2be | ||
|
|
6c6a193551 | ||
|
|
d04c5acdb8 | ||
|
|
dfd82439de | ||
|
|
dc981e6abc | ||
|
|
46cbfb4831 | ||
|
|
8c4433ee82 | ||
|
|
6b43a4ed10 | ||
|
|
3a6e8d448f | ||
|
|
e9d3923829 | ||
|
|
bba5797e72 | ||
|
|
c0f5645ab4 | ||
|
|
7c48792511 | ||
|
|
bcb4925c6d | ||
|
|
96bcc84447 | ||
|
|
86619c7334 | ||
|
|
a8583dd818 | ||
|
|
fb679470db | ||
|
|
04a32fb443 | ||
|
|
70a588ceee | ||
|
|
3163e801b9 | ||
|
|
5a88b4f818 | ||
|
|
cc8be5b780 | ||
|
|
b8394bf14d | ||
|
|
ed5a7f31e2 | ||
|
|
bb1ced0bd7 | ||
|
|
566f100480 | ||
|
|
df9d2aaa37 | ||
|
|
aa0cc06e5e | ||
|
|
1fc640e88f | ||
|
|
92029393f6 | ||
|
|
1d394e00f6 | ||
|
|
e54c4bc1b3 | ||
|
|
af91dcda33 | ||
|
|
b7f2f9e409 | ||
|
|
affead11a5 | ||
|
|
4a1ac94799 | ||
|
|
4b366a143d | ||
|
|
6d55a4052a | ||
|
|
945d86fe7d | ||
|
|
36ad8b672c | ||
|
|
f32cb40929 | ||
|
|
a1e1aa30b5 | ||
|
|
35314c8771 | ||
|
|
3ee1a576f4 | ||
|
|
471db6ab27 | ||
|
|
7df272b349 | ||
|
|
13a5cbf9d5 | ||
|
|
e4c62180d7 | ||
|
|
b15f5f6f43 | ||
|
|
d616362884 | ||
|
|
bc19c76326 | ||
|
|
3bb6596507 | ||
|
|
326996139e | ||
|
|
5d9f77f5bf | ||
|
|
342b94c76b | ||
|
|
48044a853f | ||
|
|
364796d88c | ||
|
|
30ae5d6313 | ||
|
|
39c25cd506 | ||
|
|
12b9606b9c | ||
|
|
044256c2f7 | ||
|
|
65e50a9e5a | ||
|
|
e5607bb528 | ||
|
|
dfb836170b | ||
|
|
dbac57c27b | ||
|
|
683f21a4d5 | ||
|
|
85b95750e2 | ||
|
|
931257599d | ||
|
|
f9d4c8b6aa | ||
|
|
60887521b6 | ||
|
|
18c75f11ab | ||
|
|
a8bf8362da | ||
|
|
67f5fbc94c | ||
|
|
05339414cc | ||
|
|
927febe4e8 | ||
|
|
3e1562445a | ||
|
|
4fe3e1d6e3 | ||
|
|
abd48980ee | ||
|
|
8dbee41977 | ||
|
|
35bf7f63ea | ||
|
|
ce875c902b | ||
|
|
5bb8e3a841 | ||
|
|
4aa51bd3d4 | ||
|
|
93e2a132b5 | ||
|
|
291fa71914 | ||
|
|
636a037bb1 | ||
|
|
2540ab30fb | ||
|
|
6708878f4c | ||
|
|
68b57b2ce4 | ||
|
|
c38cb324f6 | ||
|
|
bda22db9d5 | ||
|
|
894b44d034 | ||
|
|
06f6334679 | ||
|
|
d230603190 | ||
|
|
1398a32b3a | ||
|
|
3ef34b750c | ||
|
|
9b6fb37533 | ||
|
|
39920186b3 | ||
|
|
57de78f4f1 | ||
|
|
9846ad2d17 | ||
|
|
2ac2d1213d | ||
|
|
a86d0b7270 | ||
|
|
6feb0f802f | ||
|
|
2d3f498e75 | ||
|
|
57b4e73627 | ||
|
|
1b6cf85251 | ||
|
|
2042876fcf | ||
|
|
3148662234 | ||
|
|
c4c6e25bc0 | ||
|
|
1239b7648d | ||
|
|
dcbd0f3d0c | ||
|
|
68747b4174 | ||
|
|
ec49fcc638 | ||
|
|
51b16c52b3 | ||
|
|
3f55cda9d1 | ||
|
|
3908552fb9 | ||
|
|
b893aa17ac | ||
|
|
2eb25d2298 | ||
|
|
b1d981e916 | ||
|
|
990401395c | ||
|
|
122d2b161d | ||
|
|
d2f47cfa2d | ||
|
|
21b0384f28 | ||
|
|
bf03be0b9d | ||
|
|
d72929f64e | ||
|
|
766c902d8c | ||
|
|
ca5efdcebe | ||
|
|
6410bdf998 | ||
|
|
34aa52b0f7 | ||
|
|
ba2c9c7bf0 | ||
|
|
964fad5a67 | ||
|
|
05183c5348 | ||
|
|
053698eefb | ||
|
|
40ec989184 | ||
|
|
dcca363d1b | ||
|
|
9af0c7dd30 | ||
|
|
1a481bc741 | ||
|
|
aa5d634c04 | ||
|
|
7960f69ba9 | ||
|
|
f111008d66 | ||
|
|
c643f0b1fa | ||
|
|
41834bc4b2 | ||
|
|
f51a383ef1 | ||
|
|
59765c5830 | ||
|
|
8fc026bb61 | ||
|
|
176c77d602 | ||
|
|
a6528a7876 | ||
|
|
9e5f70f126 | ||
|
|
19efad5ba6 | ||
|
|
0e877810f7 | ||
|
|
2bfce59d8e | ||
|
|
d67109d5d3 | ||
|
|
7b1cd7f211 | ||
|
|
1d4710a39e | ||
|
|
1027dda432 | ||
|
|
09531d0810 | ||
|
|
7c35f2b8b0 | ||
|
|
451b2c30ad | ||
|
|
6df2c003c7 | ||
|
|
c618240c91 | ||
|
|
e72660056e | ||
|
|
2090e76794 | ||
|
|
806d34b39a | ||
|
|
d438f16628 | ||
|
|
bafb273a6c | ||
|
|
647f83b53b | ||
|
|
ae69eb01b3 | ||
|
|
3faf8c526a | ||
|
|
021077b56b | ||
|
|
b965c58ac1 | ||
|
|
f2b1b0632d | ||
|
|
a06216f24b | ||
|
|
c7252a43bf | ||
|
|
134b53e812 | ||
|
|
bbc7b44f88 | ||
|
|
02f503c74d | ||
|
|
b1f392ac99 | ||
|
|
9aceb63e7e | ||
|
|
bbbd397b80 | ||
|
|
e6ad371dd7 | ||
|
|
03952704b7 | ||
|
|
13a822f994 | ||
|
|
e97f0df3df | ||
|
|
6402b74741 | ||
|
|
cb0c8f1e07 | ||
|
|
cf7d7e823b | ||
|
|
b11ea2db25 | ||
|
|
e282bc095a | ||
|
|
9c94216a87 | ||
|
|
be1961124c | ||
|
|
5ca9341aeb | ||
|
|
8ff6ce741a | ||
|
|
c9677d8bd2 | ||
|
|
732af319fe | ||
|
|
e9dee9de16 | ||
|
|
fd3774118a | ||
|
|
5cf6acd21a | ||
|
|
d4903f19fe | ||
|
|
8f6a5737c0 | ||
|
|
97d58c7954 | ||
|
|
33dd0de661 | ||
|
|
bb73258ce9 | ||
|
|
669853de99 | ||
|
|
750131f0a1 | ||
|
|
1434ad7b28 | ||
|
|
56c75c0c8a | ||
|
|
d95a9b0b29 | ||
|
|
a98e84e09a | ||
|
|
72a386ec34 | ||
|
|
f1903133ed | ||
|
|
d228257da6 | ||
|
|
c9fbf490dc | ||
|
|
9a3743c7c4 | ||
|
|
f835556ffb | ||
|
|
25375ee819 | ||
|
|
9c3f6fb92f | ||
|
|
2321ead995 | ||
|
|
c506c48c91 | ||
|
|
e24fcfdc5c | ||
|
|
67003a471b | ||
|
|
da373758e5 | ||
|
|
ececc2b60b | ||
|
|
506ef75e3c | ||
|
|
5d49abcf62 | ||
|
|
d0b589749a | ||
|
|
710059b779 | ||
|
|
b4d4331926 | ||
|
|
b82a2f60f7 | ||
|
|
7415d5ff3c | ||
|
|
bc77a837df | ||
|
|
8445bb4bb7 | ||
|
|
75533e5e97 | ||
|
|
774c4375ee | ||
|
|
f84e4449a5 | ||
|
|
5b40589bc0 | ||
|
|
316b65514c | ||
|
|
4eac6417c3 | ||
|
|
a0d56f3451 | ||
|
|
0a3d848cbb | ||
|
|
007b55a843 | ||
|
|
ca289581a8 | ||
|
|
5fa9d9f438 | ||
|
|
38767db686 | ||
|
|
86146ddad7 | ||
|
|
ab33ff017b | ||
|
|
fc1bdf2148 | ||
|
|
a66c21fe11 | ||
|
|
1e5d4b40e7 | ||
|
|
02eb93d5d2 | ||
|
|
0b8b52cb41 | ||
|
|
8e0af61ba4 | ||
|
|
8e350f6b6a | ||
|
|
b554e93f72 | ||
|
|
45111d6576 |
120
.github/workflows/ci.yml
vendored
Normal file
120
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
name: CI checks
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.37.0
|
||||||
|
override: true
|
||||||
|
|
||||||
|
# cargo fmt does not build the code, and running it in a fresh clone of
|
||||||
|
# the codebase will fail because the protobuf code has not been generated.
|
||||||
|
- name: cargo build
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --all
|
||||||
|
|
||||||
|
# Ensure all code has been formatted with rustfmt
|
||||||
|
- run: rustup component add rustfmt
|
||||||
|
- name: Check formatting
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check --color always
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: Test on ${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.37.0
|
||||||
|
override: true
|
||||||
|
- name: cargo fetch
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fetch
|
||||||
|
- name: Build tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --verbose --release --all --tests
|
||||||
|
- name: Run tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --verbose --release --all
|
||||||
|
- name: Run slow tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --verbose --release --all -- --ignored
|
||||||
|
|
||||||
|
codecov:
|
||||||
|
name: Code coverage
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
# Use stable for this to ensure that cargo-tarpaulin can be built.
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
- name: Install cargo-tarpaulin
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: install
|
||||||
|
args: cargo-tarpaulin
|
||||||
|
- name: Generate coverage report
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: tarpaulin
|
||||||
|
args: --release --timeout 600 --out Xml --packages "librustzcash,zcash_client_backend,zcash_primitives,zcash_proofs"
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v1.0.3
|
||||||
|
with:
|
||||||
|
token: ${{secrets.CODECOV_TOKEN}}
|
||||||
|
|
||||||
|
doc-links:
|
||||||
|
name: Nightly lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
- name: cargo fetch
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fetch
|
||||||
|
|
||||||
|
# Ensure intra-documentation links all resolve correctly
|
||||||
|
# Requires #![deny(intra_doc_link_resolution_failure)] in crates.
|
||||||
|
- name: Check intra-doc links
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: doc
|
||||||
|
args: --all --document-private-items
|
||||||
|
|
||||||
|
# Build benchmarks to prevent bitrot
|
||||||
|
- name: Build benchmarks
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --verbose --all --benches
|
||||||
66
.gitlab-ci.yml
Normal file
66
.gitlab-ci.yml
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
# /************************************************************************
|
||||||
|
# File: .gitlab-ci.yml
|
||||||
|
# Author: mdr0id
|
||||||
|
# Date: 9/10/2018
|
||||||
|
# Description: Used to setup runners/jobs for librustzcash
|
||||||
|
# Usage: Commit source and the pipeline will trigger the according jobs.
|
||||||
|
# For now the build and test are done in the same jobs.
|
||||||
|
#
|
||||||
|
# Known bugs/missing features:
|
||||||
|
#
|
||||||
|
# ************************************************************************/
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
rust-latest:
|
||||||
|
stage: build
|
||||||
|
image: rust:latest
|
||||||
|
script:
|
||||||
|
- cargo --verbose --version
|
||||||
|
- time cargo build --verbose
|
||||||
|
|
||||||
|
rust-nightly:
|
||||||
|
stage: build
|
||||||
|
image: rustlang/rust:nightly
|
||||||
|
script:
|
||||||
|
- cargo --verbose --version
|
||||||
|
- cargo build --verbose
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
librustzcash-test-latest:
|
||||||
|
stage: test
|
||||||
|
image: rust:latest
|
||||||
|
script:
|
||||||
|
- cargo --verbose --version
|
||||||
|
- time cargo test --release --verbose
|
||||||
|
|
||||||
|
librustzcash-test-rust-nightly:
|
||||||
|
stage: test
|
||||||
|
image: rustlang/rust:nightly
|
||||||
|
script:
|
||||||
|
- cargo --verbose --version
|
||||||
|
- cargo test --release --verbose
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
#used to manually deploy a given release
|
||||||
|
librustzcash-rust-rc:
|
||||||
|
stage: deploy
|
||||||
|
image: rust:latest
|
||||||
|
script:
|
||||||
|
- cargo --verbose --version
|
||||||
|
- time cargo build --release --verbose
|
||||||
|
when: manual
|
||||||
|
|
||||||
|
#used to manually deploy a given release
|
||||||
|
librustzcash-rust-nightly-rc:
|
||||||
|
stage: deploy
|
||||||
|
image: rustlang/rust:nightly
|
||||||
|
script:
|
||||||
|
- cargo --verbose --version
|
||||||
|
- cargo build --release --verbose
|
||||||
|
allow_failure: true
|
||||||
|
when: manual
|
||||||
19
.travis.yml
Normal file
19
.travis.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
language: rust
|
||||||
|
rust:
|
||||||
|
- 1.37.0
|
||||||
|
|
||||||
|
cache: cargo
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- rustup component add rustfmt
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cargo build --verbose --release --all
|
||||||
|
- cargo fmt --all -- --check
|
||||||
|
- cargo test --verbose --release --all
|
||||||
|
- cargo test --verbose --release --all -- --ignored
|
||||||
|
|
||||||
|
before_cache:
|
||||||
|
- rm -rf "$TRAVIS_HOME/.cargo/registry/src"
|
||||||
|
- cargo install cargo-update || echo "cargo-update already installed"
|
||||||
|
- cargo install-update -a # update outdated cached binaries
|
||||||
973
Cargo.lock
generated
973
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
46
Cargo.toml
46
Cargo.toml
@@ -1,37 +1,15 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "librustzcash"
|
members = [
|
||||||
version = "0.1.0"
|
"bellman",
|
||||||
authors = [
|
"ff",
|
||||||
"Sean Bowe <ewillbefull@gmail.com>",
|
"group",
|
||||||
"Jack Grigg <jack@z.cash>",
|
"librustzcash",
|
||||||
"Jay Graber <jay@z.cash>",
|
"pairing",
|
||||||
"Simon Liu <simon@z.cash>"
|
"zcash_client_backend",
|
||||||
]
|
"zcash_history",
|
||||||
|
"zcash_primitives",
|
||||||
[lib]
|
"zcash_proofs",
|
||||||
name = "rustzcash"
|
]
|
||||||
path = "src/rustzcash.rs"
|
|
||||||
crate-type = ["staticlib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libc = "0.2"
|
|
||||||
pairing = "0.14.2"
|
|
||||||
lazy_static = "1"
|
|
||||||
bellman = "0.1"
|
|
||||||
byteorder = "1"
|
|
||||||
rand = "0.4"
|
|
||||||
|
|
||||||
[dependencies.blake2-rfc]
|
|
||||||
git = "https://github.com/gtank/blake2-rfc"
|
|
||||||
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
|
|
||||||
|
|
||||||
[dependencies.sapling-crypto]
|
|
||||||
git = "https://github.com/zcash-hackworks/sapling-crypto"
|
|
||||||
rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e"
|
|
||||||
|
|
||||||
[dependencies.zip32]
|
|
||||||
git = "https://github.com/zcash-hackworks/zip32"
|
|
||||||
rev = "176470ef41583b5bd0bd749bd1b61d417aa8ec79"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2017 Zcash Company
|
Copyright (c) 2017-2019 Electric Coin Company
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -1,10 +1,15 @@
|
|||||||
# librustzcash
|
# Zcash Rust crates
|
||||||
|
|
||||||
This repository contains librustzcash, a static library for Zcash code assets written in Rust.
|
This repository contains a (work-in-progress) set of Rust crates for
|
||||||
|
working with Zcash.
|
||||||
|
|
||||||
|
## Security Warnings
|
||||||
|
|
||||||
|
These libraries are currently under development and have not been fully-reviewed.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
All code in this workspace is licensed under either of
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
@@ -17,4 +22,3 @@ Unless you explicitly state otherwise, any contribution intentionally
|
|||||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||||
license, shall be dual licensed as above, without any additional terms or
|
license, shall be dual licensed as above, without any additional terms or
|
||||||
conditions.
|
conditions.
|
||||||
|
|
||||||
|
|||||||
2
bellman/.gitignore
vendored
Normal file
2
bellman/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
14
bellman/COPYRIGHT
Normal file
14
bellman/COPYRIGHT
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
Copyrights in the "bellman" library are retained by their contributors. No
|
||||||
|
copyright assignment is required to contribute to the "bellman" library.
|
||||||
|
|
||||||
|
The "bellman" library is licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally
|
||||||
|
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||||
|
license, shall be dual licensed as above, without any additional terms or
|
||||||
|
conditions.
|
||||||
42
bellman/Cargo.toml
Normal file
42
bellman/Cargo.toml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||||
|
description = "zk-SNARK library"
|
||||||
|
readme = "README.md"
|
||||||
|
homepage = "https://github.com/ebfull/bellman"
|
||||||
|
license = "MIT/Apache-2.0"
|
||||||
|
name = "bellman"
|
||||||
|
repository = "https://github.com/ebfull/bellman"
|
||||||
|
version = "0.2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bit-vec = "0.4.4"
|
||||||
|
blake2s_simd = "0.5"
|
||||||
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
|
futures = "0.1"
|
||||||
|
futures-cpupool = { version = "0.1", optional = true }
|
||||||
|
group = { version = "0.2.0", path = "../group" }
|
||||||
|
num_cpus = { version = "1", optional = true }
|
||||||
|
crossbeam = { version = "0.7", optional = true }
|
||||||
|
pairing = { version = "0.15.0", path = "../pairing", optional = true }
|
||||||
|
rand_core = "0.5"
|
||||||
|
byteorder = "1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex-literal = "0.2"
|
||||||
|
rand = "0.7"
|
||||||
|
rand_xorshift = "0.2"
|
||||||
|
sha2 = "0.8"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
groth16 = ["pairing"]
|
||||||
|
multicore = ["futures-cpupool", "crossbeam", "num_cpus"]
|
||||||
|
default = ["groth16", "multicore"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "mimc"
|
||||||
|
path = "tests/mimc.rs"
|
||||||
|
required-features = ["groth16"]
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
201
bellman/LICENSE-APACHE
Normal file
201
bellman/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
23
bellman/LICENSE-MIT
Normal file
23
bellman/LICENSE-MIT
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Permission is hereby granted, free of charge, to any
|
||||||
|
person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without
|
||||||
|
limitation the rights to use, copy, modify, merge,
|
||||||
|
publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software
|
||||||
|
is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice
|
||||||
|
shall be included in all copies or substantial portions
|
||||||
|
of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
30
bellman/README.md
Normal file
30
bellman/README.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# bellman [](https://crates.io/crates/bellman) #
|
||||||
|
|
||||||
|
`bellman` is a crate for building zk-SNARK circuits. It provides circuit traits
|
||||||
|
and primitive structures, as well as basic gadget implementations such as
|
||||||
|
booleans and number abstractions.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
`bellman` is being refactored into a generic proving library. Currently it is
|
||||||
|
pairing-specific, and different types of proving systems need to be implemented
|
||||||
|
as sub-modules. After the refactor, `bellman` will be generic using the `ff` and
|
||||||
|
`group` crates, while specific proving systems will be separate crates that pull
|
||||||
|
in the dependencies they require.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
### Contribution
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally
|
||||||
|
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||||
|
license, shall be dual licensed as above, without any additional terms or
|
||||||
|
conditions.
|
||||||
503
bellman/src/domain.rs
Normal file
503
bellman/src/domain.rs
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
//! This module contains an [`EvaluationDomain`] abstraction for performing
|
||||||
|
//! various kinds of polynomial arithmetic on top of the scalar field.
|
||||||
|
//!
|
||||||
|
//! In pairing-based SNARKs like [Groth16], we need to calculate a quotient
|
||||||
|
//! polynomial over a target polynomial with roots at distinct points associated
|
||||||
|
//! with each constraint of the constraint system. In order to be efficient, we
|
||||||
|
//! choose these roots to be the powers of a 2<sup>n</sup> root of unity in the
|
||||||
|
//! field. This allows us to perform polynomial operations in O(n) by performing
|
||||||
|
//! an O(n log n) FFT over such a domain.
|
||||||
|
//!
|
||||||
|
//! [`EvaluationDomain`]: crate::domain::EvaluationDomain
|
||||||
|
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||||
|
|
||||||
|
use ff::{Field, PrimeField, ScalarEngine};
|
||||||
|
use group::CurveProjective;
|
||||||
|
|
||||||
|
use super::SynthesisError;
|
||||||
|
|
||||||
|
use super::multicore::Worker;
|
||||||
|
|
||||||
|
pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
|
||||||
|
coeffs: Vec<G>,
|
||||||
|
exp: u32,
|
||||||
|
omega: E::Fr,
|
||||||
|
omegainv: E::Fr,
|
||||||
|
geninv: E::Fr,
|
||||||
|
minv: E::Fr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine, G: Group<E>> AsRef<[G]> for EvaluationDomain<E, G> {
|
||||||
|
fn as_ref(&self) -> &[G] {
|
||||||
|
&self.coeffs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine, G: Group<E>> AsMut<[G]> for EvaluationDomain<E, G> {
|
||||||
|
fn as_mut(&mut self) -> &mut [G] {
|
||||||
|
&mut self.coeffs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||||
|
pub fn into_coeffs(self) -> Vec<G> {
|
||||||
|
self.coeffs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError> {
|
||||||
|
// Compute the size of our evaluation domain
|
||||||
|
let mut m = 1;
|
||||||
|
let mut exp = 0;
|
||||||
|
while m < coeffs.len() {
|
||||||
|
m *= 2;
|
||||||
|
exp += 1;
|
||||||
|
|
||||||
|
// The pairing-friendly curve may not be able to support
|
||||||
|
// large enough (radix2) evaluation domains.
|
||||||
|
if exp >= E::Fr::S {
|
||||||
|
return Err(SynthesisError::PolynomialDegreeTooLarge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute omega, the 2^exp primitive root of unity
|
||||||
|
let mut omega = E::Fr::root_of_unity();
|
||||||
|
for _ in exp..E::Fr::S {
|
||||||
|
omega.square();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the coeffs vector with zeroes if necessary
|
||||||
|
coeffs.resize(m, G::group_zero());
|
||||||
|
|
||||||
|
Ok(EvaluationDomain {
|
||||||
|
coeffs,
|
||||||
|
exp,
|
||||||
|
omega,
|
||||||
|
omegainv: omega.inverse().unwrap(),
|
||||||
|
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
|
||||||
|
minv: E::Fr::from_str(&format!("{}", m))
|
||||||
|
.unwrap()
|
||||||
|
.inverse()
|
||||||
|
.unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fft(&mut self, worker: &Worker) {
|
||||||
|
best_fft(&mut self.coeffs, worker, &self.omega, self.exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ifft(&mut self, worker: &Worker) {
|
||||||
|
best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp);
|
||||||
|
|
||||||
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
|
let minv = self.minv;
|
||||||
|
|
||||||
|
for v in self.coeffs.chunks_mut(chunk) {
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
|
for v in v {
|
||||||
|
v.group_mul_assign(&minv);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) {
|
||||||
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
|
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
|
let mut u = g.pow(&[(i * chunk) as u64]);
|
||||||
|
for v in v.iter_mut() {
|
||||||
|
v.group_mul_assign(&u);
|
||||||
|
u.mul_assign(&g);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn coset_fft(&mut self, worker: &Worker) {
|
||||||
|
self.distribute_powers(worker, E::Fr::multiplicative_generator());
|
||||||
|
self.fft(worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn icoset_fft(&mut self, worker: &Worker) {
|
||||||
|
let geninv = self.geninv;
|
||||||
|
|
||||||
|
self.ifft(worker);
|
||||||
|
self.distribute_powers(worker, geninv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This evaluates t(tau) for this domain, which is
|
||||||
|
/// tau^m - 1 for these radix-2 domains.
|
||||||
|
pub fn z(&self, tau: &E::Fr) -> E::Fr {
|
||||||
|
let mut tmp = tau.pow(&[self.coeffs.len() as u64]);
|
||||||
|
tmp.sub_assign(&E::Fr::one());
|
||||||
|
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The target polynomial is the zero polynomial in our
|
||||||
|
/// evaluation domain, so we must perform division over
|
||||||
|
/// a coset.
|
||||||
|
pub fn divide_by_z_on_coset(&mut self, worker: &Worker) {
|
||||||
|
let i = self
|
||||||
|
.z(&E::Fr::multiplicative_generator())
|
||||||
|
.inverse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
|
for v in self.coeffs.chunks_mut(chunk) {
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
|
for v in v {
|
||||||
|
v.group_mul_assign(&i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform O(n) multiplication of two polynomials in the domain.
|
||||||
|
pub fn mul_assign(&mut self, worker: &Worker, other: &EvaluationDomain<E, Scalar<E>>) {
|
||||||
|
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
||||||
|
|
||||||
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
|
for (a, b) in self
|
||||||
|
.coeffs
|
||||||
|
.chunks_mut(chunk)
|
||||||
|
.zip(other.coeffs.chunks(chunk))
|
||||||
|
{
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
|
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||||
|
a.group_mul_assign(&b.0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform O(n) subtraction of one polynomial from another in the domain.
|
||||||
|
pub fn sub_assign(&mut self, worker: &Worker, other: &EvaluationDomain<E, G>) {
|
||||||
|
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
||||||
|
|
||||||
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
|
for (a, b) in self
|
||||||
|
.coeffs
|
||||||
|
.chunks_mut(chunk)
|
||||||
|
.zip(other.coeffs.chunks(chunk))
|
||||||
|
{
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
|
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||||
|
a.group_sub_assign(&b);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Group<E: ScalarEngine>: Sized + Copy + Clone + Send + Sync {
|
||||||
|
fn group_zero() -> Self;
|
||||||
|
fn group_mul_assign(&mut self, by: &E::Fr);
|
||||||
|
fn group_add_assign(&mut self, other: &Self);
|
||||||
|
fn group_sub_assign(&mut self, other: &Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Point<G: CurveProjective>(pub G);
|
||||||
|
|
||||||
|
impl<G: CurveProjective> PartialEq for Point<G> {
|
||||||
|
fn eq(&self, other: &Point<G>) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveProjective> Copy for Point<G> {}
|
||||||
|
|
||||||
|
impl<G: CurveProjective> Clone for Point<G> {
|
||||||
|
fn clone(&self) -> Point<G> {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveProjective> Group<G::Engine> for Point<G> {
|
||||||
|
fn group_zero() -> Self {
|
||||||
|
Point(G::zero())
|
||||||
|
}
|
||||||
|
fn group_mul_assign(&mut self, by: &G::Scalar) {
|
||||||
|
self.0.mul_assign(by.into_repr());
|
||||||
|
}
|
||||||
|
fn group_add_assign(&mut self, other: &Self) {
|
||||||
|
self.0.add_assign(&other.0);
|
||||||
|
}
|
||||||
|
fn group_sub_assign(&mut self, other: &Self) {
|
||||||
|
self.0.sub_assign(&other.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Scalar<E: ScalarEngine>(pub E::Fr);
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> PartialEq for Scalar<E> {
|
||||||
|
fn eq(&self, other: &Scalar<E>) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> Copy for Scalar<E> {}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> Clone for Scalar<E> {
|
||||||
|
fn clone(&self) -> Scalar<E> {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> Group<E> for Scalar<E> {
|
||||||
|
fn group_zero() -> Self {
|
||||||
|
Scalar(E::Fr::zero())
|
||||||
|
}
|
||||||
|
fn group_mul_assign(&mut self, by: &E::Fr) {
|
||||||
|
self.0.mul_assign(by);
|
||||||
|
}
|
||||||
|
fn group_add_assign(&mut self, other: &Self) {
|
||||||
|
self.0.add_assign(&other.0);
|
||||||
|
}
|
||||||
|
fn group_sub_assign(&mut self, other: &Self) {
|
||||||
|
self.0.sub_assign(&other.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) {
|
||||||
|
let log_cpus = worker.log_num_cpus();
|
||||||
|
|
||||||
|
if log_n <= log_cpus {
|
||||||
|
serial_fft(a, omega, log_n);
|
||||||
|
} else {
|
||||||
|
parallel_fft(a, worker, omega, log_n, log_cpus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32) {
|
||||||
|
fn bitreverse(mut n: u32, l: u32) -> u32 {
|
||||||
|
let mut r = 0;
|
||||||
|
for _ in 0..l {
|
||||||
|
r = (r << 1) | (n & 1);
|
||||||
|
n >>= 1;
|
||||||
|
}
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = a.len() as u32;
|
||||||
|
assert_eq!(n, 1 << log_n);
|
||||||
|
|
||||||
|
for k in 0..n {
|
||||||
|
let rk = bitreverse(k, log_n);
|
||||||
|
if k < rk {
|
||||||
|
a.swap(rk as usize, k as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut m = 1;
|
||||||
|
for _ in 0..log_n {
|
||||||
|
let w_m = omega.pow(&[u64::from(n / (2 * m))]);
|
||||||
|
|
||||||
|
let mut k = 0;
|
||||||
|
while k < n {
|
||||||
|
let mut w = E::Fr::one();
|
||||||
|
for j in 0..m {
|
||||||
|
let mut t = a[(k + j + m) as usize];
|
||||||
|
t.group_mul_assign(&w);
|
||||||
|
let mut tmp = a[(k + j) as usize];
|
||||||
|
tmp.group_sub_assign(&t);
|
||||||
|
a[(k + j + m) as usize] = tmp;
|
||||||
|
a[(k + j) as usize].group_add_assign(&t);
|
||||||
|
w.mul_assign(&w_m);
|
||||||
|
}
|
||||||
|
|
||||||
|
k += 2 * m;
|
||||||
|
}
|
||||||
|
|
||||||
|
m *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
||||||
|
a: &mut [T],
|
||||||
|
worker: &Worker,
|
||||||
|
omega: &E::Fr,
|
||||||
|
log_n: u32,
|
||||||
|
log_cpus: u32,
|
||||||
|
) {
|
||||||
|
assert!(log_n >= log_cpus);
|
||||||
|
|
||||||
|
let num_cpus = 1 << log_cpus;
|
||||||
|
let log_new_n = log_n - log_cpus;
|
||||||
|
let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus];
|
||||||
|
let new_omega = omega.pow(&[num_cpus as u64]);
|
||||||
|
|
||||||
|
worker.scope(0, |scope, _| {
|
||||||
|
let a = &*a;
|
||||||
|
|
||||||
|
for (j, tmp) in tmp.iter_mut().enumerate() {
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
|
// Shuffle into a sub-FFT
|
||||||
|
let omega_j = omega.pow(&[j as u64]);
|
||||||
|
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
|
||||||
|
|
||||||
|
let mut elt = E::Fr::one();
|
||||||
|
for (i, tmp) in tmp.iter_mut().enumerate() {
|
||||||
|
for s in 0..num_cpus {
|
||||||
|
let idx = (i + (s << log_new_n)) % (1 << log_n);
|
||||||
|
let mut t = a[idx];
|
||||||
|
t.group_mul_assign(&elt);
|
||||||
|
tmp.group_add_assign(&t);
|
||||||
|
elt.mul_assign(&omega_step);
|
||||||
|
}
|
||||||
|
elt.mul_assign(&omega_j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform sub-FFT
|
||||||
|
serial_fft(tmp, &new_omega, log_new_n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: does this hurt or help?
|
||||||
|
worker.scope(a.len(), |scope, chunk| {
|
||||||
|
let tmp = &tmp;
|
||||||
|
|
||||||
|
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
|
let mut idx = idx * chunk;
|
||||||
|
let mask = (1 << log_cpus) - 1;
|
||||||
|
for a in a {
|
||||||
|
*a = tmp[idx & mask][idx >> log_cpus];
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test multiplying various (low degree) polynomials together and
|
||||||
|
// comparing with naive evaluations.
|
||||||
|
#[cfg(feature = "pairing")]
|
||||||
|
#[test]
|
||||||
|
fn polynomial_arith() {
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
fn test_mul<E: ScalarEngine, R: RngCore>(rng: &mut R) {
|
||||||
|
let worker = Worker::new();
|
||||||
|
|
||||||
|
for coeffs_a in 0..70 {
|
||||||
|
for coeffs_b in 0..70 {
|
||||||
|
let mut a: Vec<_> = (0..coeffs_a)
|
||||||
|
.map(|_| Scalar::<E>(E::Fr::random(rng)))
|
||||||
|
.collect();
|
||||||
|
let mut b: Vec<_> = (0..coeffs_b)
|
||||||
|
.map(|_| Scalar::<E>(E::Fr::random(rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// naive evaluation
|
||||||
|
let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b];
|
||||||
|
for (i1, a) in a.iter().enumerate() {
|
||||||
|
for (i2, b) in b.iter().enumerate() {
|
||||||
|
let mut prod = *a;
|
||||||
|
prod.group_mul_assign(&b.0);
|
||||||
|
naive[i1 + i2].group_add_assign(&prod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.resize(coeffs_a + coeffs_b, Scalar(E::Fr::zero()));
|
||||||
|
b.resize(coeffs_a + coeffs_b, Scalar(E::Fr::zero()));
|
||||||
|
|
||||||
|
let mut a = EvaluationDomain::from_coeffs(a).unwrap();
|
||||||
|
let mut b = EvaluationDomain::from_coeffs(b).unwrap();
|
||||||
|
|
||||||
|
a.fft(&worker);
|
||||||
|
b.fft(&worker);
|
||||||
|
a.mul_assign(&worker, &b);
|
||||||
|
a.ifft(&worker);
|
||||||
|
|
||||||
|
for (naive, fft) in naive.iter().zip(a.coeffs.iter()) {
|
||||||
|
assert!(naive == fft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rng = &mut rand::thread_rng();
|
||||||
|
|
||||||
|
test_mul::<Bls12, _>(rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "pairing")]
|
||||||
|
#[test]
|
||||||
|
fn fft_composition() {
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
fn test_comp<E: ScalarEngine, R: RngCore>(rng: &mut R) {
|
||||||
|
let worker = Worker::new();
|
||||||
|
|
||||||
|
for coeffs in 0..10 {
|
||||||
|
let coeffs = 1 << coeffs;
|
||||||
|
|
||||||
|
let mut v = vec![];
|
||||||
|
for _ in 0..coeffs {
|
||||||
|
v.push(Scalar::<E>(E::Fr::random(rng)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
|
||||||
|
domain.ifft(&worker);
|
||||||
|
domain.fft(&worker);
|
||||||
|
assert!(v == domain.coeffs);
|
||||||
|
domain.fft(&worker);
|
||||||
|
domain.ifft(&worker);
|
||||||
|
assert!(v == domain.coeffs);
|
||||||
|
domain.icoset_fft(&worker);
|
||||||
|
domain.coset_fft(&worker);
|
||||||
|
assert!(v == domain.coeffs);
|
||||||
|
domain.coset_fft(&worker);
|
||||||
|
domain.icoset_fft(&worker);
|
||||||
|
assert!(v == domain.coeffs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rng = &mut rand::thread_rng();
|
||||||
|
|
||||||
|
test_comp::<Bls12, _>(rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "pairing")]
|
||||||
|
#[test]
|
||||||
|
fn parallel_fft_consistency() {
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand_core::RngCore;
|
||||||
|
use std::cmp::min;
|
||||||
|
|
||||||
|
fn test_consistency<E: ScalarEngine, R: RngCore>(rng: &mut R) {
|
||||||
|
let worker = Worker::new();
|
||||||
|
|
||||||
|
for _ in 0..5 {
|
||||||
|
for log_d in 0..10 {
|
||||||
|
let d = 1 << log_d;
|
||||||
|
|
||||||
|
let v1 = (0..d)
|
||||||
|
.map(|_| Scalar::<E>(E::Fr::random(rng)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap();
|
||||||
|
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();
|
||||||
|
|
||||||
|
for log_cpus in log_d..min(log_d + 1, 3) {
|
||||||
|
parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus);
|
||||||
|
serial_fft(&mut v2.coeffs, &v2.omega, log_d);
|
||||||
|
|
||||||
|
assert!(v1.coeffs == v2.coeffs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rng = &mut rand::thread_rng();
|
||||||
|
|
||||||
|
test_consistency::<Bls12, _>(rng);
|
||||||
|
}
|
||||||
33
bellman/src/gadgets.rs
Normal file
33
bellman/src/gadgets.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//! Self-contained sub-circuit implementations for various primitives.
|
||||||
|
|
||||||
|
pub mod test;
|
||||||
|
|
||||||
|
pub mod blake2s;
|
||||||
|
pub mod boolean;
|
||||||
|
pub mod lookup;
|
||||||
|
pub mod multieq;
|
||||||
|
pub mod multipack;
|
||||||
|
pub mod num;
|
||||||
|
pub mod sha256;
|
||||||
|
pub mod uint32;
|
||||||
|
|
||||||
|
use crate::SynthesisError;
|
||||||
|
|
||||||
|
// TODO: This should probably be removed and we
|
||||||
|
// should use existing helper methods on `Option`
|
||||||
|
// for mapping with an error.
|
||||||
|
/// This basically is just an extension to `Option`
|
||||||
|
/// which allows for a convenient mapping to an
|
||||||
|
/// error on `None`.
|
||||||
|
pub trait Assignment<T> {
|
||||||
|
fn get(&self) -> Result<&T, SynthesisError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Assignment<T> for Option<T> {
|
||||||
|
fn get(&self) -> Result<&T, SynthesisError> {
|
||||||
|
match *self {
|
||||||
|
Some(ref v) => Ok(v),
|
||||||
|
None => Err(SynthesisError::AssignmentMissing),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
697
bellman/src/gadgets/blake2s.rs
Normal file
697
bellman/src/gadgets/blake2s.rs
Normal file
@@ -0,0 +1,697 @@
|
|||||||
|
//! The [BLAKE2s] hash function with personalization support.
|
||||||
|
//!
|
||||||
|
//! [BLAKE2s]: https://tools.ietf.org/html/rfc7693
|
||||||
|
|
||||||
|
use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32};
|
||||||
|
use crate::{ConstraintSystem, SynthesisError};
|
||||||
|
use ff::ScalarEngine;
|
||||||
|
|
||||||
|
/*
|
||||||
|
2.1. Parameters
|
||||||
|
The following table summarizes various parameters and their ranges:
|
||||||
|
| BLAKE2b | BLAKE2s |
|
||||||
|
--------------+------------------+------------------+
|
||||||
|
Bits in word | w = 64 | w = 32 |
|
||||||
|
Rounds in F | r = 12 | r = 10 |
|
||||||
|
Block bytes | bb = 128 | bb = 64 |
|
||||||
|
Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 |
|
||||||
|
Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 |
|
||||||
|
Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 |
|
||||||
|
--------------+------------------+------------------+
|
||||||
|
G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) |
|
||||||
|
constants = | (32, 24, 16, 63) | (16, 12, 8, 7) |
|
||||||
|
--------------+------------------+------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
const R1: usize = 16;
|
||||||
|
const R2: usize = 12;
|
||||||
|
const R3: usize = 8;
|
||||||
|
const R4: usize = 7;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
||||||
|
----------+-------------------------------------------------+
|
||||||
|
SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
||||||
|
SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 |
|
||||||
|
SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 |
|
||||||
|
SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 |
|
||||||
|
SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 |
|
||||||
|
SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 |
|
||||||
|
SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 |
|
||||||
|
SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 |
|
||||||
|
SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 |
|
||||||
|
SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 |
|
||||||
|
----------+-------------------------------------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SIGMA: [[usize; 16]; 10] = [
|
||||||
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||||
|
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
|
||||||
|
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
|
||||||
|
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
|
||||||
|
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
|
||||||
|
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
|
||||||
|
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
|
||||||
|
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
|
||||||
|
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
|
||||||
|
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
3.1. Mixing Function G
|
||||||
|
The G primitive function mixes two input words, "x" and "y", into
|
||||||
|
four words indexed by "a", "b", "c", and "d" in the working vector
|
||||||
|
v[0..15]. The full modified vector is returned. The rotation
|
||||||
|
constants (R1, R2, R3, R4) are given in Section 2.1.
|
||||||
|
FUNCTION G( v[0..15], a, b, c, d, x, y )
|
||||||
|
|
|
||||||
|
| v[a] := (v[a] + v[b] + x) mod 2**w
|
||||||
|
| v[d] := (v[d] ^ v[a]) >>> R1
|
||||||
|
| v[c] := (v[c] + v[d]) mod 2**w
|
||||||
|
| v[b] := (v[b] ^ v[c]) >>> R2
|
||||||
|
| v[a] := (v[a] + v[b] + y) mod 2**w
|
||||||
|
| v[d] := (v[d] ^ v[a]) >>> R3
|
||||||
|
| v[c] := (v[c] + v[d]) mod 2**w
|
||||||
|
| v[b] := (v[b] ^ v[c]) >>> R4
|
||||||
|
|
|
||||||
|
| RETURN v[0..15]
|
||||||
|
|
|
||||||
|
END FUNCTION.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn mixing_g<E: ScalarEngine, CS: ConstraintSystem<E>, M>(
|
||||||
|
mut cs: M,
|
||||||
|
v: &mut [UInt32],
|
||||||
|
a: usize,
|
||||||
|
b: usize,
|
||||||
|
c: usize,
|
||||||
|
d: usize,
|
||||||
|
x: &UInt32,
|
||||||
|
y: &UInt32,
|
||||||
|
) -> Result<(), SynthesisError>
|
||||||
|
where
|
||||||
|
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
||||||
|
{
|
||||||
|
v[a] = UInt32::addmany(
|
||||||
|
cs.namespace(|| "mixing step 1"),
|
||||||
|
&[v[a].clone(), v[b].clone(), x.clone()],
|
||||||
|
)?;
|
||||||
|
v[d] = v[d].xor(cs.namespace(|| "mixing step 2"), &v[a])?.rotr(R1);
|
||||||
|
v[c] = UInt32::addmany(
|
||||||
|
cs.namespace(|| "mixing step 3"),
|
||||||
|
&[v[c].clone(), v[d].clone()],
|
||||||
|
)?;
|
||||||
|
v[b] = v[b].xor(cs.namespace(|| "mixing step 4"), &v[c])?.rotr(R2);
|
||||||
|
v[a] = UInt32::addmany(
|
||||||
|
cs.namespace(|| "mixing step 5"),
|
||||||
|
&[v[a].clone(), v[b].clone(), y.clone()],
|
||||||
|
)?;
|
||||||
|
v[d] = v[d].xor(cs.namespace(|| "mixing step 6"), &v[a])?.rotr(R3);
|
||||||
|
v[c] = UInt32::addmany(
|
||||||
|
cs.namespace(|| "mixing step 7"),
|
||||||
|
&[v[c].clone(), v[d].clone()],
|
||||||
|
)?;
|
||||||
|
v[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
3.2. Compression Function F
|
||||||
|
Compression function F takes as an argument the state vector "h",
|
||||||
|
message block vector "m" (last block is padded with zeros to full
|
||||||
|
block size, if required), 2w-bit offset counter "t", and final block
|
||||||
|
indicator flag "f". Local vector v[0..15] is used in processing. F
|
||||||
|
returns a new state vector. The number of rounds, "r", is 12 for
|
||||||
|
BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1.
|
||||||
|
FUNCTION F( h[0..7], m[0..15], t, f )
|
||||||
|
|
|
||||||
|
| // Initialize local work vector v[0..15]
|
||||||
|
| v[0..7] := h[0..7] // First half from state.
|
||||||
|
| v[8..15] := IV[0..7] // Second half from IV.
|
||||||
|
|
|
||||||
|
| v[12] := v[12] ^ (t mod 2**w) // Low word of the offset.
|
||||||
|
| v[13] := v[13] ^ (t >> w) // High word.
|
||||||
|
|
|
||||||
|
| IF f = TRUE THEN // last block flag?
|
||||||
|
| | v[14] := v[14] ^ 0xFF..FF // Invert all bits.
|
||||||
|
| END IF.
|
||||||
|
|
|
||||||
|
| // Cryptographic mixing
|
||||||
|
| FOR i = 0 TO r - 1 DO // Ten or twelve rounds.
|
||||||
|
| |
|
||||||
|
| | // Message word selection permutation for this round.
|
||||||
|
| | s[0..15] := SIGMA[i mod 10][0..15]
|
||||||
|
| |
|
||||||
|
| | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] )
|
||||||
|
| | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] )
|
||||||
|
| | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] )
|
||||||
|
| | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] )
|
||||||
|
| |
|
||||||
|
| | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] )
|
||||||
|
| | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] )
|
||||||
|
| | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] )
|
||||||
|
| | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] )
|
||||||
|
| |
|
||||||
|
| END FOR
|
||||||
|
|
|
||||||
|
| FOR i = 0 TO 7 DO // XOR the two halves.
|
||||||
|
| | h[i] := h[i] ^ v[i] ^ v[i + 8]
|
||||||
|
| END FOR.
|
||||||
|
|
|
||||||
|
| RETURN h[0..7] // New state.
|
||||||
|
|
|
||||||
|
END FUNCTION.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn blake2s_compression<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||||
|
mut cs: CS,
|
||||||
|
h: &mut [UInt32],
|
||||||
|
m: &[UInt32],
|
||||||
|
t: u64,
|
||||||
|
f: bool,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
|
assert_eq!(h.len(), 8);
|
||||||
|
assert_eq!(m.len(), 16);
|
||||||
|
|
||||||
|
/*
|
||||||
|
static const uint32_t blake2s_iv[8] =
|
||||||
|
{
|
||||||
|
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
||||||
|
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
let mut v = Vec::with_capacity(16);
|
||||||
|
v.extend_from_slice(h);
|
||||||
|
v.push(UInt32::constant(0x6A09E667));
|
||||||
|
v.push(UInt32::constant(0xBB67AE85));
|
||||||
|
v.push(UInt32::constant(0x3C6EF372));
|
||||||
|
v.push(UInt32::constant(0xA54FF53A));
|
||||||
|
v.push(UInt32::constant(0x510E527F));
|
||||||
|
v.push(UInt32::constant(0x9B05688C));
|
||||||
|
v.push(UInt32::constant(0x1F83D9AB));
|
||||||
|
v.push(UInt32::constant(0x5BE0CD19));
|
||||||
|
|
||||||
|
assert_eq!(v.len(), 16);
|
||||||
|
|
||||||
|
v[12] = v[12].xor(cs.namespace(|| "first xor"), &UInt32::constant(t as u32))?;
|
||||||
|
v[13] = v[13].xor(
|
||||||
|
cs.namespace(|| "second xor"),
|
||||||
|
&UInt32::constant((t >> 32) as u32),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if f {
|
||||||
|
v[14] = v[14].xor(
|
||||||
|
cs.namespace(|| "third xor"),
|
||||||
|
&UInt32::constant(u32::max_value()),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cs = MultiEq::new(&mut cs);
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
let mut cs = cs.namespace(|| format!("round {}", i));
|
||||||
|
|
||||||
|
let s = SIGMA[i % 10];
|
||||||
|
|
||||||
|
mixing_g(
|
||||||
|
cs.namespace(|| "mixing invocation 1"),
|
||||||
|
&mut v,
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
8,
|
||||||
|
12,
|
||||||
|
&m[s[0]],
|
||||||
|
&m[s[1]],
|
||||||
|
)?;
|
||||||
|
mixing_g(
|
||||||
|
cs.namespace(|| "mixing invocation 2"),
|
||||||
|
&mut v,
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
9,
|
||||||
|
13,
|
||||||
|
&m[s[2]],
|
||||||
|
&m[s[3]],
|
||||||
|
)?;
|
||||||
|
mixing_g(
|
||||||
|
cs.namespace(|| "mixing invocation 3"),
|
||||||
|
&mut v,
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
10,
|
||||||
|
14,
|
||||||
|
&m[s[4]],
|
||||||
|
&m[s[5]],
|
||||||
|
)?;
|
||||||
|
mixing_g(
|
||||||
|
cs.namespace(|| "mixing invocation 4"),
|
||||||
|
&mut v,
|
||||||
|
3,
|
||||||
|
7,
|
||||||
|
11,
|
||||||
|
15,
|
||||||
|
&m[s[6]],
|
||||||
|
&m[s[7]],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
mixing_g(
|
||||||
|
cs.namespace(|| "mixing invocation 5"),
|
||||||
|
&mut v,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
15,
|
||||||
|
&m[s[8]],
|
||||||
|
&m[s[9]],
|
||||||
|
)?;
|
||||||
|
mixing_g(
|
||||||
|
cs.namespace(|| "mixing invocation 6"),
|
||||||
|
&mut v,
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
&m[s[10]],
|
||||||
|
&m[s[11]],
|
||||||
|
)?;
|
||||||
|
mixing_g(
|
||||||
|
cs.namespace(|| "mixing invocation 7"),
|
||||||
|
&mut v,
|
||||||
|
2,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
13,
|
||||||
|
&m[s[12]],
|
||||||
|
&m[s[13]],
|
||||||
|
)?;
|
||||||
|
mixing_g(
|
||||||
|
cs.namespace(|| "mixing invocation 8"),
|
||||||
|
&mut v,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
9,
|
||||||
|
14,
|
||||||
|
&m[s[14]],
|
||||||
|
&m[s[15]],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..8 {
|
||||||
|
let mut cs = cs.namespace(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i = i));
|
||||||
|
|
||||||
|
h[i] = h[i].xor(cs.namespace(|| "first xor"), &v[i])?;
|
||||||
|
h[i] = h[i].xor(cs.namespace(|| "second xor"), &v[i + 8])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
|
||||||
|
|
|
||||||
|
| h[0..7] := IV[0..7] // Initialization Vector.
|
||||||
|
|
|
||||||
|
| // Parameter block p[0]
|
||||||
|
| h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn
|
||||||
|
|
|
||||||
|
| // Process padded key and data blocks
|
||||||
|
| IF dd > 1 THEN
|
||||||
|
| | FOR i = 0 TO dd - 2 DO
|
||||||
|
| | | h := F( h, d[i], (i + 1) * bb, FALSE )
|
||||||
|
| | END FOR.
|
||||||
|
| END IF.
|
||||||
|
|
|
||||||
|
| // Final block.
|
||||||
|
| IF kk = 0 THEN
|
||||||
|
| | h := F( h, d[dd - 1], ll, TRUE )
|
||||||
|
| ELSE
|
||||||
|
| | h := F( h, d[dd - 1], ll + bb, TRUE )
|
||||||
|
| END IF.
|
||||||
|
|
|
||||||
|
| RETURN first "nn" bytes from little-endian word array h[].
|
||||||
|
|
|
||||||
|
END FUNCTION.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn blake2s<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||||
|
mut cs: CS,
|
||||||
|
input: &[Boolean],
|
||||||
|
personalization: &[u8],
|
||||||
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||||
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
|
|
||||||
|
assert_eq!(personalization.len(), 8);
|
||||||
|
assert!(input.len() % 8 == 0);
|
||||||
|
|
||||||
|
let mut h = Vec::with_capacity(8);
|
||||||
|
h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32));
|
||||||
|
h.push(UInt32::constant(0xBB67AE85));
|
||||||
|
h.push(UInt32::constant(0x3C6EF372));
|
||||||
|
h.push(UInt32::constant(0xA54FF53A));
|
||||||
|
h.push(UInt32::constant(0x510E527F));
|
||||||
|
h.push(UInt32::constant(0x9B05688C));
|
||||||
|
|
||||||
|
// Personalization is stored here
|
||||||
|
h.push(UInt32::constant(
|
||||||
|
0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4]),
|
||||||
|
));
|
||||||
|
h.push(UInt32::constant(
|
||||||
|
0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8]),
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut blocks: Vec<Vec<UInt32>> = vec![];
|
||||||
|
|
||||||
|
for block in input.chunks(512) {
|
||||||
|
let mut this_block = Vec::with_capacity(16);
|
||||||
|
for word in block.chunks(32) {
|
||||||
|
let mut tmp = word.to_vec();
|
||||||
|
while tmp.len() < 32 {
|
||||||
|
tmp.push(Boolean::constant(false));
|
||||||
|
}
|
||||||
|
this_block.push(UInt32::from_bits(&tmp));
|
||||||
|
}
|
||||||
|
while this_block.len() < 16 {
|
||||||
|
this_block.push(UInt32::constant(0));
|
||||||
|
}
|
||||||
|
blocks.push(this_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
if blocks.is_empty() {
|
||||||
|
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
|
||||||
|
let cs = cs.namespace(|| format!("block {}", i));
|
||||||
|
|
||||||
|
blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let cs = cs.namespace(|| "final block");
|
||||||
|
|
||||||
|
blake2s_compression(
|
||||||
|
cs,
|
||||||
|
&mut h,
|
||||||
|
&blocks[blocks.len() - 1],
|
||||||
|
(input.len() / 8) as u64,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(h.into_iter().flat_map(|b| b.into_bits()).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use blake2s_simd::Params as Blake2sParams;
|
||||||
|
use hex_literal::hex;
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand_core::{RngCore, SeedableRng};
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use super::blake2s;
|
||||||
|
use crate::gadgets::boolean::{AllocatedBit, Boolean};
|
||||||
|
use crate::gadgets::test::TestConstraintSystem;
|
||||||
|
use crate::ConstraintSystem;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blank_hash() {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
let input_bits = vec![];
|
||||||
|
let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
assert_eq!(cs.num_constraints(), 0);
|
||||||
|
|
||||||
|
// >>> import blake2s from hashlib
|
||||||
|
// >>> h = blake2s(digest_size=32, person=b'12345678')
|
||||||
|
// >>> h.hexdigest()
|
||||||
|
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
|
||||||
|
|
||||||
|
let mut out = out.into_iter();
|
||||||
|
for b in expected.iter() {
|
||||||
|
for i in 0..8 {
|
||||||
|
let c = out.next().unwrap().get_value().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(c, (b >> i) & 1u8 == 1u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blake2s_constraints() {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
let input_bits: Vec<_> = (0..512)
|
||||||
|
.map(|i| {
|
||||||
|
AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true))
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
assert_eq!(cs.num_constraints(), 21518);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blake2s_precomp_constraints() {
|
||||||
|
// Test that 512 fixed leading bits (constants)
|
||||||
|
// doesn't result in more constraints.
|
||||||
|
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
let input_bits: Vec<_> = (0..512)
|
||||||
|
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||||
|
.chain((0..512).map(|i| {
|
||||||
|
AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true))
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
}))
|
||||||
|
.collect();
|
||||||
|
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
assert_eq!(cs.num_constraints(), 21518);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blake2s_constant_constraints() {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
let input_bits: Vec<_> = (0..512)
|
||||||
|
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||||
|
.collect();
|
||||||
|
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||||
|
assert_eq!(cs.num_constraints(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blake2s() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
|
||||||
|
let mut h = Blake2sParams::new()
|
||||||
|
.hash_length(32)
|
||||||
|
.personal(b"12345678")
|
||||||
|
.to_state();
|
||||||
|
|
||||||
|
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
|
||||||
|
|
||||||
|
h.update(&data);
|
||||||
|
|
||||||
|
let hash_result = h.finalize();
|
||||||
|
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let mut input_bits = vec![];
|
||||||
|
|
||||||
|
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||||
|
for bit_i in 0..8 {
|
||||||
|
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||||
|
|
||||||
|
input_bits.push(
|
||||||
|
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
let mut s = hash_result
|
||||||
|
.as_ref()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
|
||||||
|
|
||||||
|
for b in r {
|
||||||
|
match b {
|
||||||
|
Boolean::Is(b) => {
|
||||||
|
assert!(s.next().unwrap() == b.get_value().unwrap());
|
||||||
|
}
|
||||||
|
Boolean::Not(b) => {
|
||||||
|
assert!(s.next().unwrap() != b.get_value().unwrap());
|
||||||
|
}
|
||||||
|
Boolean::Constant(b) => {
|
||||||
|
assert!(input_len == 0);
|
||||||
|
assert!(s.next().unwrap() == b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blake2s_256_vars() {
|
||||||
|
let data: Vec<u8> = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec();
|
||||||
|
assert_eq!(data.len(), 256);
|
||||||
|
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let mut input_bits = vec![];
|
||||||
|
|
||||||
|
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||||
|
for bit_i in 0..8 {
|
||||||
|
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||||
|
|
||||||
|
input_bits.push(
|
||||||
|
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
let expected = hex!("0af5695115ced92c8a0341e43869209636e9aa6472e4576f0f2b996cf812b30e");
|
||||||
|
|
||||||
|
let mut out = r.into_iter();
|
||||||
|
for b in expected.iter() {
|
||||||
|
for i in 0..8 {
|
||||||
|
let c = out.next().unwrap().get_value().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(c, (b >> i) & 1u8 == 1u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blake2s_700_vars() {
|
||||||
|
let data: Vec<u8> = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec();
|
||||||
|
assert_eq!(data.len(), 700);
|
||||||
|
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let mut input_bits = vec![];
|
||||||
|
|
||||||
|
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||||
|
for bit_i in 0..8 {
|
||||||
|
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||||
|
|
||||||
|
input_bits.push(
|
||||||
|
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
let expected = hex!("2ab8f0683167ba220eef19dccf4f9b1a8193cc09b35e0235842323950530f18a");
|
||||||
|
|
||||||
|
let mut out = r.into_iter();
|
||||||
|
for b in expected.iter() {
|
||||||
|
for i in 0..8 {
|
||||||
|
let c = out.next().unwrap().get_value().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(c, (b >> i) & 1u8 == 1u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blake2s_test_vectors() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let expecteds = [
|
||||||
|
hex!("a1309e334376c8f36a736a4ab0e691ef931ee3ebdb9ea96187127136fea622a1"),
|
||||||
|
hex!("82fefff60f265cea255252f7c194a7f93965dffee0609ef74eb67f0d76cd41c6"),
|
||||||
|
];
|
||||||
|
for i in 0..2 {
|
||||||
|
let mut h = Blake2sParams::new()
|
||||||
|
.hash_length(32)
|
||||||
|
.personal(b"12345678")
|
||||||
|
.to_state();
|
||||||
|
let input_len = 1024;
|
||||||
|
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
|
||||||
|
|
||||||
|
h.update(&data);
|
||||||
|
|
||||||
|
let hash_result = h.finalize();
|
||||||
|
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let mut input_bits = vec![];
|
||||||
|
|
||||||
|
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||||
|
for bit_i in 0..8 {
|
||||||
|
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||||
|
|
||||||
|
input_bits.push(
|
||||||
|
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
let mut s = hash_result
|
||||||
|
.as_ref()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
|
||||||
|
|
||||||
|
for b in r {
|
||||||
|
match b {
|
||||||
|
Boolean::Is(b) => {
|
||||||
|
assert!(s.next().unwrap() == b.get_value().unwrap());
|
||||||
|
}
|
||||||
|
Boolean::Not(b) => {
|
||||||
|
assert!(s.next().unwrap() != b.get_value().unwrap());
|
||||||
|
}
|
||||||
|
Boolean::Constant(b) => {
|
||||||
|
assert!(input_len == 0);
|
||||||
|
assert!(s.next().unwrap() == b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(expecteds[i], hash_result.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1812
bellman/src/gadgets/boolean.rs
Normal file
1812
bellman/src/gadgets/boolean.rs
Normal file
File diff suppressed because it is too large
Load Diff
317
bellman/src/gadgets/lookup.rs
Normal file
317
bellman/src/gadgets/lookup.rs
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
//! Window table lookup gadgets.
|
||||||
|
|
||||||
|
use ff::{Field, ScalarEngine};
|
||||||
|
|
||||||
|
use super::boolean::Boolean;
|
||||||
|
use super::num::{AllocatedNum, Num};
|
||||||
|
use super::*;
|
||||||
|
use crate::ConstraintSystem;
|
||||||
|
|
||||||
|
// Synthesize the constants for each base pattern.
|
||||||
|
fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr])
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = &'a E::Fr>,
|
||||||
|
{
|
||||||
|
assert_eq!(assignment.len(), 1 << window_size);
|
||||||
|
|
||||||
|
for (i, constant) in constants.into_iter().enumerate() {
|
||||||
|
let mut cur = assignment[i];
|
||||||
|
cur.negate();
|
||||||
|
cur.add_assign(constant);
|
||||||
|
assignment[i] = cur;
|
||||||
|
for (j, eval) in assignment.iter_mut().enumerate().skip(i + 1) {
|
||||||
|
if j & i == i {
|
||||||
|
eval.add_assign(&cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs a 3-bit window table lookup. `bits` is in
|
||||||
|
/// little-endian order.
|
||||||
|
pub fn lookup3_xy<E: ScalarEngine, CS>(
|
||||||
|
mut cs: CS,
|
||||||
|
bits: &[Boolean],
|
||||||
|
coords: &[(E::Fr, E::Fr)],
|
||||||
|
) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError>
|
||||||
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
assert_eq!(bits.len(), 3);
|
||||||
|
assert_eq!(coords.len(), 8);
|
||||||
|
|
||||||
|
// Calculate the index into `coords`
|
||||||
|
let i = match (
|
||||||
|
bits[0].get_value(),
|
||||||
|
bits[1].get_value(),
|
||||||
|
bits[2].get_value(),
|
||||||
|
) {
|
||||||
|
(Some(a_value), Some(b_value), Some(c_value)) => {
|
||||||
|
let mut tmp = 0;
|
||||||
|
if a_value {
|
||||||
|
tmp += 1;
|
||||||
|
}
|
||||||
|
if b_value {
|
||||||
|
tmp += 2;
|
||||||
|
}
|
||||||
|
if c_value {
|
||||||
|
tmp += 4;
|
||||||
|
}
|
||||||
|
Some(tmp)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate the x-coordinate resulting from the lookup
|
||||||
|
let res_x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(coords[*i.get()?].0))?;
|
||||||
|
|
||||||
|
// Allocate the y-coordinate resulting from the lookup
|
||||||
|
let res_y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(coords[*i.get()?].1))?;
|
||||||
|
|
||||||
|
// Compute the coefficients for the lookup constraints
|
||||||
|
let mut x_coeffs = [E::Fr::zero(); 8];
|
||||||
|
let mut y_coeffs = [E::Fr::zero(); 8];
|
||||||
|
synth::<E, _>(3, coords.iter().map(|c| &c.0), &mut x_coeffs);
|
||||||
|
synth::<E, _>(3, coords.iter().map(|c| &c.1), &mut y_coeffs);
|
||||||
|
|
||||||
|
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[1], &bits[2])?;
|
||||||
|
|
||||||
|
let one = CS::one();
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "x-coordinate lookup",
|
||||||
|
|lc| {
|
||||||
|
lc + (x_coeffs[0b001], one)
|
||||||
|
+ &bits[1].lc::<E>(one, x_coeffs[0b011])
|
||||||
|
+ &bits[2].lc::<E>(one, x_coeffs[0b101])
|
||||||
|
+ &precomp.lc::<E>(one, x_coeffs[0b111])
|
||||||
|
},
|
||||||
|
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|
||||||
|
|lc| {
|
||||||
|
lc + res_x.get_variable()
|
||||||
|
- (x_coeffs[0b000], one)
|
||||||
|
- &bits[1].lc::<E>(one, x_coeffs[0b010])
|
||||||
|
- &bits[2].lc::<E>(one, x_coeffs[0b100])
|
||||||
|
- &precomp.lc::<E>(one, x_coeffs[0b110])
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "y-coordinate lookup",
|
||||||
|
|lc| {
|
||||||
|
lc + (y_coeffs[0b001], one)
|
||||||
|
+ &bits[1].lc::<E>(one, y_coeffs[0b011])
|
||||||
|
+ &bits[2].lc::<E>(one, y_coeffs[0b101])
|
||||||
|
+ &precomp.lc::<E>(one, y_coeffs[0b111])
|
||||||
|
},
|
||||||
|
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|
||||||
|
|lc| {
|
||||||
|
lc + res_y.get_variable()
|
||||||
|
- (y_coeffs[0b000], one)
|
||||||
|
- &bits[1].lc::<E>(one, y_coeffs[0b010])
|
||||||
|
- &bits[2].lc::<E>(one, y_coeffs[0b100])
|
||||||
|
- &precomp.lc::<E>(one, y_coeffs[0b110])
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok((res_x, res_y))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs a 3-bit window table lookup, where
|
||||||
|
/// one of the bits is a sign bit.
|
||||||
|
pub fn lookup3_xy_with_conditional_negation<E: ScalarEngine, CS>(
|
||||||
|
mut cs: CS,
|
||||||
|
bits: &[Boolean],
|
||||||
|
coords: &[(E::Fr, E::Fr)],
|
||||||
|
) -> Result<(Num<E>, Num<E>), SynthesisError>
|
||||||
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
assert_eq!(bits.len(), 3);
|
||||||
|
assert_eq!(coords.len(), 4);
|
||||||
|
|
||||||
|
// Calculate the index into `coords`
|
||||||
|
let i = match (bits[0].get_value(), bits[1].get_value()) {
|
||||||
|
(Some(a_value), Some(b_value)) => {
|
||||||
|
let mut tmp = 0;
|
||||||
|
if a_value {
|
||||||
|
tmp += 1;
|
||||||
|
}
|
||||||
|
if b_value {
|
||||||
|
tmp += 2;
|
||||||
|
}
|
||||||
|
Some(tmp)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate the y-coordinate resulting from the lookup
|
||||||
|
// and conditional negation
|
||||||
|
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
|
||||||
|
let mut tmp = coords[*i.get()?].1;
|
||||||
|
if *bits[2].get_value().get()? {
|
||||||
|
tmp.negate();
|
||||||
|
}
|
||||||
|
Ok(tmp)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let one = CS::one();
|
||||||
|
|
||||||
|
// Compute the coefficients for the lookup constraints
|
||||||
|
let mut x_coeffs = [E::Fr::zero(); 4];
|
||||||
|
let mut y_coeffs = [E::Fr::zero(); 4];
|
||||||
|
synth::<E, _>(2, coords.iter().map(|c| &c.0), &mut x_coeffs);
|
||||||
|
synth::<E, _>(2, coords.iter().map(|c| &c.1), &mut y_coeffs);
|
||||||
|
|
||||||
|
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?;
|
||||||
|
|
||||||
|
let x = Num::zero()
|
||||||
|
.add_bool_with_coeff(one, &Boolean::constant(true), x_coeffs[0b00])
|
||||||
|
.add_bool_with_coeff(one, &bits[0], x_coeffs[0b01])
|
||||||
|
.add_bool_with_coeff(one, &bits[1], x_coeffs[0b10])
|
||||||
|
.add_bool_with_coeff(one, &precomp, x_coeffs[0b11]);
|
||||||
|
|
||||||
|
let y_lc = precomp.lc::<E>(one, y_coeffs[0b11])
|
||||||
|
+ &bits[1].lc::<E>(one, y_coeffs[0b10])
|
||||||
|
+ &bits[0].lc::<E>(one, y_coeffs[0b01])
|
||||||
|
+ (y_coeffs[0b00], one);
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "y-coordinate lookup",
|
||||||
|
|lc| lc + &y_lc + &y_lc,
|
||||||
|
|lc| lc + &bits[2].lc::<E>(one, E::Fr::one()),
|
||||||
|
|lc| lc + &y_lc - y.get_variable(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok((x, y.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::gadgets::boolean::{AllocatedBit, Boolean};
|
||||||
|
use crate::gadgets::test::*;
|
||||||
|
use pairing::bls12_381::{Bls12, Fr};
|
||||||
|
use rand_core::{RngCore, SeedableRng};
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lookup3_xy() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let a_val = rng.next_u32() % 2 != 0;
|
||||||
|
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
|
||||||
|
|
||||||
|
let b_val = rng.next_u32() % 2 != 0;
|
||||||
|
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
|
||||||
|
|
||||||
|
let c_val = rng.next_u32() % 2 != 0;
|
||||||
|
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
|
||||||
|
|
||||||
|
let bits = vec![a, b, c];
|
||||||
|
|
||||||
|
let points: Vec<(Fr, Fr)> = (0..8)
|
||||||
|
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let res = lookup3_xy(&mut cs, &bits, &points).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
if a_val {
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
if b_val {
|
||||||
|
index += 2
|
||||||
|
}
|
||||||
|
if c_val {
|
||||||
|
index += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(res.0.get_value().unwrap(), points[index].0);
|
||||||
|
assert_eq!(res.1.get_value().unwrap(), points[index].1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lookup3_xy_with_conditional_negation() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let a_val = rng.next_u32() % 2 != 0;
|
||||||
|
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
|
||||||
|
|
||||||
|
let b_val = rng.next_u32() % 2 != 0;
|
||||||
|
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
|
||||||
|
|
||||||
|
let c_val = rng.next_u32() % 2 != 0;
|
||||||
|
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
|
||||||
|
|
||||||
|
let bits = vec![a, b, c];
|
||||||
|
|
||||||
|
let points: Vec<(Fr, Fr)> = (0..4)
|
||||||
|
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
if a_val {
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
if b_val {
|
||||||
|
index += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(res.0.get_value().unwrap(), points[index].0);
|
||||||
|
let mut tmp = points[index].1;
|
||||||
|
if c_val {
|
||||||
|
tmp.negate()
|
||||||
|
}
|
||||||
|
assert_eq!(res.1.get_value().unwrap(), tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_synth() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let window_size = 4;
|
||||||
|
|
||||||
|
let mut assignment = vec![Fr::zero(); 1 << window_size];
|
||||||
|
let constants: Vec<_> = (0..(1 << window_size))
|
||||||
|
.map(|_| Fr::random(&mut rng))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
synth::<Bls12, _>(window_size, &constants, &mut assignment);
|
||||||
|
|
||||||
|
for b in 0..(1 << window_size) {
|
||||||
|
let mut acc = Fr::zero();
|
||||||
|
|
||||||
|
for j in 0..(1 << window_size) {
|
||||||
|
if j & b == j {
|
||||||
|
acc.add_assign(&assignment[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(acc, constants[b]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
119
bellman/src/gadgets/multieq.rs
Normal file
119
bellman/src/gadgets/multieq.rs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
use ff::{Field, PrimeField, ScalarEngine};
|
||||||
|
|
||||||
|
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||||
|
|
||||||
|
pub struct MultiEq<E: ScalarEngine, CS: ConstraintSystem<E>> {
|
||||||
|
cs: CS,
|
||||||
|
ops: usize,
|
||||||
|
bits_used: usize,
|
||||||
|
lhs: LinearCombination<E>,
|
||||||
|
rhs: LinearCombination<E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
||||||
|
pub fn new(cs: CS) -> Self {
|
||||||
|
MultiEq {
|
||||||
|
cs,
|
||||||
|
ops: 0,
|
||||||
|
bits_used: 0,
|
||||||
|
lhs: LinearCombination::zero(),
|
||||||
|
rhs: LinearCombination::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accumulate(&mut self) {
|
||||||
|
let ops = self.ops;
|
||||||
|
let lhs = self.lhs.clone();
|
||||||
|
let rhs = self.rhs.clone();
|
||||||
|
self.cs.enforce(
|
||||||
|
|| format!("multieq {}", ops),
|
||||||
|
|_| lhs,
|
||||||
|
|lc| lc + CS::one(),
|
||||||
|
|_| rhs,
|
||||||
|
);
|
||||||
|
self.lhs = LinearCombination::zero();
|
||||||
|
self.rhs = LinearCombination::zero();
|
||||||
|
self.bits_used = 0;
|
||||||
|
self.ops += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enforce_equal(
|
||||||
|
&mut self,
|
||||||
|
num_bits: usize,
|
||||||
|
lhs: &LinearCombination<E>,
|
||||||
|
rhs: &LinearCombination<E>,
|
||||||
|
) {
|
||||||
|
// Check if we will exceed the capacity
|
||||||
|
if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) {
|
||||||
|
self.accumulate();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!((E::Fr::CAPACITY as usize) > (self.bits_used + num_bits));
|
||||||
|
|
||||||
|
let coeff = E::Fr::from_str("2").unwrap().pow(&[self.bits_used as u64]);
|
||||||
|
self.lhs = self.lhs.clone() + (coeff, lhs);
|
||||||
|
self.rhs = self.rhs.clone() + (coeff, rhs);
|
||||||
|
self.bits_used += num_bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.bits_used > 0 {
|
||||||
|
self.accumulate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> {
|
||||||
|
type Root = Self;
|
||||||
|
|
||||||
|
fn one() -> Variable {
|
||||||
|
CS::one()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
self.cs.alloc(annotation, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
self.cs.alloc_input(annotation, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||||
|
where
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
{
|
||||||
|
self.cs.enforce(annotation, a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||||
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
|
{
|
||||||
|
self.cs.get_root().push_namespace(name_fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_namespace(&mut self) {
|
||||||
|
self.cs.get_root().pop_namespace()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
111
bellman/src/gadgets/multipack.rs
Normal file
111
bellman/src/gadgets/multipack.rs
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
//! Helpers for packing vectors of bits into scalar field elements.
|
||||||
|
|
||||||
|
use super::boolean::Boolean;
|
||||||
|
use super::num::Num;
|
||||||
|
use super::Assignment;
|
||||||
|
use crate::{ConstraintSystem, SynthesisError};
|
||||||
|
use ff::{Field, PrimeField, ScalarEngine};
|
||||||
|
|
||||||
|
/// Takes a sequence of booleans and exposes them as compact
|
||||||
|
/// public inputs
|
||||||
|
pub fn pack_into_inputs<E, CS>(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() {
|
||||||
|
let mut num = Num::<E>::zero();
|
||||||
|
let mut coeff = E::Fr::one();
|
||||||
|
for bit in bits {
|
||||||
|
num = num.add_bool_with_coeff(CS::one(), bit, coeff);
|
||||||
|
|
||||||
|
coeff.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
let input = cs.alloc_input(|| format!("input {}", i), || Ok(*num.get_value().get()?))?;
|
||||||
|
|
||||||
|
// num * 1 = input
|
||||||
|
cs.enforce(
|
||||||
|
|| format!("packing constraint {}", i),
|
||||||
|
|_| num.lc(E::Fr::one()),
|
||||||
|
|lc| lc + CS::one(),
|
||||||
|
|lc| lc + input,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
|
||||||
|
bytes
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool> {
|
||||||
|
bytes
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_multipacking<E: ScalarEngine>(bits: &[bool]) -> Vec<E::Fr> {
|
||||||
|
let mut result = vec![];
|
||||||
|
|
||||||
|
for bits in bits.chunks(E::Fr::CAPACITY as usize) {
|
||||||
|
let mut cur = E::Fr::zero();
|
||||||
|
let mut coeff = E::Fr::one();
|
||||||
|
|
||||||
|
for bit in bits {
|
||||||
|
if *bit {
|
||||||
|
cur.add_assign(&coeff);
|
||||||
|
}
|
||||||
|
|
||||||
|
coeff.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multipacking() {
|
||||||
|
use crate::ConstraintSystem;
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand_core::{RngCore, SeedableRng};
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use super::boolean::{AllocatedBit, Boolean};
|
||||||
|
use crate::gadgets::test::*;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for num_bits in 0..1500 {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let bits: Vec<bool> = (0..num_bits).map(|_| rng.next_u32() % 2 != 0).collect();
|
||||||
|
|
||||||
|
let circuit_bits = bits
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, &b)| {
|
||||||
|
Boolean::from(
|
||||||
|
AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(b)).unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let expected_inputs = compute_multipacking::<Bls12>(&bits);
|
||||||
|
|
||||||
|
pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
assert!(cs.verify(&expected_inputs));
|
||||||
|
}
|
||||||
|
}
|
||||||
589
bellman/src/gadgets/num.rs
Normal file
589
bellman/src/gadgets/num.rs
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
//! Gadgets representing numbers in the scalar field of the underlying curve.
|
||||||
|
|
||||||
|
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||||
|
|
||||||
|
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||||
|
|
||||||
|
use super::Assignment;
|
||||||
|
|
||||||
|
use super::boolean::{self, AllocatedBit, Boolean};
|
||||||
|
|
||||||
|
pub struct AllocatedNum<E: ScalarEngine> {
|
||||||
|
value: Option<E::Fr>,
|
||||||
|
variable: Variable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> Clone for AllocatedNum<E> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
AllocatedNum {
|
||||||
|
value: self.value,
|
||||||
|
variable: self.variable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> AllocatedNum<E> {
|
||||||
|
pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
|
||||||
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
{
|
||||||
|
let mut new_value = None;
|
||||||
|
let var = cs.alloc(
|
||||||
|
|| "num",
|
||||||
|
|| {
|
||||||
|
let tmp = value()?;
|
||||||
|
|
||||||
|
new_value = Some(tmp);
|
||||||
|
|
||||||
|
Ok(tmp)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(AllocatedNum {
|
||||||
|
value: new_value,
|
||||||
|
variable: var,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inputize<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
|
||||||
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
let input = cs.alloc_input(|| "input variable", || Ok(*self.value.get()?))?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "enforce input is correct",
|
||||||
|
|lc| lc + input,
|
||||||
|
|lc| lc + CS::one(),
|
||||||
|
|lc| lc + self.variable,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deconstructs this allocated number into its
|
||||||
|
/// boolean representation in little-endian bit
|
||||||
|
/// order, requiring that the representation
|
||||||
|
/// strictly exists "in the field" (i.e., a
|
||||||
|
/// congruency is not allowed.)
|
||||||
|
pub fn to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
pub fn kary_and<E, CS>(
|
||||||
|
mut cs: CS,
|
||||||
|
v: &[AllocatedBit],
|
||||||
|
) -> Result<AllocatedBit, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
assert!(!v.is_empty());
|
||||||
|
|
||||||
|
// Let's keep this simple for now and just AND them all
|
||||||
|
// manually
|
||||||
|
let mut cur = None;
|
||||||
|
|
||||||
|
for (i, v) in v.iter().enumerate() {
|
||||||
|
if cur.is_none() {
|
||||||
|
cur = Some(v.clone());
|
||||||
|
} else {
|
||||||
|
cur = Some(AllocatedBit::and(
|
||||||
|
cs.namespace(|| format!("and {}", i)),
|
||||||
|
cur.as_ref().unwrap(),
|
||||||
|
v,
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(cur.expect("v.len() > 0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to ensure that the bit representation of a is
|
||||||
|
// less than or equal to r - 1.
|
||||||
|
let mut a = self.value.map(|e| BitIterator::new(e.into_repr()));
|
||||||
|
let mut b = E::Fr::char();
|
||||||
|
b.sub_noborrow(&1.into());
|
||||||
|
|
||||||
|
let mut result = vec![];
|
||||||
|
|
||||||
|
// Runs of ones in r
|
||||||
|
let mut last_run = None;
|
||||||
|
let mut current_run = vec![];
|
||||||
|
|
||||||
|
let mut found_one = false;
|
||||||
|
let mut i = 0;
|
||||||
|
for b in BitIterator::new(b) {
|
||||||
|
let a_bit = a.as_mut().map(|e| e.next().unwrap());
|
||||||
|
|
||||||
|
// Skip over unset bits at the beginning
|
||||||
|
found_one |= b;
|
||||||
|
if !found_one {
|
||||||
|
// a_bit should also be false
|
||||||
|
a_bit.map(|e| assert!(!e));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if b {
|
||||||
|
// This is part of a run of ones. Let's just
|
||||||
|
// allocate the boolean with the expected value.
|
||||||
|
let a_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), a_bit)?;
|
||||||
|
// ... and add it to the current run of ones.
|
||||||
|
current_run.push(a_bit.clone());
|
||||||
|
result.push(a_bit);
|
||||||
|
} else {
|
||||||
|
if !current_run.is_empty() {
|
||||||
|
// This is the start of a run of zeros, but we need
|
||||||
|
// to k-ary AND against `last_run` first.
|
||||||
|
|
||||||
|
if last_run.is_some() {
|
||||||
|
current_run.push(last_run.clone().unwrap());
|
||||||
|
}
|
||||||
|
last_run = Some(kary_and(
|
||||||
|
cs.namespace(|| format!("run ending at {}", i)),
|
||||||
|
¤t_run,
|
||||||
|
)?);
|
||||||
|
current_run.truncate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If `last_run` is true, `a` must be false, or it would
|
||||||
|
// not be in the field.
|
||||||
|
//
|
||||||
|
// If `last_run` is false, `a` can be true or false.
|
||||||
|
|
||||||
|
let a_bit = AllocatedBit::alloc_conditionally(
|
||||||
|
cs.namespace(|| format!("bit {}", i)),
|
||||||
|
a_bit,
|
||||||
|
&last_run.as_ref().expect("char always starts with a one"),
|
||||||
|
)?;
|
||||||
|
result.push(a_bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// char is prime, so we'll always end on
|
||||||
|
// a run of zeros.
|
||||||
|
assert_eq!(current_run.len(), 0);
|
||||||
|
|
||||||
|
// Now, we have `result` in big-endian order.
|
||||||
|
// However, now we have to unpack self!
|
||||||
|
|
||||||
|
let mut lc = LinearCombination::zero();
|
||||||
|
let mut coeff = E::Fr::one();
|
||||||
|
|
||||||
|
for bit in result.iter().rev() {
|
||||||
|
lc = lc + (coeff, bit.get_variable());
|
||||||
|
|
||||||
|
coeff.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
lc = lc - self.variable;
|
||||||
|
|
||||||
|
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||||
|
|
||||||
|
// Convert into booleans, and reverse for little-endian bit order
|
||||||
|
Ok(result.into_iter().map(Boolean::from).rev().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the allocated number into its little-endian representation.
|
||||||
|
/// Note that this does not strongly enforce that the commitment is
|
||||||
|
/// "in the field."
|
||||||
|
pub fn to_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
let bits = boolean::field_into_allocated_bits_le(&mut cs, self.value)?;
|
||||||
|
|
||||||
|
let mut lc = LinearCombination::zero();
|
||||||
|
let mut coeff = E::Fr::one();
|
||||||
|
|
||||||
|
for bit in bits.iter() {
|
||||||
|
lc = lc + (coeff, bit.get_variable());
|
||||||
|
|
||||||
|
coeff.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
lc = lc - self.variable;
|
||||||
|
|
||||||
|
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||||
|
|
||||||
|
Ok(bits.into_iter().map(Boolean::from).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||||
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
let mut value = None;
|
||||||
|
|
||||||
|
let var = cs.alloc(
|
||||||
|
|| "product num",
|
||||||
|
|| {
|
||||||
|
let mut tmp = *self.value.get()?;
|
||||||
|
tmp.mul_assign(other.value.get()?);
|
||||||
|
|
||||||
|
value = Some(tmp);
|
||||||
|
|
||||||
|
Ok(tmp)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Constrain: a * b = ab
|
||||||
|
cs.enforce(
|
||||||
|
|| "multiplication constraint",
|
||||||
|
|lc| lc + self.variable,
|
||||||
|
|lc| lc + other.variable,
|
||||||
|
|lc| lc + var,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(AllocatedNum {
|
||||||
|
value,
|
||||||
|
variable: var,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn square<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError>
|
||||||
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
let mut value = None;
|
||||||
|
|
||||||
|
let var = cs.alloc(
|
||||||
|
|| "squared num",
|
||||||
|
|| {
|
||||||
|
let mut tmp = *self.value.get()?;
|
||||||
|
tmp.square();
|
||||||
|
|
||||||
|
value = Some(tmp);
|
||||||
|
|
||||||
|
Ok(tmp)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Constrain: a * a = aa
|
||||||
|
cs.enforce(
|
||||||
|
|| "squaring constraint",
|
||||||
|
|lc| lc + self.variable,
|
||||||
|
|lc| lc + self.variable,
|
||||||
|
|lc| lc + var,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(AllocatedNum {
|
||||||
|
value,
|
||||||
|
variable: var,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assert_nonzero<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
|
||||||
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
let inv = cs.alloc(
|
||||||
|
|| "ephemeral inverse",
|
||||||
|
|| {
|
||||||
|
let tmp = *self.value.get()?;
|
||||||
|
|
||||||
|
if tmp.is_zero() {
|
||||||
|
Err(SynthesisError::DivisionByZero)
|
||||||
|
} else {
|
||||||
|
Ok(tmp.inverse().unwrap())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Constrain a * inv = 1, which is only valid
|
||||||
|
// iff a has a multiplicative inverse, untrue
|
||||||
|
// for zero.
|
||||||
|
cs.enforce(
|
||||||
|
|| "nonzero assertion constraint",
|
||||||
|
|lc| lc + self.variable,
|
||||||
|
|lc| lc + inv,
|
||||||
|
|lc| lc + CS::one(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes two allocated numbers (a, b) and returns
|
||||||
|
/// (b, a) if the condition is true, and (a, b)
|
||||||
|
/// otherwise.
|
||||||
|
pub fn conditionally_reverse<CS>(
|
||||||
|
mut cs: CS,
|
||||||
|
a: &Self,
|
||||||
|
b: &Self,
|
||||||
|
condition: &Boolean,
|
||||||
|
) -> Result<(Self, Self), SynthesisError>
|
||||||
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || {
|
||||||
|
if *condition.get_value().get()? {
|
||||||
|
Ok(*b.value.get()?)
|
||||||
|
} else {
|
||||||
|
Ok(*a.value.get()?)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "first conditional reversal",
|
||||||
|
|lc| lc + a.variable - b.variable,
|
||||||
|
|_| condition.lc(CS::one(), E::Fr::one()),
|
||||||
|
|lc| lc + a.variable - c.variable,
|
||||||
|
);
|
||||||
|
|
||||||
|
let d = Self::alloc(cs.namespace(|| "conditional reversal result 2"), || {
|
||||||
|
if *condition.get_value().get()? {
|
||||||
|
Ok(*a.value.get()?)
|
||||||
|
} else {
|
||||||
|
Ok(*b.value.get()?)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "second conditional reversal",
|
||||||
|
|lc| lc + b.variable - a.variable,
|
||||||
|
|_| condition.lc(CS::one(), E::Fr::one()),
|
||||||
|
|lc| lc + b.variable - d.variable,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok((c, d))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> Option<E::Fr> {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_variable(&self) -> Variable {
|
||||||
|
self.variable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Num<E: ScalarEngine> {
|
||||||
|
value: Option<E::Fr>,
|
||||||
|
lc: LinearCombination<E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> From<AllocatedNum<E>> for Num<E> {
|
||||||
|
fn from(num: AllocatedNum<E>) -> Num<E> {
|
||||||
|
Num {
|
||||||
|
value: num.value,
|
||||||
|
lc: LinearCombination::<E>::zero() + num.variable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> Num<E> {
|
||||||
|
pub fn zero() -> Self {
|
||||||
|
Num {
|
||||||
|
value: Some(E::Fr::zero()),
|
||||||
|
lc: LinearCombination::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> Option<E::Fr> {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lc(&self, coeff: E::Fr) -> LinearCombination<E> {
|
||||||
|
LinearCombination::zero() + (coeff, &self.lc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: E::Fr) -> Self {
|
||||||
|
let newval = match (self.value, bit.get_value()) {
|
||||||
|
(Some(mut curval), Some(bval)) => {
|
||||||
|
if bval {
|
||||||
|
curval.add_assign(&coeff);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(curval)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Num {
|
||||||
|
value: newval,
|
||||||
|
lc: self.lc + &bit.lc(one, coeff),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::ConstraintSystem;
|
||||||
|
use ff::{BitIterator, Field, PrimeField};
|
||||||
|
use pairing::bls12_381::{Bls12, Fr};
|
||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use super::{AllocatedNum, Boolean};
|
||||||
|
use crate::gadgets::test::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_allocated_num() {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
AllocatedNum::alloc(&mut cs, || Ok(Fr::one())).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.get("num") == Fr::one());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_num_squaring() {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap();
|
||||||
|
let n2 = n.square(&mut cs).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
assert!(cs.get("squared num") == Fr::from_str("9").unwrap());
|
||||||
|
assert!(n2.value.unwrap() == Fr::from_str("9").unwrap());
|
||||||
|
cs.set("squared num", Fr::from_str("10").unwrap());
|
||||||
|
assert!(!cs.is_satisfied());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_num_multiplication() {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let n =
|
||||||
|
AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap();
|
||||||
|
let n2 =
|
||||||
|
AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap();
|
||||||
|
let n3 = n.mul(&mut cs, &n2).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
assert!(cs.get("product num") == Fr::from_str("120").unwrap());
|
||||||
|
assert!(n3.value.unwrap() == Fr::from_str("120").unwrap());
|
||||||
|
cs.set("product num", Fr::from_str("121").unwrap());
|
||||||
|
assert!(!cs.is_satisfied());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_num_conditional_reversal() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
{
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap();
|
||||||
|
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap();
|
||||||
|
let condition = Boolean::constant(false);
|
||||||
|
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
assert_eq!(a.value.unwrap(), c.value.unwrap());
|
||||||
|
assert_eq!(b.value.unwrap(), d.value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap();
|
||||||
|
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap();
|
||||||
|
let condition = Boolean::constant(true);
|
||||||
|
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
assert_eq!(a.value.unwrap(), d.value.unwrap());
|
||||||
|
assert_eq!(b.value.unwrap(), c.value.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_num_nonzero() {
|
||||||
|
{
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap();
|
||||||
|
n.assert_nonzero(&mut cs).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
cs.set("ephemeral inverse", Fr::from_str("3").unwrap());
|
||||||
|
assert!(cs.which_is_unsatisfied() == Some("nonzero assertion constraint"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::zero())).unwrap();
|
||||||
|
assert!(n.assert_nonzero(&mut cs).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_into_bits_strict() {
|
||||||
|
let mut negone = Fr::one();
|
||||||
|
negone.negate();
|
||||||
|
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
|
||||||
|
n.to_bits_le_strict(&mut cs).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
// make the bit representation the characteristic
|
||||||
|
cs.set("bit 254/boolean", Fr::one());
|
||||||
|
|
||||||
|
// this makes the conditional boolean constraint fail
|
||||||
|
assert_eq!(
|
||||||
|
cs.which_is_unsatisfied().unwrap(),
|
||||||
|
"bit 254/boolean constraint"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_into_bits() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for i in 0..200 {
|
||||||
|
let r = Fr::random(&mut rng);
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
|
||||||
|
|
||||||
|
let bits = if i % 2 == 0 {
|
||||||
|
n.to_bits_le(&mut cs).unwrap()
|
||||||
|
} else {
|
||||||
|
n.to_bits_le_strict(&mut cs).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
for (b, a) in BitIterator::new(r.into_repr())
|
||||||
|
.skip(1)
|
||||||
|
.zip(bits.iter().rev())
|
||||||
|
{
|
||||||
|
if let &Boolean::Is(ref a) = a {
|
||||||
|
assert_eq!(b, a.get_value().unwrap());
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cs.set("num", Fr::random(&mut rng));
|
||||||
|
assert!(!cs.is_satisfied());
|
||||||
|
cs.set("num", r);
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
for i in 0..Fr::NUM_BITS {
|
||||||
|
let name = format!("bit {}/boolean", i);
|
||||||
|
let cur = cs.get(&name);
|
||||||
|
let mut tmp = Fr::one();
|
||||||
|
tmp.sub_assign(&cur);
|
||||||
|
cs.set(&name, tmp);
|
||||||
|
assert!(!cs.is_satisfied());
|
||||||
|
cs.set(&name, cur);
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
389
bellman/src/gadgets/sha256.rs
Normal file
389
bellman/src/gadgets/sha256.rs
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
//! Circuits for the [SHA-256] hash function and its internal compression
|
||||||
|
//! function.
|
||||||
|
//!
|
||||||
|
//! [SHA-256]: https://tools.ietf.org/html/rfc6234
|
||||||
|
|
||||||
|
use super::boolean::Boolean;
|
||||||
|
use super::multieq::MultiEq;
|
||||||
|
use super::uint32::UInt32;
|
||||||
|
use crate::{ConstraintSystem, SynthesisError};
|
||||||
|
use ff::ScalarEngine;
|
||||||
|
|
||||||
|
#[allow(clippy::unreadable_literal)]
|
||||||
|
const ROUND_CONSTANTS: [u32; 64] = [
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||||
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||||
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||||
|
];
|
||||||
|
|
||||||
|
#[allow(clippy::unreadable_literal)]
|
||||||
|
const IV: [u32; 8] = [
|
||||||
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn sha256_block_no_padding<E, CS>(
|
||||||
|
mut cs: CS,
|
||||||
|
input: &[Boolean],
|
||||||
|
) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
assert_eq!(input.len(), 512);
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
sha256_compression_function(&mut cs, &input, &get_sha256_iv())?
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|e| e.into_bits_be())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
assert!(input.len() % 8 == 0);
|
||||||
|
|
||||||
|
let mut padded = input.to_vec();
|
||||||
|
let plen = padded.len() as u64;
|
||||||
|
// append a single '1' bit
|
||||||
|
padded.push(Boolean::constant(true));
|
||||||
|
// append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512
|
||||||
|
while (padded.len() + 64) % 512 != 0 {
|
||||||
|
padded.push(Boolean::constant(false));
|
||||||
|
}
|
||||||
|
// append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits
|
||||||
|
for b in (0..64).rev().map(|i| (plen >> i) & 1 == 1) {
|
||||||
|
padded.push(Boolean::constant(b));
|
||||||
|
}
|
||||||
|
assert!(padded.len() % 512 == 0);
|
||||||
|
|
||||||
|
let mut cur = get_sha256_iv();
|
||||||
|
for (i, block) in padded.chunks(512).enumerate() {
|
||||||
|
cur = sha256_compression_function(cs.namespace(|| format!("block {}", i)), block, &cur)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(cur.into_iter().flat_map(|e| e.into_bits_be()).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_sha256_iv() -> Vec<UInt32> {
|
||||||
|
IV.iter().map(|&v| UInt32::constant(v)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sha256_compression_function<E, CS>(
|
||||||
|
cs: CS,
|
||||||
|
input: &[Boolean],
|
||||||
|
current_hash_value: &[UInt32],
|
||||||
|
) -> Result<Vec<UInt32>, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
assert_eq!(input.len(), 512);
|
||||||
|
assert_eq!(current_hash_value.len(), 8);
|
||||||
|
|
||||||
|
let mut w = input
|
||||||
|
.chunks(32)
|
||||||
|
.map(|e| UInt32::from_bits_be(e))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// We can save some constraints by combining some of
|
||||||
|
// the constraints in different u32 additions
|
||||||
|
let mut cs = MultiEq::new(cs);
|
||||||
|
|
||||||
|
for i in 16..64 {
|
||||||
|
let cs = &mut cs.namespace(|| format!("w extension {}", i));
|
||||||
|
|
||||||
|
// s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
|
||||||
|
let mut s0 = w[i - 15].rotr(7);
|
||||||
|
s0 = s0.xor(cs.namespace(|| "first xor for s0"), &w[i - 15].rotr(18))?;
|
||||||
|
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &w[i - 15].shr(3))?;
|
||||||
|
|
||||||
|
// s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
|
||||||
|
let mut s1 = w[i - 2].rotr(17);
|
||||||
|
s1 = s1.xor(cs.namespace(|| "first xor for s1"), &w[i - 2].rotr(19))?;
|
||||||
|
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?;
|
||||||
|
|
||||||
|
let tmp = UInt32::addmany(
|
||||||
|
cs.namespace(|| "computation of w[i]"),
|
||||||
|
&[w[i - 16].clone(), s0, w[i - 7].clone(), s1],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// w[i] := w[i-16] + s0 + w[i-7] + s1
|
||||||
|
w.push(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(w.len(), 64);
|
||||||
|
|
||||||
|
enum Maybe {
|
||||||
|
Deferred(Vec<UInt32>),
|
||||||
|
Concrete(UInt32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Maybe {
|
||||||
|
fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
||||||
|
{
|
||||||
|
Ok(match self {
|
||||||
|
Maybe::Concrete(ref v) => return Ok(v.clone()),
|
||||||
|
Maybe::Deferred(mut v) => {
|
||||||
|
v.extend(others.iter().cloned());
|
||||||
|
UInt32::addmany(cs, &v)?
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut a = Maybe::Concrete(current_hash_value[0].clone());
|
||||||
|
let mut b = current_hash_value[1].clone();
|
||||||
|
let mut c = current_hash_value[2].clone();
|
||||||
|
let mut d = current_hash_value[3].clone();
|
||||||
|
let mut e = Maybe::Concrete(current_hash_value[4].clone());
|
||||||
|
let mut f = current_hash_value[5].clone();
|
||||||
|
let mut g = current_hash_value[6].clone();
|
||||||
|
let mut h = current_hash_value[7].clone();
|
||||||
|
|
||||||
|
for i in 0..64 {
|
||||||
|
let cs = &mut cs.namespace(|| format!("compression round {}", i));
|
||||||
|
|
||||||
|
// S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
|
||||||
|
let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?;
|
||||||
|
let mut s1 = new_e.rotr(6);
|
||||||
|
s1 = s1.xor(cs.namespace(|| "first xor for s1"), &new_e.rotr(11))?;
|
||||||
|
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &new_e.rotr(25))?;
|
||||||
|
|
||||||
|
// ch := (e and f) xor ((not e) and g)
|
||||||
|
let ch = UInt32::sha256_ch(cs.namespace(|| "ch"), &new_e, &f, &g)?;
|
||||||
|
|
||||||
|
// temp1 := h + S1 + ch + k[i] + w[i]
|
||||||
|
let temp1 = vec![
|
||||||
|
h.clone(),
|
||||||
|
s1,
|
||||||
|
ch,
|
||||||
|
UInt32::constant(ROUND_CONSTANTS[i]),
|
||||||
|
w[i].clone(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
|
||||||
|
let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?;
|
||||||
|
let mut s0 = new_a.rotr(2);
|
||||||
|
s0 = s0.xor(cs.namespace(|| "first xor for s0"), &new_a.rotr(13))?;
|
||||||
|
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &new_a.rotr(22))?;
|
||||||
|
|
||||||
|
// maj := (a and b) xor (a and c) xor (b and c)
|
||||||
|
let maj = UInt32::sha256_maj(cs.namespace(|| "maj"), &new_a, &b, &c)?;
|
||||||
|
|
||||||
|
// temp2 := S0 + maj
|
||||||
|
let temp2 = vec![s0, maj];
|
||||||
|
|
||||||
|
/*
|
||||||
|
h := g
|
||||||
|
g := f
|
||||||
|
f := e
|
||||||
|
e := d + temp1
|
||||||
|
d := c
|
||||||
|
c := b
|
||||||
|
b := a
|
||||||
|
a := temp1 + temp2
|
||||||
|
*/
|
||||||
|
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = new_e;
|
||||||
|
e = Maybe::Deferred(temp1.iter().cloned().chain(Some(d)).collect::<Vec<_>>());
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = new_a;
|
||||||
|
a = Maybe::Deferred(
|
||||||
|
temp1
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.chain(temp2.iter().cloned())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add the compressed chunk to the current hash value:
|
||||||
|
h0 := h0 + a
|
||||||
|
h1 := h1 + b
|
||||||
|
h2 := h2 + c
|
||||||
|
h3 := h3 + d
|
||||||
|
h4 := h4 + e
|
||||||
|
h5 := h5 + f
|
||||||
|
h6 := h6 + g
|
||||||
|
h7 := h7 + h
|
||||||
|
*/
|
||||||
|
|
||||||
|
let h0 = a.compute(
|
||||||
|
cs.namespace(|| "deferred h0 computation"),
|
||||||
|
&[current_hash_value[0].clone()],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let h1 = UInt32::addmany(
|
||||||
|
cs.namespace(|| "new h1"),
|
||||||
|
&[current_hash_value[1].clone(), b],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let h2 = UInt32::addmany(
|
||||||
|
cs.namespace(|| "new h2"),
|
||||||
|
&[current_hash_value[2].clone(), c],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let h3 = UInt32::addmany(
|
||||||
|
cs.namespace(|| "new h3"),
|
||||||
|
&[current_hash_value[3].clone(), d],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let h4 = e.compute(
|
||||||
|
cs.namespace(|| "deferred h4 computation"),
|
||||||
|
&[current_hash_value[4].clone()],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let h5 = UInt32::addmany(
|
||||||
|
cs.namespace(|| "new h5"),
|
||||||
|
&[current_hash_value[5].clone(), f],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let h6 = UInt32::addmany(
|
||||||
|
cs.namespace(|| "new h6"),
|
||||||
|
&[current_hash_value[6].clone(), g],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let h7 = UInt32::addmany(
|
||||||
|
cs.namespace(|| "new h7"),
|
||||||
|
&[current_hash_value[7].clone(), h],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::gadgets::boolean::AllocatedBit;
|
||||||
|
use crate::gadgets::test::TestConstraintSystem;
|
||||||
|
use hex_literal::hex;
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand_core::{RngCore, SeedableRng};
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blank_hash() {
|
||||||
|
let iv = get_sha256_iv();
|
||||||
|
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect();
|
||||||
|
input_bits[0] = Boolean::Constant(true);
|
||||||
|
let out = sha256_compression_function(&mut cs, &input_bits, &iv).unwrap();
|
||||||
|
let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
assert_eq!(cs.num_constraints(), 0);
|
||||||
|
|
||||||
|
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
||||||
|
|
||||||
|
let mut out = out_bits.into_iter();
|
||||||
|
for b in expected.iter() {
|
||||||
|
for i in (0..8).rev() {
|
||||||
|
let c = out.next().unwrap().get_value().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(c, (b >> i) & 1u8 == 1u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_full_block() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let iv = get_sha256_iv();
|
||||||
|
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
let input_bits: Vec<_> = (0..512)
|
||||||
|
.map(|i| {
|
||||||
|
Boolean::from(
|
||||||
|
AllocatedBit::alloc(
|
||||||
|
cs.namespace(|| format!("input bit {}", i)),
|
||||||
|
Some(rng.next_u32() % 2 != 0),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
sha256_compression_function(cs.namespace(|| "sha256"), &input_bits, &iv).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
assert_eq!(cs.num_constraints() - 512, 25840);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_against_vectors() {
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
|
||||||
|
let mut h = Sha256::new();
|
||||||
|
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
|
||||||
|
h.input(&data);
|
||||||
|
let hash_result = h.result();
|
||||||
|
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
let mut input_bits = vec![];
|
||||||
|
|
||||||
|
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||||
|
for bit_i in (0..8).rev() {
|
||||||
|
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||||
|
|
||||||
|
input_bits.push(
|
||||||
|
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = sha256(&mut cs, &input_bits).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
let mut s = hash_result
|
||||||
|
.as_ref()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
|
||||||
|
|
||||||
|
for b in r {
|
||||||
|
match b {
|
||||||
|
Boolean::Is(b) => {
|
||||||
|
assert!(s.next().unwrap() == b.get_value().unwrap());
|
||||||
|
}
|
||||||
|
Boolean::Not(b) => {
|
||||||
|
assert!(s.next().unwrap() != b.get_value().unwrap());
|
||||||
|
}
|
||||||
|
Boolean::Constant(b) => {
|
||||||
|
assert!(input_len == 0);
|
||||||
|
assert!(s.next().unwrap() == b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
464
bellman/src/gadgets/test/mod.rs
Normal file
464
bellman/src/gadgets/test/mod.rs
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
//! Helpers for testing circuit implementations.
|
||||||
|
|
||||||
|
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||||
|
|
||||||
|
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use blake2s_simd::{Params as Blake2sParams, State as Blake2sState};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum NamedObject {
|
||||||
|
Constraint(usize),
|
||||||
|
Var(Variable),
|
||||||
|
Namespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constraint system for testing purposes.
|
||||||
|
pub struct TestConstraintSystem<E: ScalarEngine> {
|
||||||
|
named_objects: HashMap<String, NamedObject>,
|
||||||
|
current_namespace: Vec<String>,
|
||||||
|
constraints: Vec<(
|
||||||
|
LinearCombination<E>,
|
||||||
|
LinearCombination<E>,
|
||||||
|
LinearCombination<E>,
|
||||||
|
String,
|
||||||
|
)>,
|
||||||
|
inputs: Vec<(E::Fr, String)>,
|
||||||
|
aux: Vec<(E::Fr, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct OrderedVariable(Variable);
|
||||||
|
|
||||||
|
impl Eq for OrderedVariable {}
|
||||||
|
impl PartialEq for OrderedVariable {
|
||||||
|
fn eq(&self, other: &OrderedVariable) -> bool {
|
||||||
|
match (self.0.get_unchecked(), other.0.get_unchecked()) {
|
||||||
|
(Index::Input(ref a), Index::Input(ref b)) => a == b,
|
||||||
|
(Index::Aux(ref a), Index::Aux(ref b)) => a == b,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PartialOrd for OrderedVariable {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Ord for OrderedVariable {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
match (self.0.get_unchecked(), other.0.get_unchecked()) {
|
||||||
|
(Index::Input(ref a), Index::Input(ref b)) => a.cmp(b),
|
||||||
|
(Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b),
|
||||||
|
(Index::Input(_), Index::Aux(_)) => Ordering::Less,
|
||||||
|
(Index::Aux(_), Index::Input(_)) => Ordering::Greater,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn proc_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> {
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
for &(var, coeff) in terms {
|
||||||
|
map.entry(OrderedVariable(var))
|
||||||
|
.or_insert_with(E::Fr::zero)
|
||||||
|
.add_assign(&coeff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove terms that have a zero coefficient to normalize
|
||||||
|
let mut to_remove = vec![];
|
||||||
|
for (var, coeff) in map.iter() {
|
||||||
|
if coeff.is_zero() {
|
||||||
|
to_remove.push(var.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for var in to_remove {
|
||||||
|
map.remove(&var);
|
||||||
|
}
|
||||||
|
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
|
||||||
|
let map = proc_lc::<E>(terms);
|
||||||
|
|
||||||
|
let mut buf = [0u8; 9 + 32];
|
||||||
|
BigEndian::write_u64(&mut buf[0..8], map.len() as u64);
|
||||||
|
h.update(&buf[0..8]);
|
||||||
|
|
||||||
|
for (var, coeff) in map {
|
||||||
|
match var.0.get_unchecked() {
|
||||||
|
Index::Input(i) => {
|
||||||
|
buf[0] = b'I';
|
||||||
|
BigEndian::write_u64(&mut buf[1..9], i as u64);
|
||||||
|
}
|
||||||
|
Index::Aux(i) => {
|
||||||
|
buf[0] = b'A';
|
||||||
|
BigEndian::write_u64(&mut buf[1..9], i as u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coeff.into_repr().write_be(&mut buf[9..]).unwrap();
|
||||||
|
|
||||||
|
h.update(&buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_lc<E: ScalarEngine>(
|
||||||
|
terms: &[(Variable, E::Fr)],
|
||||||
|
inputs: &[(E::Fr, String)],
|
||||||
|
aux: &[(E::Fr, String)],
|
||||||
|
) -> E::Fr {
|
||||||
|
let mut acc = E::Fr::zero();
|
||||||
|
|
||||||
|
for &(var, ref coeff) in terms {
|
||||||
|
let mut tmp = match var.get_unchecked() {
|
||||||
|
Index::Input(index) => inputs[index].0,
|
||||||
|
Index::Aux(index) => aux[index].0,
|
||||||
|
};
|
||||||
|
|
||||||
|
tmp.mul_assign(&coeff);
|
||||||
|
acc.add_assign(&tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> TestConstraintSystem<E> {
|
||||||
|
pub fn new() -> TestConstraintSystem<E> {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert(
|
||||||
|
"ONE".into(),
|
||||||
|
NamedObject::Var(TestConstraintSystem::<E>::one()),
|
||||||
|
);
|
||||||
|
|
||||||
|
TestConstraintSystem {
|
||||||
|
named_objects: map,
|
||||||
|
current_namespace: vec![],
|
||||||
|
constraints: vec![],
|
||||||
|
inputs: vec![(E::Fr::one(), "ONE".into())],
|
||||||
|
aux: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print(&self) -> String {
|
||||||
|
let mut s = String::new();
|
||||||
|
|
||||||
|
let negone = {
|
||||||
|
let mut tmp = E::Fr::one();
|
||||||
|
tmp.negate();
|
||||||
|
tmp
|
||||||
|
};
|
||||||
|
|
||||||
|
let powers_of_two = (0..E::Fr::NUM_BITS)
|
||||||
|
.map(|i| E::Fr::from_str("2").unwrap().pow(&[u64::from(i)]))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let pp = |s: &mut String, lc: &LinearCombination<E>| {
|
||||||
|
write!(s, "(").unwrap();
|
||||||
|
let mut is_first = true;
|
||||||
|
for (var, coeff) in proc_lc::<E>(lc.as_ref()) {
|
||||||
|
if coeff == negone {
|
||||||
|
write!(s, " - ").unwrap();
|
||||||
|
} else if !is_first {
|
||||||
|
write!(s, " + ").unwrap();
|
||||||
|
}
|
||||||
|
is_first = false;
|
||||||
|
|
||||||
|
if coeff != E::Fr::one() && coeff != negone {
|
||||||
|
for (i, x) in powers_of_two.iter().enumerate() {
|
||||||
|
if x == &coeff {
|
||||||
|
write!(s, "2^{} . ", i).unwrap();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(s, "{} . ", coeff).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
match var.0.get_unchecked() {
|
||||||
|
Index::Input(i) => {
|
||||||
|
write!(s, "`{}`", &self.inputs[i].1).unwrap();
|
||||||
|
}
|
||||||
|
Index::Aux(i) => {
|
||||||
|
write!(s, "`{}`", &self.aux[i].1).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_first {
|
||||||
|
// Nothing was visited, print 0.
|
||||||
|
write!(s, "0").unwrap();
|
||||||
|
}
|
||||||
|
write!(s, ")").unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
for &(ref a, ref b, ref c, ref name) in &self.constraints {
|
||||||
|
write!(&mut s, "\n").unwrap();
|
||||||
|
|
||||||
|
write!(&mut s, "{}: ", name).unwrap();
|
||||||
|
pp(&mut s, a);
|
||||||
|
write!(&mut s, " * ").unwrap();
|
||||||
|
pp(&mut s, b);
|
||||||
|
write!(&mut s, " = ").unwrap();
|
||||||
|
pp(&mut s, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(&mut s, "\n").unwrap();
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash(&self) -> String {
|
||||||
|
let mut h = Blake2sParams::new().hash_length(32).to_state();
|
||||||
|
{
|
||||||
|
let mut buf = [0u8; 24];
|
||||||
|
|
||||||
|
BigEndian::write_u64(&mut buf[0..8], self.inputs.len() as u64);
|
||||||
|
BigEndian::write_u64(&mut buf[8..16], self.aux.len() as u64);
|
||||||
|
BigEndian::write_u64(&mut buf[16..24], self.constraints.len() as u64);
|
||||||
|
h.update(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
for constraint in &self.constraints {
|
||||||
|
hash_lc::<E>(constraint.0.as_ref(), &mut h);
|
||||||
|
hash_lc::<E>(constraint.1.as_ref(), &mut h);
|
||||||
|
hash_lc::<E>(constraint.2.as_ref(), &mut h);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
for b in h.finalize().as_ref() {
|
||||||
|
s += &format!("{:02x}", b);
|
||||||
|
}
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn which_is_unsatisfied(&self) -> Option<&str> {
|
||||||
|
for &(ref a, ref b, ref c, ref path) in &self.constraints {
|
||||||
|
let mut a = eval_lc::<E>(a.as_ref(), &self.inputs, &self.aux);
|
||||||
|
let b = eval_lc::<E>(b.as_ref(), &self.inputs, &self.aux);
|
||||||
|
let c = eval_lc::<E>(c.as_ref(), &self.inputs, &self.aux);
|
||||||
|
|
||||||
|
a.mul_assign(&b);
|
||||||
|
|
||||||
|
if a != c {
|
||||||
|
return Some(&*path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_satisfied(&self) -> bool {
|
||||||
|
self.which_is_unsatisfied().is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_constraints(&self) -> usize {
|
||||||
|
self.constraints.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, path: &str, to: E::Fr) {
|
||||||
|
match self.named_objects.get(path) {
|
||||||
|
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
|
||||||
|
Index::Input(index) => self.inputs[index].0 = to,
|
||||||
|
Index::Aux(index) => self.aux[index].0 = to,
|
||||||
|
},
|
||||||
|
Some(e) => panic!(
|
||||||
|
"tried to set path `{}` to value, but `{:?}` already exists there.",
|
||||||
|
path, e
|
||||||
|
),
|
||||||
|
_ => panic!("no variable exists at path: {}", path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify(&self, expected: &[E::Fr]) -> bool {
|
||||||
|
assert_eq!(expected.len() + 1, self.inputs.len());
|
||||||
|
|
||||||
|
for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) {
|
||||||
|
if &a.0 != b {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_inputs(&self) -> usize {
|
||||||
|
self.inputs.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr {
|
||||||
|
let (assignment, name) = self.inputs[index].clone();
|
||||||
|
|
||||||
|
assert_eq!(path, name);
|
||||||
|
|
||||||
|
assignment
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&mut self, path: &str) -> E::Fr {
|
||||||
|
match self.named_objects.get(path) {
|
||||||
|
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
|
||||||
|
Index::Input(index) => self.inputs[index].0,
|
||||||
|
Index::Aux(index) => self.aux[index].0,
|
||||||
|
},
|
||||||
|
Some(e) => panic!(
|
||||||
|
"tried to get value of path `{}`, but `{:?}` exists there (not a variable)",
|
||||||
|
path, e
|
||||||
|
),
|
||||||
|
_ => panic!("no variable exists at path: {}", path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_named_obj(&mut self, path: String, to: NamedObject) {
|
||||||
|
if self.named_objects.contains_key(&path) {
|
||||||
|
panic!("tried to create object at existing path: {}", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.named_objects.insert(path, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_path(ns: &[String], this: String) -> String {
|
||||||
|
if this.chars().any(|a| a == '/') {
|
||||||
|
panic!("'/' is not allowed in names");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut name = String::new();
|
||||||
|
|
||||||
|
let mut needs_separation = false;
|
||||||
|
for ns in ns.iter().chain(Some(&this).into_iter()) {
|
||||||
|
if needs_separation {
|
||||||
|
name += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
name += ns;
|
||||||
|
needs_separation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
name
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
||||||
|
type Root = Self;
|
||||||
|
|
||||||
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
let index = self.aux.len();
|
||||||
|
let path = compute_path(&self.current_namespace, annotation().into());
|
||||||
|
self.aux.push((f()?, path.clone()));
|
||||||
|
let var = Variable::new_unchecked(Index::Aux(index));
|
||||||
|
self.set_named_obj(path, NamedObject::Var(var));
|
||||||
|
|
||||||
|
Ok(var)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
let index = self.inputs.len();
|
||||||
|
let path = compute_path(&self.current_namespace, annotation().into());
|
||||||
|
self.inputs.push((f()?, path.clone()));
|
||||||
|
let var = Variable::new_unchecked(Index::Input(index));
|
||||||
|
self.set_named_obj(path, NamedObject::Var(var));
|
||||||
|
|
||||||
|
Ok(var)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||||
|
where
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
{
|
||||||
|
let path = compute_path(&self.current_namespace, annotation().into());
|
||||||
|
let index = self.constraints.len();
|
||||||
|
self.set_named_obj(path.clone(), NamedObject::Constraint(index));
|
||||||
|
|
||||||
|
let a = a(LinearCombination::zero());
|
||||||
|
let b = b(LinearCombination::zero());
|
||||||
|
let c = c(LinearCombination::zero());
|
||||||
|
|
||||||
|
self.constraints.push((a, b, c, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||||
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
|
{
|
||||||
|
let name = name_fn().into();
|
||||||
|
let path = compute_path(&self.current_namespace, name.clone());
|
||||||
|
self.set_named_obj(path.clone(), NamedObject::Namespace);
|
||||||
|
self.current_namespace.push(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_namespace(&mut self) {
|
||||||
|
assert!(self.current_namespace.pop().is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cs() {
|
||||||
|
use ff::PrimeField;
|
||||||
|
use pairing::bls12_381::{Bls12, Fr};
|
||||||
|
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
assert_eq!(cs.num_constraints(), 0);
|
||||||
|
let a = cs
|
||||||
|
.namespace(|| "a")
|
||||||
|
.alloc(|| "var", || Ok(Fr::from_str("10").unwrap()))
|
||||||
|
.unwrap();
|
||||||
|
let b = cs
|
||||||
|
.namespace(|| "b")
|
||||||
|
.alloc(|| "var", || Ok(Fr::from_str("4").unwrap()))
|
||||||
|
.unwrap();
|
||||||
|
let c = cs
|
||||||
|
.alloc(|| "product", || Ok(Fr::from_str("40").unwrap()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
cs.enforce(|| "mult", |lc| lc + a, |lc| lc + b, |lc| lc + c);
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
assert_eq!(cs.num_constraints(), 1);
|
||||||
|
|
||||||
|
cs.set("a/var", Fr::from_str("4").unwrap());
|
||||||
|
|
||||||
|
let one = TestConstraintSystem::<Bls12>::one();
|
||||||
|
cs.enforce(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b);
|
||||||
|
|
||||||
|
assert!(!cs.is_satisfied());
|
||||||
|
assert!(cs.which_is_unsatisfied() == Some("mult"));
|
||||||
|
|
||||||
|
assert!(cs.get("product") == Fr::from_str("40").unwrap());
|
||||||
|
|
||||||
|
cs.set("product", Fr::from_str("16").unwrap());
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cs = cs.namespace(|| "test1");
|
||||||
|
let mut cs = cs.namespace(|| "test2");
|
||||||
|
cs.alloc(|| "hehe", || Ok(Fr::one())).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(cs.get("test1/test2/hehe") == Fr::one());
|
||||||
|
}
|
||||||
757
bellman/src/gadgets/uint32.rs
Normal file
757
bellman/src/gadgets/uint32.rs
Normal file
@@ -0,0 +1,757 @@
|
|||||||
|
//! Circuit representation of a [`u32`], with helpers for the [`sha256`]
|
||||||
|
//! gadgets.
|
||||||
|
//!
|
||||||
|
//! [`sha256`]: crate::gadgets::sha256
|
||||||
|
|
||||||
|
use ff::{Field, PrimeField, ScalarEngine};
|
||||||
|
|
||||||
|
use crate::{ConstraintSystem, LinearCombination, SynthesisError};
|
||||||
|
|
||||||
|
use super::boolean::{AllocatedBit, Boolean};
|
||||||
|
|
||||||
|
use super::multieq::MultiEq;
|
||||||
|
|
||||||
|
/// Represents an interpretation of 32 `Boolean` objects as an
|
||||||
|
/// unsigned integer.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct UInt32 {
|
||||||
|
// Least significant bit first
|
||||||
|
bits: Vec<Boolean>,
|
||||||
|
value: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UInt32 {
|
||||||
|
/// Construct a constant `UInt32` from a `u32`
|
||||||
|
pub fn constant(value: u32) -> Self {
|
||||||
|
let mut bits = Vec::with_capacity(32);
|
||||||
|
|
||||||
|
let mut tmp = value;
|
||||||
|
for _ in 0..32 {
|
||||||
|
if tmp & 1 == 1 {
|
||||||
|
bits.push(Boolean::constant(true))
|
||||||
|
} else {
|
||||||
|
bits.push(Boolean::constant(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 {
|
||||||
|
bits,
|
||||||
|
value: Some(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a `UInt32` in the constraint system
|
||||||
|
pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
let values = match value {
|
||||||
|
Some(mut val) => {
|
||||||
|
let mut v = Vec::with_capacity(32);
|
||||||
|
|
||||||
|
for _ in 0..32 {
|
||||||
|
v.push(Some(val & 1 == 1));
|
||||||
|
val >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
v
|
||||||
|
}
|
||||||
|
None => vec![None; 32],
|
||||||
|
};
|
||||||
|
|
||||||
|
let bits = values
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, v)| {
|
||||||
|
Ok(Boolean::from(AllocatedBit::alloc(
|
||||||
|
cs.namespace(|| format!("allocated bit {}", i)),
|
||||||
|
v,
|
||||||
|
)?))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
||||||
|
|
||||||
|
Ok(UInt32 { bits, value })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_bits_be(self) -> Vec<Boolean> {
|
||||||
|
let mut ret = self.bits;
|
||||||
|
ret.reverse();
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bits_be(bits: &[Boolean]) -> Self {
|
||||||
|
assert_eq!(bits.len(), 32);
|
||||||
|
|
||||||
|
let mut value = Some(0u32);
|
||||||
|
for b in bits {
|
||||||
|
value.as_mut().map(|v| *v <<= 1);
|
||||||
|
|
||||||
|
match b.get_value() {
|
||||||
|
Some(true) => {
|
||||||
|
value.as_mut().map(|v| *v |= 1);
|
||||||
|
}
|
||||||
|
Some(false) => {}
|
||||||
|
None => {
|
||||||
|
value = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 {
|
||||||
|
value,
|
||||||
|
bits: bits.iter().rev().cloned().collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Turns this `UInt32` into its little-endian byte order representation.
|
||||||
|
pub fn into_bits(self) -> Vec<Boolean> {
|
||||||
|
self.bits
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a little-endian byte order representation of bits into a
|
||||||
|
/// `UInt32`.
|
||||||
|
pub fn from_bits(bits: &[Boolean]) -> Self {
|
||||||
|
assert_eq!(bits.len(), 32);
|
||||||
|
|
||||||
|
let new_bits = bits.to_vec();
|
||||||
|
|
||||||
|
let mut value = Some(0u32);
|
||||||
|
for b in new_bits.iter().rev() {
|
||||||
|
value.as_mut().map(|v| *v <<= 1);
|
||||||
|
|
||||||
|
match *b {
|
||||||
|
Boolean::Constant(b) => {
|
||||||
|
if b {
|
||||||
|
value.as_mut().map(|v| *v |= 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Boolean::Is(ref b) => match b.get_value() {
|
||||||
|
Some(true) => {
|
||||||
|
value.as_mut().map(|v| *v |= 1);
|
||||||
|
}
|
||||||
|
Some(false) => {}
|
||||||
|
None => value = None,
|
||||||
|
},
|
||||||
|
Boolean::Not(ref b) => match b.get_value() {
|
||||||
|
Some(false) => {
|
||||||
|
value.as_mut().map(|v| *v |= 1);
|
||||||
|
}
|
||||||
|
Some(true) => {}
|
||||||
|
None => value = None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 {
|
||||||
|
value,
|
||||||
|
bits: new_bits,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotr(&self, by: usize) -> Self {
|
||||||
|
let by = by % 32;
|
||||||
|
|
||||||
|
let new_bits = self
|
||||||
|
.bits
|
||||||
|
.iter()
|
||||||
|
.skip(by)
|
||||||
|
.chain(self.bits.iter())
|
||||||
|
.take(32)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
UInt32 {
|
||||||
|
bits: new_bits,
|
||||||
|
value: self.value.map(|v| v.rotate_right(by as u32)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shr(&self, by: usize) -> Self {
|
||||||
|
let by = by % 32;
|
||||||
|
|
||||||
|
let fill = Boolean::constant(false);
|
||||||
|
|
||||||
|
let new_bits = self
|
||||||
|
.bits
|
||||||
|
.iter() // The bits are least significant first
|
||||||
|
.skip(by) // Skip the bits that will be lost during the shift
|
||||||
|
.chain(Some(&fill).into_iter().cycle()) // Rest will be zeros
|
||||||
|
.take(32) // Only 32 bits needed!
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
UInt32 {
|
||||||
|
bits: new_bits,
|
||||||
|
value: self.value.map(|v| v >> by as u32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn triop<E, CS, F, U>(
|
||||||
|
mut cs: CS,
|
||||||
|
a: &Self,
|
||||||
|
b: &Self,
|
||||||
|
c: &Self,
|
||||||
|
tri_fn: F,
|
||||||
|
circuit_fn: U,
|
||||||
|
) -> Result<Self, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
F: Fn(u32, u32, u32) -> u32,
|
||||||
|
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
|
||||||
|
{
|
||||||
|
let new_value = match (a.value, b.value, c.value) {
|
||||||
|
(Some(a), Some(b), Some(c)) => Some(tri_fn(a, b, c)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bits = a
|
||||||
|
.bits
|
||||||
|
.iter()
|
||||||
|
.zip(b.bits.iter())
|
||||||
|
.zip(c.bits.iter())
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
Ok(UInt32 {
|
||||||
|
bits,
|
||||||
|
value: new_value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the `maj` value (a and b) xor (a and c) xor (b and c)
|
||||||
|
/// during SHA256.
|
||||||
|
pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
Self::triop(
|
||||||
|
cs,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
|a, b, c| (a & b) ^ (a & c) ^ (b & c),
|
||||||
|
|cs, i, a, b, c| Boolean::sha256_maj(cs.namespace(|| format!("maj {}", i)), a, b, c),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the `ch` value `(a and b) xor ((not a) and c)`
|
||||||
|
/// during SHA256.
|
||||||
|
pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
Self::triop(
|
||||||
|
cs,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
|a, b, c| (a & b) ^ ((!a) & c),
|
||||||
|
|cs, i, a, b, c| Boolean::sha256_ch(cs.namespace(|| format!("ch {}", i)), a, b, c),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// XOR this `UInt32` with another `UInt32`
|
||||||
|
pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
{
|
||||||
|
let new_value = match (self.value, other.value) {
|
||||||
|
(Some(a), Some(b)) => Some(a ^ b),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bits = self
|
||||||
|
.bits
|
||||||
|
.iter()
|
||||||
|
.zip(other.bits.iter())
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
Ok(UInt32 {
|
||||||
|
bits,
|
||||||
|
value: new_value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform modular addition of several `UInt32` objects.
|
||||||
|
pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
|
||||||
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
|
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
||||||
|
{
|
||||||
|
// Make some arbitrary bounds for ourselves to avoid overflows
|
||||||
|
// in the scalar field
|
||||||
|
assert!(E::Fr::NUM_BITS >= 64);
|
||||||
|
assert!(operands.len() >= 2); // Weird trivial cases that should never happen
|
||||||
|
assert!(operands.len() <= 10);
|
||||||
|
|
||||||
|
// Compute the maximum value of the sum so we allocate enough bits for
|
||||||
|
// the result
|
||||||
|
let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value()));
|
||||||
|
|
||||||
|
// Keep track of the resulting value
|
||||||
|
let mut result_value = Some(0u64);
|
||||||
|
|
||||||
|
// This is a linear combination that we will enforce to equal the
|
||||||
|
// output
|
||||||
|
let mut lc = LinearCombination::zero();
|
||||||
|
|
||||||
|
let mut all_constants = true;
|
||||||
|
|
||||||
|
// Iterate over the operands
|
||||||
|
for op in operands {
|
||||||
|
// Accumulate the value
|
||||||
|
match op.value {
|
||||||
|
Some(val) => {
|
||||||
|
result_value.as_mut().map(|v| *v += u64::from(val));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// If any of our operands have unknown value, we won't
|
||||||
|
// know the value of the result
|
||||||
|
result_value = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over each bit of the operand and add the operand to
|
||||||
|
// the linear combination
|
||||||
|
let mut coeff = E::Fr::one();
|
||||||
|
for bit in &op.bits {
|
||||||
|
lc = lc + &bit.lc(CS::one(), coeff);
|
||||||
|
|
||||||
|
all_constants &= bit.is_constant();
|
||||||
|
|
||||||
|
coeff.double();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The value of the actual result is modulo 2^32
|
||||||
|
let modular_value = result_value.map(|v| v as u32);
|
||||||
|
|
||||||
|
if all_constants && modular_value.is_some() {
|
||||||
|
// We can just return a constant, rather than
|
||||||
|
// unpacking the result into allocated bits.
|
||||||
|
|
||||||
|
return Ok(UInt32::constant(modular_value.unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage area for the resulting bits
|
||||||
|
let mut result_bits = vec![];
|
||||||
|
|
||||||
|
// Linear combination representing the output,
|
||||||
|
// for comparison with the sum of the operands
|
||||||
|
let mut result_lc = LinearCombination::zero();
|
||||||
|
|
||||||
|
// Allocate each bit of the result
|
||||||
|
let mut coeff = E::Fr::one();
|
||||||
|
let mut i = 0;
|
||||||
|
while max_value != 0 {
|
||||||
|
// Allocate the bit
|
||||||
|
let b = AllocatedBit::alloc(
|
||||||
|
cs.namespace(|| format!("result bit {}", i)),
|
||||||
|
result_value.map(|v| (v >> i) & 1 == 1),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Add this bit to the result combination
|
||||||
|
result_lc = result_lc + (coeff, b.get_variable());
|
||||||
|
|
||||||
|
result_bits.push(b.into());
|
||||||
|
|
||||||
|
max_value >>= 1;
|
||||||
|
i += 1;
|
||||||
|
coeff.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce equality between the sum and result
|
||||||
|
cs.get_root().enforce_equal(i, &lc, &result_lc);
|
||||||
|
|
||||||
|
// Discard carry bits that we don't care about
|
||||||
|
result_bits.truncate(32);
|
||||||
|
|
||||||
|
Ok(UInt32 {
|
||||||
|
bits: result_bits,
|
||||||
|
value: modular_value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::UInt32;
|
||||||
|
use crate::gadgets::boolean::Boolean;
|
||||||
|
use crate::gadgets::multieq::MultiEq;
|
||||||
|
use crate::gadgets::test::*;
|
||||||
|
use crate::ConstraintSystem;
|
||||||
|
use ff::Field;
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand_core::{RngCore, SeedableRng};
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uint32_from_bits_be() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let v = (0..32)
|
||||||
|
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let b = UInt32::from_bits_be(&v);
|
||||||
|
|
||||||
|
for (i, bit) in b.bits.iter().enumerate() {
|
||||||
|
match *bit {
|
||||||
|
Boolean::Constant(bit) => {
|
||||||
|
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_to_be_same = b.into_bits_be();
|
||||||
|
|
||||||
|
for x in v.iter().zip(expected_to_be_same.iter()) {
|
||||||
|
match x {
|
||||||
|
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}
|
||||||
|
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uint32_from_bits() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let v = (0..32)
|
||||||
|
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let b = UInt32::from_bits(&v);
|
||||||
|
|
||||||
|
for (i, bit) in b.bits.iter().enumerate() {
|
||||||
|
match *bit {
|
||||||
|
Boolean::Constant(bit) => {
|
||||||
|
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_to_be_same = b.into_bits();
|
||||||
|
|
||||||
|
for x in v.iter().zip(expected_to_be_same.iter()) {
|
||||||
|
match x {
|
||||||
|
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}
|
||||||
|
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uint32_xor() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let a = rng.next_u32();
|
||||||
|
let b = rng.next_u32();
|
||||||
|
let c = rng.next_u32();
|
||||||
|
|
||||||
|
let mut expected = a ^ b ^ c;
|
||||||
|
|
||||||
|
let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap();
|
||||||
|
let b_bit = UInt32::constant(b);
|
||||||
|
let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap();
|
||||||
|
|
||||||
|
let r = a_bit.xor(cs.namespace(|| "first xor"), &b_bit).unwrap();
|
||||||
|
let r = r.xor(cs.namespace(|| "second xor"), &c_bit).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
|
for b in r.bits.iter() {
|
||||||
|
match *b {
|
||||||
|
Boolean::Is(ref b) => {
|
||||||
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
Boolean::Not(ref b) => {
|
||||||
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
Boolean::Constant(b) => {
|
||||||
|
assert!(b == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expected >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uint32_addmany_constants() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let a = rng.next_u32();
|
||||||
|
let b = rng.next_u32();
|
||||||
|
let c = rng.next_u32();
|
||||||
|
|
||||||
|
let a_bit = UInt32::constant(a);
|
||||||
|
let b_bit = UInt32::constant(b);
|
||||||
|
let c_bit = UInt32::constant(c);
|
||||||
|
|
||||||
|
let mut expected = a.wrapping_add(b).wrapping_add(c);
|
||||||
|
|
||||||
|
let r = {
|
||||||
|
let mut cs = MultiEq::new(&mut cs);
|
||||||
|
let r =
|
||||||
|
UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
|
||||||
|
r
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
|
for b in r.bits.iter() {
|
||||||
|
match *b {
|
||||||
|
Boolean::Is(_) => panic!(),
|
||||||
|
Boolean::Not(_) => panic!(),
|
||||||
|
Boolean::Constant(b) => {
|
||||||
|
assert!(b == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expected >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uint32_addmany() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let a = rng.next_u32();
|
||||||
|
let b = rng.next_u32();
|
||||||
|
let c = rng.next_u32();
|
||||||
|
let d = rng.next_u32();
|
||||||
|
|
||||||
|
let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d);
|
||||||
|
|
||||||
|
let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap();
|
||||||
|
let b_bit = UInt32::constant(b);
|
||||||
|
let c_bit = UInt32::constant(c);
|
||||||
|
let d_bit = UInt32::alloc(cs.namespace(|| "d_bit"), Some(d)).unwrap();
|
||||||
|
|
||||||
|
let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
|
||||||
|
let r = {
|
||||||
|
let mut cs = MultiEq::new(&mut cs);
|
||||||
|
UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
|
for b in r.bits.iter() {
|
||||||
|
match *b {
|
||||||
|
Boolean::Is(ref b) => {
|
||||||
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
Boolean::Not(ref b) => {
|
||||||
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
Boolean::Constant(_) => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
expected >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flip a bit and see if the addition constraint still works
|
||||||
|
if cs.get("addition/result bit 0/boolean").is_zero() {
|
||||||
|
cs.set("addition/result bit 0/boolean", Field::one());
|
||||||
|
} else {
|
||||||
|
cs.set("addition/result bit 0/boolean", Field::zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(!cs.is_satisfied());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uint32_rotr() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let mut num = rng.next_u32();
|
||||||
|
|
||||||
|
let a = UInt32::constant(num);
|
||||||
|
|
||||||
|
for i in 0..32 {
|
||||||
|
let b = a.rotr(i);
|
||||||
|
assert_eq!(a.bits.len(), b.bits.len());
|
||||||
|
|
||||||
|
assert!(b.value.unwrap() == num);
|
||||||
|
|
||||||
|
let mut tmp = num;
|
||||||
|
for b in &b.bits {
|
||||||
|
match *b {
|
||||||
|
Boolean::Constant(b) => {
|
||||||
|
assert_eq!(b, tmp & 1 == 1);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
num = num.rotate_right(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uint32_shr() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..50 {
|
||||||
|
for i in 0..60 {
|
||||||
|
let num = rng.next_u32();
|
||||||
|
let a = UInt32::constant(num).shr(i);
|
||||||
|
let b = UInt32::constant(num.wrapping_shr(i as u32));
|
||||||
|
|
||||||
|
assert_eq!(a.value.unwrap(), num.wrapping_shr(i as u32));
|
||||||
|
|
||||||
|
assert_eq!(a.bits.len(), b.bits.len());
|
||||||
|
for (a, b) in a.bits.iter().zip(b.bits.iter()) {
|
||||||
|
assert_eq!(a.get_value().unwrap(), b.get_value().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uint32_sha256_maj() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let a = rng.next_u32();
|
||||||
|
let b = rng.next_u32();
|
||||||
|
let c = rng.next_u32();
|
||||||
|
|
||||||
|
let mut expected = (a & b) ^ (a & c) ^ (b & c);
|
||||||
|
|
||||||
|
let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap();
|
||||||
|
let b_bit = UInt32::constant(b);
|
||||||
|
let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap();
|
||||||
|
|
||||||
|
let r = UInt32::sha256_maj(&mut cs, &a_bit, &b_bit, &c_bit).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
|
for b in r.bits.iter() {
|
||||||
|
match b {
|
||||||
|
&Boolean::Is(ref b) => {
|
||||||
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
&Boolean::Not(ref b) => {
|
||||||
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
&Boolean::Constant(b) => {
|
||||||
|
assert!(b == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expected >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uint32_sha256_ch() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
|
let a = rng.next_u32();
|
||||||
|
let b = rng.next_u32();
|
||||||
|
let c = rng.next_u32();
|
||||||
|
|
||||||
|
let mut expected = (a & b) ^ ((!a) & c);
|
||||||
|
|
||||||
|
let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap();
|
||||||
|
let b_bit = UInt32::constant(b);
|
||||||
|
let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap();
|
||||||
|
|
||||||
|
let r = UInt32::sha256_ch(&mut cs, &a_bit, &b_bit, &c_bit).unwrap();
|
||||||
|
|
||||||
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
|
for b in r.bits.iter() {
|
||||||
|
match b {
|
||||||
|
&Boolean::Is(ref b) => {
|
||||||
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
&Boolean::Not(ref b) => {
|
||||||
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
&Boolean::Constant(b) => {
|
||||||
|
assert!(b == (expected & 1 == 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expected >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
478
bellman/src/groth16/generator.rs
Normal file
478
bellman/src/groth16/generator.rs
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ff::{Field, PrimeField};
|
||||||
|
use group::{CurveAffine, CurveProjective, Wnaf};
|
||||||
|
use pairing::Engine;
|
||||||
|
|
||||||
|
use super::{Parameters, VerifyingKey};
|
||||||
|
|
||||||
|
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||||
|
|
||||||
|
use crate::domain::{EvaluationDomain, Scalar};
|
||||||
|
|
||||||
|
use crate::multicore::Worker;
|
||||||
|
|
||||||
|
/// Generates a random common reference string for
|
||||||
|
/// a circuit.
|
||||||
|
pub fn generate_random_parameters<E, C, R>(
|
||||||
|
circuit: C,
|
||||||
|
rng: &mut R,
|
||||||
|
) -> Result<Parameters<E>, SynthesisError>
|
||||||
|
where
|
||||||
|
E: Engine,
|
||||||
|
C: Circuit<E>,
|
||||||
|
R: RngCore,
|
||||||
|
{
|
||||||
|
let g1 = E::G1::random(rng);
|
||||||
|
let g2 = E::G2::random(rng);
|
||||||
|
let alpha = E::Fr::random(rng);
|
||||||
|
let beta = E::Fr::random(rng);
|
||||||
|
let gamma = E::Fr::random(rng);
|
||||||
|
let delta = E::Fr::random(rng);
|
||||||
|
let tau = E::Fr::random(rng);
|
||||||
|
|
||||||
|
generate_parameters::<E, C>(circuit, g1, g2, alpha, beta, gamma, delta, tau)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is our assembly structure that we'll use to synthesize the
|
||||||
|
/// circuit into a QAP.
|
||||||
|
struct KeypairAssembly<E: Engine> {
|
||||||
|
num_inputs: usize,
|
||||||
|
num_aux: usize,
|
||||||
|
num_constraints: usize,
|
||||||
|
at_inputs: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
bt_inputs: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
ct_inputs: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
at_aux: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
bt_aux: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
ct_aux: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
|
||||||
|
type Root = Self;
|
||||||
|
|
||||||
|
fn alloc<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
// There is no assignment, so we don't even invoke the
|
||||||
|
// function for obtaining one.
|
||||||
|
|
||||||
|
let index = self.num_aux;
|
||||||
|
self.num_aux += 1;
|
||||||
|
|
||||||
|
self.at_aux.push(vec![]);
|
||||||
|
self.bt_aux.push(vec![]);
|
||||||
|
self.ct_aux.push(vec![]);
|
||||||
|
|
||||||
|
Ok(Variable(Index::Aux(index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
// There is no assignment, so we don't even invoke the
|
||||||
|
// function for obtaining one.
|
||||||
|
|
||||||
|
let index = self.num_inputs;
|
||||||
|
self.num_inputs += 1;
|
||||||
|
|
||||||
|
self.at_inputs.push(vec![]);
|
||||||
|
self.bt_inputs.push(vec![]);
|
||||||
|
self.ct_inputs.push(vec![]);
|
||||||
|
|
||||||
|
Ok(Variable(Index::Input(index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
|
||||||
|
where
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
{
|
||||||
|
fn eval<E: Engine>(
|
||||||
|
l: LinearCombination<E>,
|
||||||
|
inputs: &mut [Vec<(E::Fr, usize)>],
|
||||||
|
aux: &mut [Vec<(E::Fr, usize)>],
|
||||||
|
this_constraint: usize,
|
||||||
|
) {
|
||||||
|
for (index, coeff) in l.0 {
|
||||||
|
match index {
|
||||||
|
Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)),
|
||||||
|
Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eval(
|
||||||
|
a(LinearCombination::zero()),
|
||||||
|
&mut self.at_inputs,
|
||||||
|
&mut self.at_aux,
|
||||||
|
self.num_constraints,
|
||||||
|
);
|
||||||
|
eval(
|
||||||
|
b(LinearCombination::zero()),
|
||||||
|
&mut self.bt_inputs,
|
||||||
|
&mut self.bt_aux,
|
||||||
|
self.num_constraints,
|
||||||
|
);
|
||||||
|
eval(
|
||||||
|
c(LinearCombination::zero()),
|
||||||
|
&mut self.ct_inputs,
|
||||||
|
&mut self.ct_aux,
|
||||||
|
self.num_constraints,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.num_constraints += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_namespace<NR, N>(&mut self, _: N)
|
||||||
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
|
{
|
||||||
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_namespace(&mut self) {
|
||||||
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create parameters for a circuit, given some toxic waste.
|
||||||
|
pub fn generate_parameters<E, C>(
|
||||||
|
circuit: C,
|
||||||
|
g1: E::G1,
|
||||||
|
g2: E::G2,
|
||||||
|
alpha: E::Fr,
|
||||||
|
beta: E::Fr,
|
||||||
|
gamma: E::Fr,
|
||||||
|
delta: E::Fr,
|
||||||
|
tau: E::Fr,
|
||||||
|
) -> Result<Parameters<E>, SynthesisError>
|
||||||
|
where
|
||||||
|
E: Engine,
|
||||||
|
C: Circuit<E>,
|
||||||
|
{
|
||||||
|
let mut assembly = KeypairAssembly {
|
||||||
|
num_inputs: 0,
|
||||||
|
num_aux: 0,
|
||||||
|
num_constraints: 0,
|
||||||
|
at_inputs: vec![],
|
||||||
|
bt_inputs: vec![],
|
||||||
|
ct_inputs: vec![],
|
||||||
|
at_aux: vec![],
|
||||||
|
bt_aux: vec![],
|
||||||
|
ct_aux: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate the "one" input variable
|
||||||
|
assembly.alloc_input(|| "", || Ok(E::Fr::one()))?;
|
||||||
|
|
||||||
|
// Synthesize the circuit.
|
||||||
|
circuit.synthesize(&mut assembly)?;
|
||||||
|
|
||||||
|
// Input constraints to ensure full density of IC query
|
||||||
|
// x * 0 = 0
|
||||||
|
for i in 0..assembly.num_inputs {
|
||||||
|
assembly.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create bases for blind evaluation of polynomials at tau
|
||||||
|
let powers_of_tau = vec![Scalar::<E>(E::Fr::zero()); assembly.num_constraints];
|
||||||
|
let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?;
|
||||||
|
|
||||||
|
// Compute G1 window table
|
||||||
|
let mut g1_wnaf = Wnaf::new();
|
||||||
|
let g1_wnaf = g1_wnaf.base(g1, {
|
||||||
|
// H query
|
||||||
|
(powers_of_tau.as_ref().len() - 1)
|
||||||
|
// IC/L queries
|
||||||
|
+ assembly.num_inputs + assembly.num_aux
|
||||||
|
// A query
|
||||||
|
+ assembly.num_inputs + assembly.num_aux
|
||||||
|
// B query
|
||||||
|
+ assembly.num_inputs + assembly.num_aux
|
||||||
|
});
|
||||||
|
|
||||||
|
// Compute G2 window table
|
||||||
|
let mut g2_wnaf = Wnaf::new();
|
||||||
|
let g2_wnaf = g2_wnaf.base(g2, {
|
||||||
|
// B query
|
||||||
|
assembly.num_inputs + assembly.num_aux
|
||||||
|
});
|
||||||
|
|
||||||
|
let gamma_inverse = gamma.inverse().ok_or(SynthesisError::UnexpectedIdentity)?;
|
||||||
|
let delta_inverse = delta.inverse().ok_or(SynthesisError::UnexpectedIdentity)?;
|
||||||
|
|
||||||
|
let worker = Worker::new();
|
||||||
|
|
||||||
|
let mut h = vec![E::G1::zero(); powers_of_tau.as_ref().len() - 1];
|
||||||
|
{
|
||||||
|
// Compute powers of tau
|
||||||
|
{
|
||||||
|
let powers_of_tau = powers_of_tau.as_mut();
|
||||||
|
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
||||||
|
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
|
let mut current_tau_power = tau.pow(&[(i * chunk) as u64]);
|
||||||
|
|
||||||
|
for p in powers_of_tau {
|
||||||
|
p.0 = current_tau_power;
|
||||||
|
current_tau_power.mul_assign(&tau);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// coeff = t(x) / delta
|
||||||
|
let mut coeff = powers_of_tau.z(&tau);
|
||||||
|
coeff.mul_assign(&delta_inverse);
|
||||||
|
|
||||||
|
// Compute the H query with multiple threads
|
||||||
|
worker.scope(h.len(), |scope, chunk| {
|
||||||
|
for (h, p) in h
|
||||||
|
.chunks_mut(chunk)
|
||||||
|
.zip(powers_of_tau.as_ref().chunks(chunk))
|
||||||
|
{
|
||||||
|
let mut g1_wnaf = g1_wnaf.shared();
|
||||||
|
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
|
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
|
||||||
|
for (h, p) in h.iter_mut().zip(p.iter()) {
|
||||||
|
// Compute final exponent
|
||||||
|
let mut exp = p.0;
|
||||||
|
exp.mul_assign(&coeff);
|
||||||
|
|
||||||
|
// Exponentiate
|
||||||
|
*h = g1_wnaf.scalar(exp.into_repr());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch normalize
|
||||||
|
E::G1::batch_normalization(h);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use inverse FFT to convert powers of tau to Lagrange coefficients
|
||||||
|
powers_of_tau.ifft(&worker);
|
||||||
|
let powers_of_tau = powers_of_tau.into_coeffs();
|
||||||
|
|
||||||
|
let mut a = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
|
||||||
|
let mut b_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
|
||||||
|
let mut b_g2 = vec![E::G2::zero(); assembly.num_inputs + assembly.num_aux];
|
||||||
|
let mut ic = vec![E::G1::zero(); assembly.num_inputs];
|
||||||
|
let mut l = vec![E::G1::zero(); assembly.num_aux];
|
||||||
|
|
||||||
|
fn eval<E: Engine>(
|
||||||
|
// wNAF window tables
|
||||||
|
g1_wnaf: &Wnaf<usize, &[E::G1], &mut Vec<i64>>,
|
||||||
|
g2_wnaf: &Wnaf<usize, &[E::G2], &mut Vec<i64>>,
|
||||||
|
|
||||||
|
// Lagrange coefficients for tau
|
||||||
|
powers_of_tau: &[Scalar<E>],
|
||||||
|
|
||||||
|
// QAP polynomials
|
||||||
|
at: &[Vec<(E::Fr, usize)>],
|
||||||
|
bt: &[Vec<(E::Fr, usize)>],
|
||||||
|
ct: &[Vec<(E::Fr, usize)>],
|
||||||
|
|
||||||
|
// Resulting evaluated QAP polynomials
|
||||||
|
a: &mut [E::G1],
|
||||||
|
b_g1: &mut [E::G1],
|
||||||
|
b_g2: &mut [E::G2],
|
||||||
|
ext: &mut [E::G1],
|
||||||
|
|
||||||
|
// Inverse coefficient for ext elements
|
||||||
|
inv: &E::Fr,
|
||||||
|
|
||||||
|
// Trapdoors
|
||||||
|
alpha: &E::Fr,
|
||||||
|
beta: &E::Fr,
|
||||||
|
|
||||||
|
// Worker
|
||||||
|
worker: &Worker,
|
||||||
|
) {
|
||||||
|
// Sanity check
|
||||||
|
assert_eq!(a.len(), at.len());
|
||||||
|
assert_eq!(a.len(), bt.len());
|
||||||
|
assert_eq!(a.len(), ct.len());
|
||||||
|
assert_eq!(a.len(), b_g1.len());
|
||||||
|
assert_eq!(a.len(), b_g2.len());
|
||||||
|
assert_eq!(a.len(), ext.len());
|
||||||
|
|
||||||
|
// Evaluate polynomials in multiple threads
|
||||||
|
worker.scope(a.len(), |scope, chunk| {
|
||||||
|
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
|
||||||
|
.chunks_mut(chunk)
|
||||||
|
.zip(b_g1.chunks_mut(chunk))
|
||||||
|
.zip(b_g2.chunks_mut(chunk))
|
||||||
|
.zip(ext.chunks_mut(chunk))
|
||||||
|
.zip(at.chunks(chunk))
|
||||||
|
.zip(bt.chunks(chunk))
|
||||||
|
.zip(ct.chunks(chunk))
|
||||||
|
{
|
||||||
|
let mut g1_wnaf = g1_wnaf.shared();
|
||||||
|
let mut g2_wnaf = g2_wnaf.shared();
|
||||||
|
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
|
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
|
||||||
|
.iter_mut()
|
||||||
|
.zip(b_g1.iter_mut())
|
||||||
|
.zip(b_g2.iter_mut())
|
||||||
|
.zip(ext.iter_mut())
|
||||||
|
.zip(at.iter())
|
||||||
|
.zip(bt.iter())
|
||||||
|
.zip(ct.iter())
|
||||||
|
{
|
||||||
|
fn eval_at_tau<E: Engine>(
|
||||||
|
powers_of_tau: &[Scalar<E>],
|
||||||
|
p: &[(E::Fr, usize)],
|
||||||
|
) -> E::Fr {
|
||||||
|
let mut acc = E::Fr::zero();
|
||||||
|
|
||||||
|
for &(ref coeff, index) in p {
|
||||||
|
let mut n = powers_of_tau[index].0;
|
||||||
|
n.mul_assign(coeff);
|
||||||
|
acc.add_assign(&n);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate QAP polynomials at tau
|
||||||
|
let mut at = eval_at_tau(powers_of_tau, at);
|
||||||
|
let mut bt = eval_at_tau(powers_of_tau, bt);
|
||||||
|
let ct = eval_at_tau(powers_of_tau, ct);
|
||||||
|
|
||||||
|
// Compute A query (in G1)
|
||||||
|
if !at.is_zero() {
|
||||||
|
*a = g1_wnaf.scalar(at.into_repr());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute B query (in G1/G2)
|
||||||
|
if !bt.is_zero() {
|
||||||
|
let bt_repr = bt.into_repr();
|
||||||
|
*b_g1 = g1_wnaf.scalar(bt_repr);
|
||||||
|
*b_g2 = g2_wnaf.scalar(bt_repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
at.mul_assign(&beta);
|
||||||
|
bt.mul_assign(&alpha);
|
||||||
|
|
||||||
|
let mut e = at;
|
||||||
|
e.add_assign(&bt);
|
||||||
|
e.add_assign(&ct);
|
||||||
|
e.mul_assign(inv);
|
||||||
|
|
||||||
|
*ext = g1_wnaf.scalar(e.into_repr());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch normalize
|
||||||
|
E::G1::batch_normalization(a);
|
||||||
|
E::G1::batch_normalization(b_g1);
|
||||||
|
E::G2::batch_normalization(b_g2);
|
||||||
|
E::G1::batch_normalization(ext);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate for inputs.
|
||||||
|
eval(
|
||||||
|
&g1_wnaf,
|
||||||
|
&g2_wnaf,
|
||||||
|
&powers_of_tau,
|
||||||
|
&assembly.at_inputs,
|
||||||
|
&assembly.bt_inputs,
|
||||||
|
&assembly.ct_inputs,
|
||||||
|
&mut a[0..assembly.num_inputs],
|
||||||
|
&mut b_g1[0..assembly.num_inputs],
|
||||||
|
&mut b_g2[0..assembly.num_inputs],
|
||||||
|
&mut ic,
|
||||||
|
&gamma_inverse,
|
||||||
|
&alpha,
|
||||||
|
&beta,
|
||||||
|
&worker,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Evaluate for auxiliary variables.
|
||||||
|
eval(
|
||||||
|
&g1_wnaf,
|
||||||
|
&g2_wnaf,
|
||||||
|
&powers_of_tau,
|
||||||
|
&assembly.at_aux,
|
||||||
|
&assembly.bt_aux,
|
||||||
|
&assembly.ct_aux,
|
||||||
|
&mut a[assembly.num_inputs..],
|
||||||
|
&mut b_g1[assembly.num_inputs..],
|
||||||
|
&mut b_g2[assembly.num_inputs..],
|
||||||
|
&mut l,
|
||||||
|
&delta_inverse,
|
||||||
|
&alpha,
|
||||||
|
&beta,
|
||||||
|
&worker,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Don't allow any elements be unconstrained, so that
|
||||||
|
// the L query is always fully dense.
|
||||||
|
for e in l.iter() {
|
||||||
|
if e.is_zero() {
|
||||||
|
return Err(SynthesisError::UnconstrainedVariable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let g1 = g1.into_affine();
|
||||||
|
let g2 = g2.into_affine();
|
||||||
|
|
||||||
|
let vk = VerifyingKey::<E> {
|
||||||
|
alpha_g1: g1.mul(alpha).into_affine(),
|
||||||
|
beta_g1: g1.mul(beta).into_affine(),
|
||||||
|
beta_g2: g2.mul(beta).into_affine(),
|
||||||
|
gamma_g2: g2.mul(gamma).into_affine(),
|
||||||
|
delta_g1: g1.mul(delta).into_affine(),
|
||||||
|
delta_g2: g2.mul(delta).into_affine(),
|
||||||
|
ic: ic.into_iter().map(|e| e.into_affine()).collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Parameters {
|
||||||
|
vk,
|
||||||
|
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
|
||||||
|
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
|
||||||
|
|
||||||
|
// Filter points at infinity away from A/B queries
|
||||||
|
a: Arc::new(
|
||||||
|
a.into_iter()
|
||||||
|
.filter(|e| !e.is_zero())
|
||||||
|
.map(|e| e.into_affine())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
b_g1: Arc::new(
|
||||||
|
b_g1.into_iter()
|
||||||
|
.filter(|e| !e.is_zero())
|
||||||
|
.map(|e| e.into_affine())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
b_g2: Arc::new(
|
||||||
|
b_g2.into_iter()
|
||||||
|
.filter(|e| !e.is_zero())
|
||||||
|
.map(|e| e.into_affine())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
558
bellman/src/groth16/mod.rs
Normal file
558
bellman/src/groth16/mod.rs
Normal file
@@ -0,0 +1,558 @@
|
|||||||
|
//! The [Groth16] proving system.
|
||||||
|
//!
|
||||||
|
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||||
|
|
||||||
|
use group::{CurveAffine, EncodedPoint};
|
||||||
|
use pairing::{Engine, PairingCurveAffine};
|
||||||
|
|
||||||
|
use crate::SynthesisError;
|
||||||
|
|
||||||
|
use crate::multiexp::SourceBuilder;
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
mod generator;
|
||||||
|
mod prover;
|
||||||
|
mod verifier;
|
||||||
|
|
||||||
|
pub use self::generator::*;
|
||||||
|
pub use self::prover::*;
|
||||||
|
pub use self::verifier::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Proof<E: Engine> {
|
||||||
|
pub a: E::G1Affine,
|
||||||
|
pub b: E::G2Affine,
|
||||||
|
pub c: E::G1Affine,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> PartialEq for Proof<E> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.a == other.a && self.b == other.b && self.c == other.c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Proof<E> {
|
||||||
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
writer.write_all(self.a.into_compressed().as_ref())?;
|
||||||
|
writer.write_all(self.b.into_compressed().as_ref())?;
|
||||||
|
writer.write_all(self.c.into_compressed().as_ref())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
|
let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty();
|
||||||
|
let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty();
|
||||||
|
|
||||||
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
|
let a = g1_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
|
.and_then(|e| {
|
||||||
|
if e.is_zero() {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(e)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
reader.read_exact(g2_repr.as_mut())?;
|
||||||
|
let b = g2_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
|
.and_then(|e| {
|
||||||
|
if e.is_zero() {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(e)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
|
let c = g1_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
|
.and_then(|e| {
|
||||||
|
if e.is_zero() {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(e)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Proof { a, b, c })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VerifyingKey<E: Engine> {
|
||||||
|
// alpha in g1 for verifying and for creating A/C elements of
|
||||||
|
// proof. Never the point at infinity.
|
||||||
|
pub alpha_g1: E::G1Affine,
|
||||||
|
|
||||||
|
// beta in g1 and g2 for verifying and for creating B/C elements
|
||||||
|
// of proof. Never the point at infinity.
|
||||||
|
pub beta_g1: E::G1Affine,
|
||||||
|
pub beta_g2: E::G2Affine,
|
||||||
|
|
||||||
|
// gamma in g2 for verifying. Never the point at infinity.
|
||||||
|
pub gamma_g2: E::G2Affine,
|
||||||
|
|
||||||
|
// delta in g1/g2 for verifying and proving, essentially the magic
|
||||||
|
// trapdoor that forces the prover to evaluate the C element of the
|
||||||
|
// proof with only components from the CRS. Never the point at
|
||||||
|
// infinity.
|
||||||
|
pub delta_g1: E::G1Affine,
|
||||||
|
pub delta_g2: E::G2Affine,
|
||||||
|
|
||||||
|
// Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / gamma
|
||||||
|
// for all public inputs. Because all public inputs have a dummy constraint,
|
||||||
|
// this is the same size as the number of inputs, and never contains points
|
||||||
|
// at infinity.
|
||||||
|
pub ic: Vec<E::G1Affine>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> PartialEq for VerifyingKey<E> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.alpha_g1 == other.alpha_g1
|
||||||
|
&& self.beta_g1 == other.beta_g1
|
||||||
|
&& self.beta_g2 == other.beta_g2
|
||||||
|
&& self.gamma_g2 == other.gamma_g2
|
||||||
|
&& self.delta_g1 == other.delta_g1
|
||||||
|
&& self.delta_g2 == other.delta_g2
|
||||||
|
&& self.ic == other.ic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> VerifyingKey<E> {
|
||||||
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_all(self.beta_g1.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_all(self.beta_g2.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_all(self.gamma_g2.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_all(self.delta_g1.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_all(self.delta_g2.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_u32::<BigEndian>(self.ic.len() as u32)?;
|
||||||
|
for ic in &self.ic {
|
||||||
|
writer.write_all(ic.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
|
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
|
||||||
|
let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
|
||||||
|
|
||||||
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
|
let alpha_g1 = g1_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
|
let beta_g1 = g1_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
|
reader.read_exact(g2_repr.as_mut())?;
|
||||||
|
let beta_g2 = g2_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
|
reader.read_exact(g2_repr.as_mut())?;
|
||||||
|
let gamma_g2 = g2_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
|
let delta_g1 = g1_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
|
reader.read_exact(g2_repr.as_mut())?;
|
||||||
|
let delta_g2 = g2_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
|
let ic_len = reader.read_u32::<BigEndian>()? as usize;
|
||||||
|
|
||||||
|
let mut ic = vec![];
|
||||||
|
|
||||||
|
for _ in 0..ic_len {
|
||||||
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
|
let g1 = g1_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
|
.and_then(|e| {
|
||||||
|
if e.is_zero() {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(e)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
ic.push(g1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(VerifyingKey {
|
||||||
|
alpha_g1,
|
||||||
|
beta_g1,
|
||||||
|
beta_g2,
|
||||||
|
gamma_g2,
|
||||||
|
delta_g1,
|
||||||
|
delta_g2,
|
||||||
|
ic,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Parameters<E: Engine> {
|
||||||
|
pub vk: VerifyingKey<E>,
|
||||||
|
|
||||||
|
// Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and
|
||||||
|
// m-2 inclusive. Never contains points at infinity.
|
||||||
|
pub h: Arc<Vec<E::G1Affine>>,
|
||||||
|
|
||||||
|
// Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / delta
|
||||||
|
// for all auxiliary inputs. Variables can never be unconstrained, so this
|
||||||
|
// never contains points at infinity.
|
||||||
|
pub l: Arc<Vec<E::G1Affine>>,
|
||||||
|
|
||||||
|
// QAP "A" polynomials evaluated at tau in the Lagrange basis. Never contains
|
||||||
|
// points at infinity: polynomials that evaluate to zero are omitted from
|
||||||
|
// the CRS and the prover can deterministically skip their evaluation.
|
||||||
|
pub a: Arc<Vec<E::G1Affine>>,
|
||||||
|
|
||||||
|
// QAP "B" polynomials evaluated at tau in the Lagrange basis. Needed in
|
||||||
|
// G1 and G2 for C/B queries, respectively. Never contains points at
|
||||||
|
// infinity for the same reason as the "A" polynomials.
|
||||||
|
pub b_g1: Arc<Vec<E::G1Affine>>,
|
||||||
|
pub b_g2: Arc<Vec<E::G2Affine>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> PartialEq for Parameters<E> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.vk == other.vk
|
||||||
|
&& self.h == other.h
|
||||||
|
&& self.l == other.l
|
||||||
|
&& self.a == other.a
|
||||||
|
&& self.b_g1 == other.b_g1
|
||||||
|
&& self.b_g2 == other.b_g2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Parameters<E> {
|
||||||
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
self.vk.write(&mut writer)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.h.len() as u32)?;
|
||||||
|
for g in &self.h[..] {
|
||||||
|
writer.write_all(g.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.l.len() as u32)?;
|
||||||
|
for g in &self.l[..] {
|
||||||
|
writer.write_all(g.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.a.len() as u32)?;
|
||||||
|
for g in &self.a[..] {
|
||||||
|
writer.write_all(g.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.b_g1.len() as u32)?;
|
||||||
|
for g in &self.b_g1[..] {
|
||||||
|
writer.write_all(g.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.b_g2.len() as u32)?;
|
||||||
|
for g in &self.b_g2[..] {
|
||||||
|
writer.write_all(g.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<R: Read>(mut reader: R, checked: bool) -> io::Result<Self> {
|
||||||
|
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
|
||||||
|
let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
|
||||||
|
reader.read_exact(repr.as_mut())?;
|
||||||
|
|
||||||
|
if checked {
|
||||||
|
repr.into_affine()
|
||||||
|
} else {
|
||||||
|
repr.into_affine_unchecked()
|
||||||
|
}
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
|
.and_then(|e| {
|
||||||
|
if e.is_zero() {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
|
||||||
|
let mut repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
|
||||||
|
reader.read_exact(repr.as_mut())?;
|
||||||
|
|
||||||
|
if checked {
|
||||||
|
repr.into_affine()
|
||||||
|
} else {
|
||||||
|
repr.into_affine_unchecked()
|
||||||
|
}
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
|
.and_then(|e| {
|
||||||
|
if e.is_zero() {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let vk = VerifyingKey::<E>::read(&mut reader)?;
|
||||||
|
|
||||||
|
let mut h = vec![];
|
||||||
|
let mut l = vec![];
|
||||||
|
let mut a = vec![];
|
||||||
|
let mut b_g1 = vec![];
|
||||||
|
let mut b_g2 = vec![];
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = reader.read_u32::<BigEndian>()? as usize;
|
||||||
|
for _ in 0..len {
|
||||||
|
h.push(read_g1(&mut reader)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = reader.read_u32::<BigEndian>()? as usize;
|
||||||
|
for _ in 0..len {
|
||||||
|
l.push(read_g1(&mut reader)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = reader.read_u32::<BigEndian>()? as usize;
|
||||||
|
for _ in 0..len {
|
||||||
|
a.push(read_g1(&mut reader)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = reader.read_u32::<BigEndian>()? as usize;
|
||||||
|
for _ in 0..len {
|
||||||
|
b_g1.push(read_g1(&mut reader)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let len = reader.read_u32::<BigEndian>()? as usize;
|
||||||
|
for _ in 0..len {
|
||||||
|
b_g2.push(read_g2(&mut reader)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Parameters {
|
||||||
|
vk,
|
||||||
|
h: Arc::new(h),
|
||||||
|
l: Arc::new(l),
|
||||||
|
a: Arc::new(a),
|
||||||
|
b_g1: Arc::new(b_g1),
|
||||||
|
b_g2: Arc::new(b_g2),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PreparedVerifyingKey<E: Engine> {
|
||||||
|
/// Pairing result of alpha*beta
|
||||||
|
alpha_g1_beta_g2: E::Fqk,
|
||||||
|
/// -gamma in G2
|
||||||
|
neg_gamma_g2: <E::G2Affine as PairingCurveAffine>::Prepared,
|
||||||
|
/// -delta in G2
|
||||||
|
neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared,
|
||||||
|
/// Copy of IC from `VerifiyingKey`.
|
||||||
|
ic: Vec<E::G1Affine>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ParameterSource<E: Engine> {
|
||||||
|
type G1Builder: SourceBuilder<E::G1Affine>;
|
||||||
|
type G2Builder: SourceBuilder<E::G2Affine>;
|
||||||
|
|
||||||
|
fn get_vk(&mut self, num_ic: usize) -> Result<VerifyingKey<E>, SynthesisError>;
|
||||||
|
fn get_h(&mut self, num_h: usize) -> Result<Self::G1Builder, SynthesisError>;
|
||||||
|
fn get_l(&mut self, num_l: usize) -> Result<Self::G1Builder, SynthesisError>;
|
||||||
|
fn get_a(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
num_aux: usize,
|
||||||
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
|
||||||
|
fn get_b_g1(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
num_aux: usize,
|
||||||
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
|
||||||
|
fn get_b_g2(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
num_aux: usize,
|
||||||
|
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
|
||||||
|
type G1Builder = (Arc<Vec<E::G1Affine>>, usize);
|
||||||
|
type G2Builder = (Arc<Vec<E::G2Affine>>, usize);
|
||||||
|
|
||||||
|
fn get_vk(&mut self, _: usize) -> Result<VerifyingKey<E>, SynthesisError> {
|
||||||
|
Ok(self.vk.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_h(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> {
|
||||||
|
Ok((self.h.clone(), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_l(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> {
|
||||||
|
Ok((self.l.clone(), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_a(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
_: usize,
|
||||||
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
|
||||||
|
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_b_g1(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
_: usize,
|
||||||
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
|
||||||
|
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_b_g2(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
_: usize,
|
||||||
|
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> {
|
||||||
|
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_with_bls12_381 {
|
||||||
|
use super::*;
|
||||||
|
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||||
|
|
||||||
|
use ff::Field;
|
||||||
|
use pairing::bls12_381::{Bls12, Fr};
|
||||||
|
use rand::thread_rng;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialization() {
|
||||||
|
struct MySillyCircuit<E: Engine> {
|
||||||
|
a: Option<E::Fr>,
|
||||||
|
b: Option<E::Fr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Circuit<E> for MySillyCircuit<E> {
|
||||||
|
fn synthesize<CS: ConstraintSystem<E>>(
|
||||||
|
self,
|
||||||
|
cs: &mut CS,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
|
let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?;
|
||||||
|
let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?;
|
||||||
|
let c = cs.alloc_input(
|
||||||
|
|| "c",
|
||||||
|
|| {
|
||||||
|
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
|
||||||
|
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
|
||||||
|
|
||||||
|
a.mul_assign(&b);
|
||||||
|
Ok(a)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
cs.enforce(|| "a*b=c", |lc| lc + a, |lc| lc + b, |lc| lc + c);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rng = &mut thread_rng();
|
||||||
|
|
||||||
|
let params =
|
||||||
|
generate_random_parameters::<Bls12, _, _>(MySillyCircuit { a: None, b: None }, rng)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut v = vec![];
|
||||||
|
|
||||||
|
params.write(&mut v).unwrap();
|
||||||
|
assert_eq!(v.len(), 2136);
|
||||||
|
|
||||||
|
let de_params = Parameters::read(&v[..], true).unwrap();
|
||||||
|
assert!(params == de_params);
|
||||||
|
|
||||||
|
let de_params = Parameters::read(&v[..], false).unwrap();
|
||||||
|
assert!(params == de_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pvk = prepare_verifying_key::<Bls12>(¶ms.vk);
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
let a = Fr::random(rng);
|
||||||
|
let b = Fr::random(rng);
|
||||||
|
let mut c = a;
|
||||||
|
c.mul_assign(&b);
|
||||||
|
|
||||||
|
let proof = create_random_proof(
|
||||||
|
MySillyCircuit {
|
||||||
|
a: Some(a),
|
||||||
|
b: Some(b),
|
||||||
|
},
|
||||||
|
¶ms,
|
||||||
|
rng,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut v = vec![];
|
||||||
|
proof.write(&mut v).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(v.len(), 192);
|
||||||
|
|
||||||
|
let de_proof = Proof::read(&v[..]).unwrap();
|
||||||
|
assert!(proof == de_proof);
|
||||||
|
|
||||||
|
assert!(verify_proof(&pvk, &proof, &[c]).unwrap());
|
||||||
|
assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
351
bellman/src/groth16/prover.rs
Normal file
351
bellman/src/groth16/prover.rs
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use futures::Future;
|
||||||
|
|
||||||
|
use ff::{Field, PrimeField};
|
||||||
|
use group::{CurveAffine, CurveProjective};
|
||||||
|
use pairing::Engine;
|
||||||
|
|
||||||
|
use super::{ParameterSource, Proof};
|
||||||
|
|
||||||
|
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||||
|
|
||||||
|
use crate::domain::{EvaluationDomain, Scalar};
|
||||||
|
|
||||||
|
use crate::multiexp::{multiexp, DensityTracker, FullDensity};
|
||||||
|
|
||||||
|
use crate::multicore::Worker;
|
||||||
|
|
||||||
|
fn eval<E: Engine>(
|
||||||
|
lc: &LinearCombination<E>,
|
||||||
|
mut input_density: Option<&mut DensityTracker>,
|
||||||
|
mut aux_density: Option<&mut DensityTracker>,
|
||||||
|
input_assignment: &[E::Fr],
|
||||||
|
aux_assignment: &[E::Fr],
|
||||||
|
) -> E::Fr {
|
||||||
|
let mut acc = E::Fr::zero();
|
||||||
|
|
||||||
|
for &(index, coeff) in lc.0.iter() {
|
||||||
|
let mut tmp;
|
||||||
|
|
||||||
|
match index {
|
||||||
|
Variable(Index::Input(i)) => {
|
||||||
|
tmp = input_assignment[i];
|
||||||
|
if let Some(ref mut v) = input_density {
|
||||||
|
v.inc(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Variable(Index::Aux(i)) => {
|
||||||
|
tmp = aux_assignment[i];
|
||||||
|
if let Some(ref mut v) = aux_density {
|
||||||
|
v.inc(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if coeff == E::Fr::one() {
|
||||||
|
acc.add_assign(&tmp);
|
||||||
|
} else {
|
||||||
|
tmp.mul_assign(&coeff);
|
||||||
|
acc.add_assign(&tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProvingAssignment<E: Engine> {
|
||||||
|
// Density of queries
|
||||||
|
a_aux_density: DensityTracker,
|
||||||
|
b_input_density: DensityTracker,
|
||||||
|
b_aux_density: DensityTracker,
|
||||||
|
|
||||||
|
// Evaluations of A, B, C polynomials
|
||||||
|
a: Vec<Scalar<E>>,
|
||||||
|
b: Vec<Scalar<E>>,
|
||||||
|
c: Vec<Scalar<E>>,
|
||||||
|
|
||||||
|
// Assignments of variables
|
||||||
|
input_assignment: Vec<E::Fr>,
|
||||||
|
aux_assignment: Vec<E::Fr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
|
||||||
|
type Root = Self;
|
||||||
|
|
||||||
|
fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
self.aux_assignment.push(f()?);
|
||||||
|
self.a_aux_density.add_element();
|
||||||
|
self.b_aux_density.add_element();
|
||||||
|
|
||||||
|
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_input<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
self.input_assignment.push(f()?);
|
||||||
|
self.b_input_density.add_element();
|
||||||
|
|
||||||
|
Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
|
||||||
|
where
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
{
|
||||||
|
let a = a(LinearCombination::zero());
|
||||||
|
let b = b(LinearCombination::zero());
|
||||||
|
let c = c(LinearCombination::zero());
|
||||||
|
|
||||||
|
self.a.push(Scalar(eval(
|
||||||
|
&a,
|
||||||
|
// Inputs have full density in the A query
|
||||||
|
// because there are constraints of the
|
||||||
|
// form x * 0 = 0 for each input.
|
||||||
|
None,
|
||||||
|
Some(&mut self.a_aux_density),
|
||||||
|
&self.input_assignment,
|
||||||
|
&self.aux_assignment,
|
||||||
|
)));
|
||||||
|
self.b.push(Scalar(eval(
|
||||||
|
&b,
|
||||||
|
Some(&mut self.b_input_density),
|
||||||
|
Some(&mut self.b_aux_density),
|
||||||
|
&self.input_assignment,
|
||||||
|
&self.aux_assignment,
|
||||||
|
)));
|
||||||
|
self.c.push(Scalar(eval(
|
||||||
|
&c,
|
||||||
|
// There is no C polynomial query,
|
||||||
|
// though there is an (beta)A + (alpha)B + C
|
||||||
|
// query for all aux variables.
|
||||||
|
// However, that query has full density.
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
&self.input_assignment,
|
||||||
|
&self.aux_assignment,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_namespace<NR, N>(&mut self, _: N)
|
||||||
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
|
{
|
||||||
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_namespace(&mut self) {
|
||||||
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
|
||||||
|
circuit: C,
|
||||||
|
params: P,
|
||||||
|
rng: &mut R,
|
||||||
|
) -> Result<Proof<E>, SynthesisError>
|
||||||
|
where
|
||||||
|
E: Engine,
|
||||||
|
C: Circuit<E>,
|
||||||
|
R: RngCore,
|
||||||
|
{
|
||||||
|
let r = E::Fr::random(rng);
|
||||||
|
let s = E::Fr::random(rng);
|
||||||
|
|
||||||
|
create_proof::<E, C, P>(circuit, params, r, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_proof<E, C, P: ParameterSource<E>>(
|
||||||
|
circuit: C,
|
||||||
|
mut params: P,
|
||||||
|
r: E::Fr,
|
||||||
|
s: E::Fr,
|
||||||
|
) -> Result<Proof<E>, SynthesisError>
|
||||||
|
where
|
||||||
|
E: Engine,
|
||||||
|
C: Circuit<E>,
|
||||||
|
{
|
||||||
|
let mut prover = ProvingAssignment {
|
||||||
|
a_aux_density: DensityTracker::new(),
|
||||||
|
b_input_density: DensityTracker::new(),
|
||||||
|
b_aux_density: DensityTracker::new(),
|
||||||
|
a: vec![],
|
||||||
|
b: vec![],
|
||||||
|
c: vec![],
|
||||||
|
input_assignment: vec![],
|
||||||
|
aux_assignment: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
prover.alloc_input(|| "", || Ok(E::Fr::one()))?;
|
||||||
|
|
||||||
|
circuit.synthesize(&mut prover)?;
|
||||||
|
|
||||||
|
for i in 0..prover.input_assignment.len() {
|
||||||
|
prover.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc);
|
||||||
|
}
|
||||||
|
|
||||||
|
let worker = Worker::new();
|
||||||
|
|
||||||
|
let vk = params.get_vk(prover.input_assignment.len())?;
|
||||||
|
|
||||||
|
let h = {
|
||||||
|
let mut a = EvaluationDomain::from_coeffs(prover.a)?;
|
||||||
|
let mut b = EvaluationDomain::from_coeffs(prover.b)?;
|
||||||
|
let mut c = EvaluationDomain::from_coeffs(prover.c)?;
|
||||||
|
a.ifft(&worker);
|
||||||
|
a.coset_fft(&worker);
|
||||||
|
b.ifft(&worker);
|
||||||
|
b.coset_fft(&worker);
|
||||||
|
c.ifft(&worker);
|
||||||
|
c.coset_fft(&worker);
|
||||||
|
|
||||||
|
a.mul_assign(&worker, &b);
|
||||||
|
drop(b);
|
||||||
|
a.sub_assign(&worker, &c);
|
||||||
|
drop(c);
|
||||||
|
a.divide_by_z_on_coset(&worker);
|
||||||
|
a.icoset_fft(&worker);
|
||||||
|
let mut a = a.into_coeffs();
|
||||||
|
let a_len = a.len() - 1;
|
||||||
|
a.truncate(a_len);
|
||||||
|
// TODO: parallelize if it's even helpful
|
||||||
|
let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::<Vec<_>>());
|
||||||
|
|
||||||
|
multiexp(&worker, params.get_h(a.len())?, FullDensity, a)
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: parallelize if it's even helpful
|
||||||
|
let input_assignment = Arc::new(
|
||||||
|
prover
|
||||||
|
.input_assignment
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.into_repr())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
let aux_assignment = Arc::new(
|
||||||
|
prover
|
||||||
|
.aux_assignment
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.into_repr())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let l = multiexp(
|
||||||
|
&worker,
|
||||||
|
params.get_l(aux_assignment.len())?,
|
||||||
|
FullDensity,
|
||||||
|
aux_assignment.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let a_aux_density_total = prover.a_aux_density.get_total_density();
|
||||||
|
|
||||||
|
let (a_inputs_source, a_aux_source) =
|
||||||
|
params.get_a(input_assignment.len(), a_aux_density_total)?;
|
||||||
|
|
||||||
|
let a_inputs = multiexp(
|
||||||
|
&worker,
|
||||||
|
a_inputs_source,
|
||||||
|
FullDensity,
|
||||||
|
input_assignment.clone(),
|
||||||
|
);
|
||||||
|
let a_aux = multiexp(
|
||||||
|
&worker,
|
||||||
|
a_aux_source,
|
||||||
|
Arc::new(prover.a_aux_density),
|
||||||
|
aux_assignment.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let b_input_density = Arc::new(prover.b_input_density);
|
||||||
|
let b_input_density_total = b_input_density.get_total_density();
|
||||||
|
let b_aux_density = Arc::new(prover.b_aux_density);
|
||||||
|
let b_aux_density_total = b_aux_density.get_total_density();
|
||||||
|
|
||||||
|
let (b_g1_inputs_source, b_g1_aux_source) =
|
||||||
|
params.get_b_g1(b_input_density_total, b_aux_density_total)?;
|
||||||
|
|
||||||
|
let b_g1_inputs = multiexp(
|
||||||
|
&worker,
|
||||||
|
b_g1_inputs_source,
|
||||||
|
b_input_density.clone(),
|
||||||
|
input_assignment.clone(),
|
||||||
|
);
|
||||||
|
let b_g1_aux = multiexp(
|
||||||
|
&worker,
|
||||||
|
b_g1_aux_source,
|
||||||
|
b_aux_density.clone(),
|
||||||
|
aux_assignment.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let (b_g2_inputs_source, b_g2_aux_source) =
|
||||||
|
params.get_b_g2(b_input_density_total, b_aux_density_total)?;
|
||||||
|
|
||||||
|
let b_g2_inputs = multiexp(
|
||||||
|
&worker,
|
||||||
|
b_g2_inputs_source,
|
||||||
|
b_input_density,
|
||||||
|
input_assignment,
|
||||||
|
);
|
||||||
|
let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment);
|
||||||
|
|
||||||
|
if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() {
|
||||||
|
// If this element is zero, someone is trying to perform a
|
||||||
|
// subversion-CRS attack.
|
||||||
|
return Err(SynthesisError::UnexpectedIdentity);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut g_a = vk.delta_g1.mul(r);
|
||||||
|
g_a.add_assign_mixed(&vk.alpha_g1);
|
||||||
|
let mut g_b = vk.delta_g2.mul(s);
|
||||||
|
g_b.add_assign_mixed(&vk.beta_g2);
|
||||||
|
let mut g_c;
|
||||||
|
{
|
||||||
|
let mut rs = r;
|
||||||
|
rs.mul_assign(&s);
|
||||||
|
|
||||||
|
g_c = vk.delta_g1.mul(rs);
|
||||||
|
g_c.add_assign(&vk.alpha_g1.mul(s));
|
||||||
|
g_c.add_assign(&vk.beta_g1.mul(r));
|
||||||
|
}
|
||||||
|
let mut a_answer = a_inputs.wait()?;
|
||||||
|
a_answer.add_assign(&a_aux.wait()?);
|
||||||
|
g_a.add_assign(&a_answer);
|
||||||
|
a_answer.mul_assign(s);
|
||||||
|
g_c.add_assign(&a_answer);
|
||||||
|
|
||||||
|
let mut b1_answer = b_g1_inputs.wait()?;
|
||||||
|
b1_answer.add_assign(&b_g1_aux.wait()?);
|
||||||
|
let mut b2_answer = b_g2_inputs.wait()?;
|
||||||
|
b2_answer.add_assign(&b_g2_aux.wait()?);
|
||||||
|
|
||||||
|
g_b.add_assign(&b2_answer);
|
||||||
|
b1_answer.mul_assign(r);
|
||||||
|
g_c.add_assign(&b1_answer);
|
||||||
|
g_c.add_assign(&h.wait()?);
|
||||||
|
g_c.add_assign(&l.wait()?);
|
||||||
|
|
||||||
|
Ok(Proof {
|
||||||
|
a: g_a.into_affine(),
|
||||||
|
b: g_b.into_affine(),
|
||||||
|
c: g_c.into_affine(),
|
||||||
|
})
|
||||||
|
}
|
||||||
448
bellman/src/groth16/tests/dummy_engine.rs
Normal file
448
bellman/src/groth16/tests/dummy_engine.rs
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
use ff::{
|
||||||
|
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine,
|
||||||
|
SqrtField,
|
||||||
|
};
|
||||||
|
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||||
|
use pairing::{Engine, PairingCurveAffine};
|
||||||
|
|
||||||
|
use rand_core::RngCore;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::fmt;
|
||||||
|
use std::num::Wrapping;
|
||||||
|
|
||||||
|
const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Fr(Wrapping<u32>);
|
||||||
|
|
||||||
|
impl fmt::Display for Fr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "{}", (self.0).0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Field for Fr {
|
||||||
|
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||||
|
Fr(Wrapping(rng.next_u32()) % MODULUS_R)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zero() -> Self {
|
||||||
|
Fr(Wrapping(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
Fr(Wrapping(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
(self.0).0 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(&mut self) {
|
||||||
|
self.0 = (self.0 * self.0) % MODULUS_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn double(&mut self) {
|
||||||
|
self.0 = (self.0 << 1) % MODULUS_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate(&mut self) {
|
||||||
|
if !<Fr as Field>::is_zero(self) {
|
||||||
|
self.0 = MODULUS_R - self.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_assign(&mut self, other: &Self) {
|
||||||
|
self.0 = (self.0 + other.0) % MODULUS_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub_assign(&mut self, other: &Self) {
|
||||||
|
self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul_assign(&mut self, other: &Self) {
|
||||||
|
self.0 = (self.0 * other.0) % MODULUS_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inverse(&self) -> Option<Self> {
|
||||||
|
if <Fr as Field>::is_zero(self) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.pow(&[(MODULUS_R.0 as u64) - 2]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frobenius_map(&mut self, _: usize) {
|
||||||
|
// identity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SqrtField for Fr {
|
||||||
|
fn legendre(&self) -> LegendreSymbol {
|
||||||
|
// s = self^((r - 1) // 2)
|
||||||
|
let s = self.pow([32256]);
|
||||||
|
if s == <Fr as Field>::zero() {
|
||||||
|
LegendreSymbol::Zero
|
||||||
|
} else if s == <Fr as Field>::one() {
|
||||||
|
LegendreSymbol::QuadraticResidue
|
||||||
|
} else {
|
||||||
|
LegendreSymbol::QuadraticNonResidue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqrt(&self) -> Option<Self> {
|
||||||
|
// Tonelli-Shank's algorithm for q mod 16 = 1
|
||||||
|
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||||
|
match self.legendre() {
|
||||||
|
LegendreSymbol::Zero => Some(*self),
|
||||||
|
LegendreSymbol::QuadraticNonResidue => None,
|
||||||
|
LegendreSymbol::QuadraticResidue => {
|
||||||
|
let mut c = Fr::root_of_unity();
|
||||||
|
// r = self^((t + 1) // 2)
|
||||||
|
let mut r = self.pow([32]);
|
||||||
|
// t = self^t
|
||||||
|
let mut t = self.pow([63]);
|
||||||
|
let mut m = Fr::S;
|
||||||
|
|
||||||
|
while t != <Fr as Field>::one() {
|
||||||
|
let mut i = 1;
|
||||||
|
{
|
||||||
|
let mut t2i = t;
|
||||||
|
t2i.square();
|
||||||
|
loop {
|
||||||
|
if t2i == <Fr as Field>::one() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t2i.square();
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..(m - i - 1) {
|
||||||
|
c.square();
|
||||||
|
}
|
||||||
|
<Fr as Field>::mul_assign(&mut r, &c);
|
||||||
|
c.square();
|
||||||
|
<Fr as Field>::mul_assign(&mut t, &c);
|
||||||
|
m = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct FrRepr([u64; 1]);
|
||||||
|
|
||||||
|
impl Ord for FrRepr {
|
||||||
|
fn cmp(&self, other: &FrRepr) -> Ordering {
|
||||||
|
(self.0)[0].cmp(&(other.0)[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for FrRepr {
|
||||||
|
fn partial_cmp(&self, other: &FrRepr) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FrRepr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "{}", (self.0)[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for FrRepr {
|
||||||
|
fn from(v: u64) -> FrRepr {
|
||||||
|
FrRepr([v])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Fr> for FrRepr {
|
||||||
|
fn from(v: Fr) -> FrRepr {
|
||||||
|
FrRepr([(v.0).0 as u64])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMut<[u64]> for FrRepr {
|
||||||
|
fn as_mut(&mut self) -> &mut [u64] {
|
||||||
|
&mut self.0[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u64]> for FrRepr {
|
||||||
|
fn as_ref(&self) -> &[u64] {
|
||||||
|
&self.0[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FrRepr {
|
||||||
|
fn default() -> FrRepr {
|
||||||
|
FrRepr::from(0u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimeFieldRepr for FrRepr {
|
||||||
|
fn sub_noborrow(&mut self, other: &Self) {
|
||||||
|
self.0[0] = self.0[0].wrapping_sub(other.0[0]);
|
||||||
|
}
|
||||||
|
fn add_nocarry(&mut self, other: &Self) {
|
||||||
|
self.0[0] = self.0[0].wrapping_add(other.0[0]);
|
||||||
|
}
|
||||||
|
fn num_bits(&self) -> u32 {
|
||||||
|
64 - self.0[0].leading_zeros()
|
||||||
|
}
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.0[0] == 0
|
||||||
|
}
|
||||||
|
fn is_odd(&self) -> bool {
|
||||||
|
!self.is_even()
|
||||||
|
}
|
||||||
|
fn is_even(&self) -> bool {
|
||||||
|
self.0[0] % 2 == 0
|
||||||
|
}
|
||||||
|
fn div2(&mut self) {
|
||||||
|
self.shr(1)
|
||||||
|
}
|
||||||
|
fn shr(&mut self, amt: u32) {
|
||||||
|
self.0[0] >>= amt;
|
||||||
|
}
|
||||||
|
fn mul2(&mut self) {
|
||||||
|
self.shl(1)
|
||||||
|
}
|
||||||
|
fn shl(&mut self, amt: u32) {
|
||||||
|
self.0[0] <<= amt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimeField for Fr {
|
||||||
|
type Repr = FrRepr;
|
||||||
|
|
||||||
|
const NUM_BITS: u32 = 16;
|
||||||
|
const CAPACITY: u32 = 15;
|
||||||
|
const S: u32 = 10;
|
||||||
|
|
||||||
|
fn from_repr(repr: FrRepr) -> Result<Self, PrimeFieldDecodingError> {
|
||||||
|
if repr.0[0] >= (MODULUS_R.0 as u64) {
|
||||||
|
Err(PrimeFieldDecodingError::NotInField(format!("{}", repr)))
|
||||||
|
} else {
|
||||||
|
Ok(Fr(Wrapping(repr.0[0] as u32)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_repr(&self) -> FrRepr {
|
||||||
|
FrRepr::from(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn char() -> FrRepr {
|
||||||
|
Fr(MODULUS_R).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiplicative_generator() -> Fr {
|
||||||
|
Fr(Wrapping(5))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_of_unity() -> Fr {
|
||||||
|
Fr(Wrapping(57751))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DummyEngine;
|
||||||
|
|
||||||
|
impl ScalarEngine for DummyEngine {
|
||||||
|
type Fr = Fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Engine for DummyEngine {
|
||||||
|
type G1 = Fr;
|
||||||
|
type G1Affine = Fr;
|
||||||
|
type G2 = Fr;
|
||||||
|
type G2Affine = Fr;
|
||||||
|
type Fq = Fr;
|
||||||
|
type Fqe = Fr;
|
||||||
|
|
||||||
|
// TODO: This should be F_645131 or something. Doesn't matter for now.
|
||||||
|
type Fqk = Fr;
|
||||||
|
|
||||||
|
fn miller_loop<'a, I>(i: I) -> Self::Fqk
|
||||||
|
where
|
||||||
|
I: IntoIterator<
|
||||||
|
Item = &'a (
|
||||||
|
&'a <Self::G1Affine as PairingCurveAffine>::Prepared,
|
||||||
|
&'a <Self::G2Affine as PairingCurveAffine>::Prepared,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
{
|
||||||
|
let mut acc = <Fr as Field>::zero();
|
||||||
|
|
||||||
|
for &(a, b) in i {
|
||||||
|
let mut tmp = *a;
|
||||||
|
<Fr as Field>::mul_assign(&mut tmp, b);
|
||||||
|
<Fr as Field>::add_assign(&mut acc, &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform final exponentiation of the result of a miller loop.
|
||||||
|
fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk> {
|
||||||
|
Some(*this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CurveProjective for Fr {
|
||||||
|
type Affine = Fr;
|
||||||
|
type Base = Fr;
|
||||||
|
type Scalar = Fr;
|
||||||
|
type Engine = DummyEngine;
|
||||||
|
|
||||||
|
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||||
|
<Fr as Field>::random(rng)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zero() -> Self {
|
||||||
|
<Fr as Field>::zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
<Fr as Field>::one()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
<Fr as Field>::is_zero(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn batch_normalization(_: &mut [Self]) {}
|
||||||
|
|
||||||
|
fn is_normalized(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn double(&mut self) {
|
||||||
|
<Fr as Field>::double(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_assign(&mut self, other: &Self) {
|
||||||
|
<Fr as Field>::add_assign(self, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_assign_mixed(&mut self, other: &Self) {
|
||||||
|
<Fr as Field>::add_assign(self, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate(&mut self) {
|
||||||
|
<Fr as Field>::negate(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) {
|
||||||
|
let tmp = Fr::from_repr(other.into()).unwrap();
|
||||||
|
|
||||||
|
<Fr as Field>::mul_assign(self, &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_affine(&self) -> Fr {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recommended_wnaf_for_scalar(_: <Self::Scalar as PrimeField>::Repr) -> usize {
|
||||||
|
3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recommended_wnaf_for_num_scalars(_: usize) -> usize {
|
||||||
|
3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct FakePoint;
|
||||||
|
|
||||||
|
impl AsMut<[u8]> for FakePoint {
|
||||||
|
fn as_mut(&mut self) -> &mut [u8] {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for FakePoint {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncodedPoint for FakePoint {
|
||||||
|
type Affine = Fr;
|
||||||
|
|
||||||
|
fn empty() -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size() -> usize {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_affine(&self) -> Result<Self::Affine, GroupDecodingError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_affine_unchecked(&self) -> Result<Self::Affine, GroupDecodingError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_affine(_: Self::Affine) -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CurveAffine for Fr {
|
||||||
|
type Compressed = FakePoint;
|
||||||
|
type Uncompressed = FakePoint;
|
||||||
|
type Projective = Fr;
|
||||||
|
type Base = Fr;
|
||||||
|
type Scalar = Fr;
|
||||||
|
type Engine = DummyEngine;
|
||||||
|
|
||||||
|
fn zero() -> Self {
|
||||||
|
<Fr as Field>::zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
<Fr as Field>::one()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
<Fr as Field>::is_zero(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate(&mut self) {
|
||||||
|
<Fr as Field>::negate(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective {
|
||||||
|
let mut res = *self;
|
||||||
|
let tmp = Fr::from_repr(other.into()).unwrap();
|
||||||
|
|
||||||
|
<Fr as Field>::mul_assign(&mut res, &tmp);
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_projective(&self) -> Self::Projective {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PairingCurveAffine for Fr {
|
||||||
|
type Prepared = Fr;
|
||||||
|
type Pair = Fr;
|
||||||
|
type PairingResult = Fr;
|
||||||
|
|
||||||
|
fn prepare(&self) -> Self::Prepared {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
|
||||||
|
self.mul(*other)
|
||||||
|
}
|
||||||
|
}
|
||||||
381
bellman/src/groth16/tests/mod.rs
Normal file
381
bellman/src/groth16/tests/mod.rs
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
use ff::{Field, PrimeField};
|
||||||
|
use pairing::Engine;
|
||||||
|
|
||||||
|
mod dummy_engine;
|
||||||
|
use self::dummy_engine::*;
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||||
|
|
||||||
|
use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof};
|
||||||
|
|
||||||
|
struct XORDemo<E: Engine> {
|
||||||
|
a: Option<bool>,
|
||||||
|
b: Option<bool>,
|
||||||
|
_marker: PhantomData<E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Circuit<E> for XORDemo<E> {
|
||||||
|
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||||
|
let a_var = cs.alloc(
|
||||||
|
|| "a",
|
||||||
|
|| {
|
||||||
|
if self.a.is_some() {
|
||||||
|
if self.a.unwrap() {
|
||||||
|
Ok(E::Fr::one())
|
||||||
|
} else {
|
||||||
|
Ok(E::Fr::zero())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(SynthesisError::AssignmentMissing)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "a_boolean_constraint",
|
||||||
|
|lc| lc + CS::one() - a_var,
|
||||||
|
|lc| lc + a_var,
|
||||||
|
|lc| lc,
|
||||||
|
);
|
||||||
|
|
||||||
|
let b_var = cs.alloc(
|
||||||
|
|| "b",
|
||||||
|
|| {
|
||||||
|
if self.b.is_some() {
|
||||||
|
if self.b.unwrap() {
|
||||||
|
Ok(E::Fr::one())
|
||||||
|
} else {
|
||||||
|
Ok(E::Fr::zero())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(SynthesisError::AssignmentMissing)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "b_boolean_constraint",
|
||||||
|
|lc| lc + CS::one() - b_var,
|
||||||
|
|lc| lc + b_var,
|
||||||
|
|lc| lc,
|
||||||
|
);
|
||||||
|
|
||||||
|
let c_var = cs.alloc_input(
|
||||||
|
|| "c",
|
||||||
|
|| {
|
||||||
|
if self.a.is_some() && self.b.is_some() {
|
||||||
|
if self.a.unwrap() ^ self.b.unwrap() {
|
||||||
|
Ok(E::Fr::one())
|
||||||
|
} else {
|
||||||
|
Ok(E::Fr::zero())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(SynthesisError::AssignmentMissing)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "c_xor_constraint",
|
||||||
|
|lc| lc + a_var + a_var,
|
||||||
|
|lc| lc + b_var,
|
||||||
|
|lc| lc + a_var + b_var - c_var,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_xordemo() {
|
||||||
|
let g1 = Fr::one();
|
||||||
|
let g2 = Fr::one();
|
||||||
|
let alpha = Fr::from_str("48577").unwrap();
|
||||||
|
let beta = Fr::from_str("22580").unwrap();
|
||||||
|
let gamma = Fr::from_str("53332").unwrap();
|
||||||
|
let delta = Fr::from_str("5481").unwrap();
|
||||||
|
let tau = Fr::from_str("3673").unwrap();
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
let c = XORDemo::<DummyEngine> {
|
||||||
|
a: None,
|
||||||
|
b: None,
|
||||||
|
_marker: PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
generate_parameters(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// This will synthesize the constraint system:
|
||||||
|
//
|
||||||
|
// public inputs: a_0 = 1, a_1 = c
|
||||||
|
// aux inputs: a_2 = a, a_3 = b
|
||||||
|
// constraints:
|
||||||
|
// (a_0 - a_2) * (a_2) = 0
|
||||||
|
// (a_0 - a_3) * (a_3) = 0
|
||||||
|
// (a_2 + a_2) * (a_3) = (a_2 + a_3 - a_1)
|
||||||
|
// (a_0) * 0 = 0
|
||||||
|
// (a_1) * 0 = 0
|
||||||
|
|
||||||
|
// The evaluation domain is 8. The H query should
|
||||||
|
// have 7 elements (it's a quotient polynomial)
|
||||||
|
assert_eq!(7, params.h.len());
|
||||||
|
|
||||||
|
let mut root_of_unity = Fr::root_of_unity();
|
||||||
|
|
||||||
|
// We expect this to be a 2^10 root of unity
|
||||||
|
assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 10]));
|
||||||
|
|
||||||
|
// Let's turn it into a 2^3 root of unity.
|
||||||
|
root_of_unity = root_of_unity.pow(&[1 << 7]);
|
||||||
|
assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 3]));
|
||||||
|
assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity);
|
||||||
|
|
||||||
|
// Let's compute all the points in our evaluation domain.
|
||||||
|
let mut points = Vec::with_capacity(8);
|
||||||
|
for i in 0..8 {
|
||||||
|
points.push(root_of_unity.pow(&[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's compute t(tau) = (tau - p_0)(tau - p_1)...
|
||||||
|
// = tau^8 - 1
|
||||||
|
let mut t_at_tau = tau.pow(&[8]);
|
||||||
|
t_at_tau.sub_assign(&Fr::one());
|
||||||
|
{
|
||||||
|
let mut tmp = Fr::one();
|
||||||
|
for p in &points {
|
||||||
|
let mut term = tau;
|
||||||
|
term.sub_assign(p);
|
||||||
|
tmp.mul_assign(&term);
|
||||||
|
}
|
||||||
|
assert_eq!(tmp, t_at_tau);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect our H query to be 7 elements of the form...
|
||||||
|
// {tau^i t(tau) / delta}
|
||||||
|
let delta_inverse = delta.inverse().unwrap();
|
||||||
|
let gamma_inverse = gamma.inverse().unwrap();
|
||||||
|
{
|
||||||
|
let mut coeff = delta_inverse;
|
||||||
|
coeff.mul_assign(&t_at_tau);
|
||||||
|
|
||||||
|
let mut cur = Fr::one();
|
||||||
|
for h in params.h.iter() {
|
||||||
|
let mut tmp = cur;
|
||||||
|
tmp.mul_assign(&coeff);
|
||||||
|
|
||||||
|
assert_eq!(*h, tmp);
|
||||||
|
|
||||||
|
cur.mul_assign(&tau);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The density of the IC query is 2 (2 inputs)
|
||||||
|
assert_eq!(2, params.vk.ic.len());
|
||||||
|
|
||||||
|
// The density of the L query is 2 (2 aux variables)
|
||||||
|
assert_eq!(2, params.l.len());
|
||||||
|
|
||||||
|
// The density of the A query is 4 (each variable is in at least one A term)
|
||||||
|
assert_eq!(4, params.a.len());
|
||||||
|
|
||||||
|
// The density of the B query is 2 (two variables are in at least one B term)
|
||||||
|
assert_eq!(2, params.b_g1.len());
|
||||||
|
assert_eq!(2, params.b_g2.len());
|
||||||
|
|
||||||
|
/*
|
||||||
|
Lagrange interpolation polynomials in our evaluation domain:
|
||||||
|
|
||||||
|
,-------------------------------. ,-------------------------------. ,-------------------------------.
|
||||||
|
| A TERM | | B TERM | | C TERM |
|
||||||
|
`-------------------------------. `-------------------------------' `-------------------------------'
|
||||||
|
| a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 |
|
||||||
|
| 1 | 0 | 64512 | 0 | | 0 | 0 | 1 | 0 | | 0 | 0 | 0 | 0 |
|
||||||
|
| 1 | 0 | 0 | 64512 | | 0 | 0 | 0 | 1 | | 0 | 0 | 0 | 0 |
|
||||||
|
| 0 | 0 | 2 | 0 | | 0 | 0 | 0 | 1 | | 0 | 64512 | 1 | 1 |
|
||||||
|
| 1 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |
|
||||||
|
| 0 | 1 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |
|
||||||
|
`-------'-------'-------'-------' `-------'-------'-------'-------' `-------'-------'-------'-------'
|
||||||
|
|
||||||
|
Example for u_0:
|
||||||
|
|
||||||
|
sage: r = 64513
|
||||||
|
sage: Fr = GF(r)
|
||||||
|
sage: omega = (Fr(5)^63)^(2^7)
|
||||||
|
sage: tau = Fr(3673)
|
||||||
|
sage: R.<x> = PolynomialRing(Fr, 'x')
|
||||||
|
sage: def eval(tau, c0, c1, c2, c3, c4):
|
||||||
|
....: p = R.lagrange_polynomial([(omega^0, c0), (omega^1, c1), (omega^2, c2), (omega^3, c3), (omega^4, c4), (omega^5, 0), (omega^6, 0), (omega^7, 0)])
|
||||||
|
....: return p.substitute(tau)
|
||||||
|
sage: eval(tau, 1, 1, 0, 1, 0)
|
||||||
|
59158
|
||||||
|
*/
|
||||||
|
|
||||||
|
let u_i = [59158, 48317, 21767, 10402]
|
||||||
|
.iter()
|
||||||
|
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
|
||||||
|
.collect::<Vec<Fr>>();
|
||||||
|
let v_i = [0, 0, 60619, 30791]
|
||||||
|
.iter()
|
||||||
|
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
|
||||||
|
.collect::<Vec<Fr>>();
|
||||||
|
let w_i = [0, 23320, 41193, 41193]
|
||||||
|
.iter()
|
||||||
|
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
|
||||||
|
.collect::<Vec<Fr>>();
|
||||||
|
|
||||||
|
for (u, a) in u_i.iter().zip(¶ms.a[..]) {
|
||||||
|
assert_eq!(u, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (v, b) in v_i
|
||||||
|
.iter()
|
||||||
|
.filter(|&&e| e != Fr::zero())
|
||||||
|
.zip(¶ms.b_g1[..])
|
||||||
|
{
|
||||||
|
assert_eq!(v, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (v, b) in v_i
|
||||||
|
.iter()
|
||||||
|
.filter(|&&e| e != Fr::zero())
|
||||||
|
.zip(¶ms.b_g2[..])
|
||||||
|
{
|
||||||
|
assert_eq!(v, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..4 {
|
||||||
|
let mut tmp1 = beta;
|
||||||
|
tmp1.mul_assign(&u_i[i]);
|
||||||
|
|
||||||
|
let mut tmp2 = alpha;
|
||||||
|
tmp2.mul_assign(&v_i[i]);
|
||||||
|
|
||||||
|
tmp1.add_assign(&tmp2);
|
||||||
|
tmp1.add_assign(&w_i[i]);
|
||||||
|
|
||||||
|
if i < 2 {
|
||||||
|
// Check the correctness of the IC query elements
|
||||||
|
tmp1.mul_assign(&gamma_inverse);
|
||||||
|
|
||||||
|
assert_eq!(tmp1, params.vk.ic[i]);
|
||||||
|
} else {
|
||||||
|
// Check the correctness of the L query elements
|
||||||
|
tmp1.mul_assign(&delta_inverse);
|
||||||
|
|
||||||
|
assert_eq!(tmp1, params.l[i - 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check consistency of the other elements
|
||||||
|
assert_eq!(alpha, params.vk.alpha_g1);
|
||||||
|
assert_eq!(beta, params.vk.beta_g1);
|
||||||
|
assert_eq!(beta, params.vk.beta_g2);
|
||||||
|
assert_eq!(gamma, params.vk.gamma_g2);
|
||||||
|
assert_eq!(delta, params.vk.delta_g1);
|
||||||
|
assert_eq!(delta, params.vk.delta_g2);
|
||||||
|
|
||||||
|
let pvk = prepare_verifying_key(¶ms.vk);
|
||||||
|
|
||||||
|
let r = Fr::from_str("27134").unwrap();
|
||||||
|
let s = Fr::from_str("17146").unwrap();
|
||||||
|
|
||||||
|
let proof = {
|
||||||
|
let c = XORDemo {
|
||||||
|
a: Some(true),
|
||||||
|
b: Some(false),
|
||||||
|
_marker: PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
create_proof(c, ¶ms, r, s).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// A(x) =
|
||||||
|
// a_0 * (44865*x^7 + 56449*x^6 + 44865*x^5 + 8064*x^4 + 3520*x^3 + 56449*x^2 + 3520*x + 40321) +
|
||||||
|
// a_1 * (8064*x^7 + 56449*x^6 + 8064*x^5 + 56449*x^4 + 8064*x^3 + 56449*x^2 + 8064*x + 56449) +
|
||||||
|
// a_2 * (16983*x^7 + 24192*x^6 + 63658*x^5 + 56449*x^4 + 16983*x^3 + 24192*x^2 + 63658*x + 56449) +
|
||||||
|
// a_3 * (5539*x^7 + 27797*x^6 + 6045*x^5 + 56449*x^4 + 58974*x^3 + 36716*x^2 + 58468*x + 8064) +
|
||||||
|
{
|
||||||
|
// proof A = alpha + A(tau) + delta * r
|
||||||
|
let mut expected_a = delta;
|
||||||
|
expected_a.mul_assign(&r);
|
||||||
|
expected_a.add_assign(&alpha);
|
||||||
|
expected_a.add_assign(&u_i[0]); // a_0 = 1
|
||||||
|
expected_a.add_assign(&u_i[1]); // a_1 = 1
|
||||||
|
expected_a.add_assign(&u_i[2]); // a_2 = 1
|
||||||
|
// a_3 = 0
|
||||||
|
assert_eq!(proof.a, expected_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// B(x) =
|
||||||
|
// a_0 * (0) +
|
||||||
|
// a_1 * (0) +
|
||||||
|
// a_2 * (56449*x^7 + 56449*x^6 + 56449*x^5 + 56449*x^4 + 56449*x^3 + 56449*x^2 + 56449*x + 56449) +
|
||||||
|
// a_3 * (31177*x^7 + 44780*x^6 + 21752*x^5 + 42255*x^3 + 35861*x^2 + 33842*x + 48385)
|
||||||
|
{
|
||||||
|
// proof B = beta + B(tau) + delta * s
|
||||||
|
let mut expected_b = delta;
|
||||||
|
expected_b.mul_assign(&s);
|
||||||
|
expected_b.add_assign(&beta);
|
||||||
|
expected_b.add_assign(&v_i[0]); // a_0 = 1
|
||||||
|
expected_b.add_assign(&v_i[1]); // a_1 = 1
|
||||||
|
expected_b.add_assign(&v_i[2]); // a_2 = 1
|
||||||
|
// a_3 = 0
|
||||||
|
assert_eq!(proof.b, expected_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// C(x) =
|
||||||
|
// a_0 * (0) +
|
||||||
|
// a_1 * (27797*x^7 + 56449*x^6 + 36716*x^5 + 8064*x^4 + 27797*x^3 + 56449*x^2 + 36716*x + 8064) +
|
||||||
|
// a_2 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) +
|
||||||
|
// a_3 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449)
|
||||||
|
//
|
||||||
|
// If A * B = C at each point in the domain, then the following polynomial...
|
||||||
|
// P(x) = A(x) * B(x) - C(x)
|
||||||
|
// = 49752*x^14 + 13914*x^13 + 29243*x^12 + 27227*x^11 + 62362*x^10 + 35703*x^9 + 4032*x^8 + 14761*x^6 + 50599*x^5 + 35270*x^4 + 37286*x^3 + 2151*x^2 + 28810*x + 60481
|
||||||
|
//
|
||||||
|
// ... should be divisible by t(x), producing the quotient polynomial:
|
||||||
|
// h(x) = P(x) / t(x)
|
||||||
|
// = 49752*x^6 + 13914*x^5 + 29243*x^4 + 27227*x^3 + 62362*x^2 + 35703*x + 4032
|
||||||
|
{
|
||||||
|
let mut expected_c = Fr::zero();
|
||||||
|
|
||||||
|
// A * s
|
||||||
|
let mut tmp = proof.a;
|
||||||
|
tmp.mul_assign(&s);
|
||||||
|
expected_c.add_assign(&tmp);
|
||||||
|
|
||||||
|
// B * r
|
||||||
|
let mut tmp = proof.b;
|
||||||
|
tmp.mul_assign(&r);
|
||||||
|
expected_c.add_assign(&tmp);
|
||||||
|
|
||||||
|
// delta * r * s
|
||||||
|
let mut tmp = delta;
|
||||||
|
tmp.mul_assign(&r);
|
||||||
|
tmp.mul_assign(&s);
|
||||||
|
expected_c.sub_assign(&tmp);
|
||||||
|
|
||||||
|
// L query answer
|
||||||
|
// a_2 = 1, a_3 = 0
|
||||||
|
expected_c.add_assign(¶ms.l[0]);
|
||||||
|
|
||||||
|
// H query answer
|
||||||
|
for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739]
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let coeff = Fr::from_str(&format!("{}", coeff)).unwrap();
|
||||||
|
|
||||||
|
let mut tmp = params.h[i];
|
||||||
|
tmp.mul_assign(&coeff);
|
||||||
|
expected_c.add_assign(&tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(expected_c, proof.c);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(verify_proof(&pvk, &proof, &[Fr::one()]).unwrap());
|
||||||
|
}
|
||||||
56
bellman/src/groth16/verifier.rs
Normal file
56
bellman/src/groth16/verifier.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
use ff::PrimeField;
|
||||||
|
use group::{CurveAffine, CurveProjective};
|
||||||
|
use pairing::{Engine, PairingCurveAffine};
|
||||||
|
|
||||||
|
use super::{PreparedVerifyingKey, Proof, VerifyingKey};
|
||||||
|
|
||||||
|
use crate::SynthesisError;
|
||||||
|
|
||||||
|
pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
|
||||||
|
let mut gamma = vk.gamma_g2;
|
||||||
|
gamma.negate();
|
||||||
|
let mut delta = vk.delta_g2;
|
||||||
|
delta.negate();
|
||||||
|
|
||||||
|
PreparedVerifyingKey {
|
||||||
|
alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2),
|
||||||
|
neg_gamma_g2: gamma.prepare(),
|
||||||
|
neg_delta_g2: delta.prepare(),
|
||||||
|
ic: vk.ic.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_proof<'a, E: Engine>(
|
||||||
|
pvk: &'a PreparedVerifyingKey<E>,
|
||||||
|
proof: &Proof<E>,
|
||||||
|
public_inputs: &[E::Fr],
|
||||||
|
) -> Result<bool, SynthesisError> {
|
||||||
|
if (public_inputs.len() + 1) != pvk.ic.len() {
|
||||||
|
return Err(SynthesisError::MalformedVerifyingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut acc = pvk.ic[0].into_projective();
|
||||||
|
|
||||||
|
for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) {
|
||||||
|
acc.add_assign(&b.mul(i.into_repr()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The original verification equation is:
|
||||||
|
// A * B = alpha * beta + inputs * gamma + C * delta
|
||||||
|
// ... however, we rearrange it so that it is:
|
||||||
|
// A * B - inputs * gamma - C * delta = alpha * beta
|
||||||
|
// or equivalently:
|
||||||
|
// A * B + inputs * (-gamma) + C * (-delta) = alpha * beta
|
||||||
|
// which allows us to do a single final exponentiation.
|
||||||
|
|
||||||
|
Ok(E::final_exponentiation(&E::miller_loop(
|
||||||
|
[
|
||||||
|
(&proof.a.prepare(), &proof.b.prepare()),
|
||||||
|
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
|
||||||
|
(&proof.c.prepare(), &pvk.neg_delta_g2),
|
||||||
|
]
|
||||||
|
.iter(),
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
== pvk.alpha_g1_beta_g2)
|
||||||
|
}
|
||||||
537
bellman/src/lib.rs
Normal file
537
bellman/src/lib.rs
Normal file
@@ -0,0 +1,537 @@
|
|||||||
|
//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit
|
||||||
|
//! traits and and primitive structures, as well as basic gadget implementations
|
||||||
|
//! such as booleans and number abstractions.
|
||||||
|
//!
|
||||||
|
//! # Example circuit
|
||||||
|
//!
|
||||||
|
//! Say we want to write a circuit that proves we know the preimage to some hash
|
||||||
|
//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a
|
||||||
|
//! fixed length known in advance (because the circuit parameters will depend on
|
||||||
|
//! it), but can otherwise have any value. We take the following strategy:
|
||||||
|
//!
|
||||||
|
//! - Witness each bit of the preimage.
|
||||||
|
//! - Compute `hash = SHA-256d(preimage)` inside the circuit.
|
||||||
|
//! - Expose `hash` as a public input using multiscalar packing.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use bellman::{
|
||||||
|
//! gadgets::{
|
||||||
|
//! boolean::{AllocatedBit, Boolean},
|
||||||
|
//! multipack,
|
||||||
|
//! sha256::sha256,
|
||||||
|
//! },
|
||||||
|
//! groth16, Circuit, ConstraintSystem, SynthesisError,
|
||||||
|
//! };
|
||||||
|
//! use pairing::{bls12_381::Bls12, Engine};
|
||||||
|
//! use rand::rngs::OsRng;
|
||||||
|
//! use sha2::{Digest, Sha256};
|
||||||
|
//!
|
||||||
|
//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order.
|
||||||
|
//! fn sha256d<E: Engine, CS: ConstraintSystem<E>>(
|
||||||
|
//! mut cs: CS,
|
||||||
|
//! data: &[Boolean],
|
||||||
|
//! ) -> Result<Vec<Boolean>, SynthesisError> {
|
||||||
|
//! // Flip endianness of each input byte
|
||||||
|
//! let input: Vec<_> = data
|
||||||
|
//! .chunks(8)
|
||||||
|
//! .map(|c| c.iter().rev())
|
||||||
|
//! .flatten()
|
||||||
|
//! .cloned()
|
||||||
|
//! .collect();
|
||||||
|
//!
|
||||||
|
//! let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?;
|
||||||
|
//! let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?;
|
||||||
|
//!
|
||||||
|
//! // Flip endianness of each output byte
|
||||||
|
//! Ok(res
|
||||||
|
//! .chunks(8)
|
||||||
|
//! .map(|c| c.iter().rev())
|
||||||
|
//! .flatten()
|
||||||
|
//! .cloned()
|
||||||
|
//! .collect())
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! struct MyCircuit {
|
||||||
|
//! /// The input to SHA-256d we are proving that we know. Set to `None` when we
|
||||||
|
//! /// are verifying a proof (and do not have the witness data).
|
||||||
|
//! preimage: Option<[u8; 80]>,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl<E: Engine> Circuit<E> for MyCircuit {
|
||||||
|
//! fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||||
|
//! // Compute the values for the bits of the preimage. If we are verifying a proof,
|
||||||
|
//! // we still need to create the same constraints, so we return an equivalent-size
|
||||||
|
//! // Vec of None (indicating that the value of each bit is unknown).
|
||||||
|
//! let bit_values = if let Some(preimage) = self.preimage {
|
||||||
|
//! preimage
|
||||||
|
//! .into_iter()
|
||||||
|
//! .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
|
||||||
|
//! .flatten()
|
||||||
|
//! .map(|b| Some(b))
|
||||||
|
//! .collect()
|
||||||
|
//! } else {
|
||||||
|
//! vec![None; 80 * 8]
|
||||||
|
//! };
|
||||||
|
//! assert_eq!(bit_values.len(), 80 * 8);
|
||||||
|
//!
|
||||||
|
//! // Witness the bits of the preimage.
|
||||||
|
//! let preimage_bits = bit_values
|
||||||
|
//! .into_iter()
|
||||||
|
//! .enumerate()
|
||||||
|
//! // Allocate each bit.
|
||||||
|
//! .map(|(i, b)| {
|
||||||
|
//! AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b)
|
||||||
|
//! })
|
||||||
|
//! // Convert the AllocatedBits into Booleans (required for the sha256 gadget).
|
||||||
|
//! .map(|b| b.map(Boolean::from))
|
||||||
|
//! .collect::<Result<Vec<_>, _>>()?;
|
||||||
|
//!
|
||||||
|
//! // Compute hash = SHA-256d(preimage).
|
||||||
|
//! let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?;
|
||||||
|
//!
|
||||||
|
//! // Expose the vector of 32 boolean variables as compact public inputs.
|
||||||
|
//! multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash)
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // Create parameters for our circuit. In a production deployment these would
|
||||||
|
//! // be generated securely using a multiparty computation.
|
||||||
|
//! let params = {
|
||||||
|
//! let c = MyCircuit { preimage: None };
|
||||||
|
//! groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
|
||||||
|
//! };
|
||||||
|
//!
|
||||||
|
//! // Prepare the verification key (for proof verification).
|
||||||
|
//! let pvk = groth16::prepare_verifying_key(¶ms.vk);
|
||||||
|
//!
|
||||||
|
//! // Pick a preimage and compute its hash.
|
||||||
|
//! let preimage = [42; 80];
|
||||||
|
//! let hash = Sha256::digest(&Sha256::digest(&preimage));
|
||||||
|
//!
|
||||||
|
//! // Create an instance of our circuit (with the preimage as a witness).
|
||||||
|
//! let c = MyCircuit {
|
||||||
|
//! preimage: Some(preimage),
|
||||||
|
//! };
|
||||||
|
//!
|
||||||
|
//! // Create a Groth16 proof with our parameters.
|
||||||
|
//! let proof = groth16::create_random_proof(c, ¶ms, &mut OsRng).unwrap();
|
||||||
|
//!
|
||||||
|
//! // Pack the hash as inputs for proof verification.
|
||||||
|
//! let hash_bits = multipack::bytes_to_bits_le(&hash);
|
||||||
|
//! let inputs = multipack::compute_multipacking::<Bls12>(&hash_bits);
|
||||||
|
//!
|
||||||
|
//! // Check the proof!
|
||||||
|
//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).unwrap());
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Roadmap
|
||||||
|
//!
|
||||||
|
//! `bellman` is being refactored into a generic proving library. Currently it
|
||||||
|
//! is pairing-specific, and different types of proving systems need to be
|
||||||
|
//! implemented as sub-modules. After the refactor, `bellman` will be generic
|
||||||
|
//! using the [`ff`] and [`group`] crates, while specific proving systems will
|
||||||
|
//! be separate crates that pull in the dependencies they require.
|
||||||
|
|
||||||
|
// Catch documentation errors caused by code changes.
|
||||||
|
#![deny(intra_doc_link_resolution_failure)]
|
||||||
|
|
||||||
|
pub mod domain;
|
||||||
|
pub mod gadgets;
|
||||||
|
#[cfg(feature = "groth16")]
|
||||||
|
pub mod groth16;
|
||||||
|
pub mod multicore;
|
||||||
|
mod multiexp;
|
||||||
|
|
||||||
|
use ff::{Field, ScalarEngine};
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
|
/// Computations are expressed in terms of arithmetic circuits, in particular
|
||||||
|
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a
|
||||||
|
/// circuit that can be synthesized. The `synthesize` method is called during
|
||||||
|
/// CRS generation and during proving.
|
||||||
|
pub trait Circuit<E: ScalarEngine> {
|
||||||
|
/// Synthesize the circuit into a rank-1 quadratic constraint system
|
||||||
|
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a variable in our constraint system.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Variable(Index);
|
||||||
|
|
||||||
|
impl Variable {
|
||||||
|
/// This constructs a variable with an arbitrary index.
|
||||||
|
/// Circuit implementations are not recommended to use this.
|
||||||
|
pub fn new_unchecked(idx: Index) -> Variable {
|
||||||
|
Variable(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This returns the index underlying the variable.
|
||||||
|
/// Circuit implementations are not recommended to use this.
|
||||||
|
pub fn get_unchecked(&self) -> Index {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the index of either an input variable or
|
||||||
|
/// auxiliary variable.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
pub enum Index {
|
||||||
|
Input(usize),
|
||||||
|
Aux(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This represents a linear combination of some variables, with coefficients
|
||||||
|
/// in the scalar field of a pairing-friendly elliptic curve group.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LinearCombination<E: ScalarEngine>(Vec<(Variable, E::Fr)>);
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> AsRef<[(Variable, E::Fr)]> for LinearCombination<E> {
|
||||||
|
fn as_ref(&self) -> &[(Variable, E::Fr)] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> LinearCombination<E> {
|
||||||
|
pub fn zero() -> LinearCombination<E> {
|
||||||
|
LinearCombination(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||||
|
self.0.push((var, coeff));
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
|
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||||
|
coeff.negate();
|
||||||
|
|
||||||
|
self + (coeff, var)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> Add<Variable> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn add(self, other: Variable) -> LinearCombination<E> {
|
||||||
|
self + (E::Fr::one(), other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine> Sub<Variable> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn sub(self, other: Variable) -> LinearCombination<E> {
|
||||||
|
self - (E::Fr::one(), other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: ScalarEngine> Add<&'a LinearCombination<E>> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn add(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
|
||||||
|
for s in &other.0 {
|
||||||
|
self = self + (s.1, s.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: ScalarEngine> Sub<&'a LinearCombination<E>> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn sub(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
|
||||||
|
for s in &other.0 {
|
||||||
|
self = self - (s.1, s.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: ScalarEngine> Add<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> {
|
||||||
|
for s in &other.0 {
|
||||||
|
let mut tmp = s.1;
|
||||||
|
tmp.mul_assign(&coeff);
|
||||||
|
self = self + (tmp, s.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: ScalarEngine> Sub<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> {
|
||||||
|
for s in &other.0 {
|
||||||
|
let mut tmp = s.1;
|
||||||
|
tmp.mul_assign(&coeff);
|
||||||
|
self = self - (tmp, s.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is an error that could occur during circuit synthesis contexts,
|
||||||
|
/// such as CRS generation, proving or verification.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SynthesisError {
|
||||||
|
/// During synthesis, we lacked knowledge of a variable assignment.
|
||||||
|
AssignmentMissing,
|
||||||
|
/// During synthesis, we divided by zero.
|
||||||
|
DivisionByZero,
|
||||||
|
/// During synthesis, we constructed an unsatisfiable constraint system.
|
||||||
|
Unsatisfiable,
|
||||||
|
/// During synthesis, our polynomials ended up being too high of degree
|
||||||
|
PolynomialDegreeTooLarge,
|
||||||
|
/// During proof generation, we encountered an identity in the CRS
|
||||||
|
UnexpectedIdentity,
|
||||||
|
/// During proof generation, we encountered an I/O error with the CRS
|
||||||
|
IoError(io::Error),
|
||||||
|
/// During verification, our verifying key was malformed.
|
||||||
|
MalformedVerifyingKey,
|
||||||
|
/// During CRS generation, we observed an unconstrained auxiliary variable
|
||||||
|
UnconstrainedVariable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for SynthesisError {
|
||||||
|
fn from(e: io::Error) -> SynthesisError {
|
||||||
|
SynthesisError::IoError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for SynthesisError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
SynthesisError::AssignmentMissing => {
|
||||||
|
"an assignment for a variable could not be computed"
|
||||||
|
}
|
||||||
|
SynthesisError::DivisionByZero => "division by zero",
|
||||||
|
SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
|
||||||
|
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
|
||||||
|
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
|
||||||
|
SynthesisError::IoError(_) => "encountered an I/O error",
|
||||||
|
SynthesisError::MalformedVerifyingKey => "malformed verifying key",
|
||||||
|
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SynthesisError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
if let SynthesisError::IoError(ref e) = *self {
|
||||||
|
write!(f, "I/O error: ")?;
|
||||||
|
e.fmt(f)
|
||||||
|
} else {
|
||||||
|
write!(f, "{}", self.description())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a constraint system which can have new variables
|
||||||
|
/// allocated and constrains between them formed.
|
||||||
|
pub trait ConstraintSystem<E: ScalarEngine>: Sized {
|
||||||
|
/// Represents the type of the "root" of this constraint system
|
||||||
|
/// so that nested namespaces can minimize indirection.
|
||||||
|
type Root: ConstraintSystem<E>;
|
||||||
|
|
||||||
|
/// Return the "one" input variable
|
||||||
|
fn one() -> Variable {
|
||||||
|
Variable::new_unchecked(Index::Input(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a private variable in the constraint system. The provided function is used to
|
||||||
|
/// determine the assignment of the variable. The given `annotation` function is invoked
|
||||||
|
/// in testing contexts in order to derive a unique name for this variable in the current
|
||||||
|
/// namespace.
|
||||||
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>;
|
||||||
|
|
||||||
|
/// Allocate a public variable in the constraint system. The provided function is used to
|
||||||
|
/// determine the assignment of the variable.
|
||||||
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>;
|
||||||
|
|
||||||
|
/// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts
|
||||||
|
/// in order to derive a unique name for the constraint in the current namespace.
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||||
|
where
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>;
|
||||||
|
|
||||||
|
/// Create a new (sub)namespace and enter into it. Not intended
|
||||||
|
/// for downstream use; use `namespace` instead.
|
||||||
|
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||||
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR;
|
||||||
|
|
||||||
|
/// Exit out of the existing namespace. Not intended for
|
||||||
|
/// downstream use; use `namespace` instead.
|
||||||
|
fn pop_namespace(&mut self);
|
||||||
|
|
||||||
|
/// Gets the "root" constraint system, bypassing the namespacing.
|
||||||
|
/// Not intended for downstream use; use `namespace` instead.
|
||||||
|
fn get_root(&mut self) -> &mut Self::Root;
|
||||||
|
|
||||||
|
/// Begin a namespace for this constraint system.
|
||||||
|
fn namespace<NR, N>(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root>
|
||||||
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
|
{
|
||||||
|
self.get_root().push_namespace(name_fn);
|
||||||
|
|
||||||
|
Namespace(self.get_root(), PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
|
||||||
|
/// a namespace context) and, when dropped, pops out of the namespace context.
|
||||||
|
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E>>(&'a mut CS, PhantomData<E>);
|
||||||
|
|
||||||
|
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
|
||||||
|
type Root = CS::Root;
|
||||||
|
|
||||||
|
fn one() -> Variable {
|
||||||
|
CS::one()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
self.0.alloc(annotation, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
self.0.alloc_input(annotation, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||||
|
where
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
{
|
||||||
|
self.0.enforce(annotation, a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downstream users who use `namespace` will never interact with these
|
||||||
|
// functions and they will never be invoked because the namespace is
|
||||||
|
// never a root constraint system.
|
||||||
|
|
||||||
|
fn push_namespace<NR, N>(&mut self, _: N)
|
||||||
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
|
{
|
||||||
|
panic!("only the root's push_namespace should be called");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_namespace(&mut self) {
|
||||||
|
panic!("only the root's pop_namespace should be called");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
|
self.0.get_root()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: ScalarEngine, CS: ConstraintSystem<E>> Drop for Namespace<'a, E, CS> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.get_root().pop_namespace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience implementation of ConstraintSystem<E> for mutable references to
|
||||||
|
/// constraint systems.
|
||||||
|
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs mut CS {
|
||||||
|
type Root = CS::Root;
|
||||||
|
|
||||||
|
fn one() -> Variable {
|
||||||
|
CS::one()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
(**self).alloc(annotation, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
{
|
||||||
|
(**self).alloc_input(annotation, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||||
|
where
|
||||||
|
A: FnOnce() -> AR,
|
||||||
|
AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
{
|
||||||
|
(**self).enforce(annotation, a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||||
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
|
{
|
||||||
|
(**self).push_namespace(name_fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_namespace(&mut self) {
|
||||||
|
(**self).pop_namespace()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
|
(**self).get_root()
|
||||||
|
}
|
||||||
|
}
|
||||||
164
bellman/src/multicore.rs
Normal file
164
bellman/src/multicore.rs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
//! An interface for dealing with the kinds of parallel computations involved in
|
||||||
|
//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and
|
||||||
|
//! [`crossbeam`] but may be extended in the future to allow for various
|
||||||
|
//! parallelism strategies.
|
||||||
|
//!
|
||||||
|
//! [`CpuPool`]: futures_cpupool::CpuPool
|
||||||
|
|
||||||
|
#[cfg(feature = "multicore")]
|
||||||
|
mod implementation {
|
||||||
|
use crossbeam::{self, thread::Scope};
|
||||||
|
use futures::{Future, IntoFuture, Poll};
|
||||||
|
use futures_cpupool::{CpuFuture, CpuPool};
|
||||||
|
use num_cpus;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Worker {
|
||||||
|
cpus: usize,
|
||||||
|
pool: CpuPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Worker {
|
||||||
|
// We don't expose this outside the library so that
|
||||||
|
// all `Worker` instances have the same number of
|
||||||
|
// CPUs configured.
|
||||||
|
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
|
||||||
|
Worker {
|
||||||
|
cpus,
|
||||||
|
pool: CpuPool::new(cpus),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Worker {
|
||||||
|
Self::new_with_cpus(num_cpus::get())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_num_cpus(&self) -> u32 {
|
||||||
|
log2_floor(self.cpus)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute<F, R>(&self, f: F) -> WorkerFuture<R::Item, R::Error>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> R + Send + 'static,
|
||||||
|
R: IntoFuture + 'static,
|
||||||
|
R::Future: Send + 'static,
|
||||||
|
R::Item: Send + 'static,
|
||||||
|
R::Error: Send + 'static,
|
||||||
|
{
|
||||||
|
WorkerFuture {
|
||||||
|
future: self.pool.spawn_fn(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope<'a, F, R>(&self, elements: usize, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&Scope<'a>, usize) -> R,
|
||||||
|
{
|
||||||
|
let chunk_size = if elements < self.cpus {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
elements / self.cpus
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Handle case where threads fail
|
||||||
|
crossbeam::scope(|scope| f(scope, chunk_size))
|
||||||
|
.expect("Threads aren't allowed to fail yet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WorkerFuture<T, E> {
|
||||||
|
future: CpuFuture<T, E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
|
||||||
|
type Item = T;
|
||||||
|
type Error = E;
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
|
self.future.poll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log2_floor(num: usize) -> u32 {
|
||||||
|
assert!(num > 0);
|
||||||
|
|
||||||
|
let mut pow = 0;
|
||||||
|
|
||||||
|
while (1 << (pow + 1)) <= num {
|
||||||
|
pow += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pow
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_log2_floor() {
|
||||||
|
assert_eq!(log2_floor(1), 0);
|
||||||
|
assert_eq!(log2_floor(2), 1);
|
||||||
|
assert_eq!(log2_floor(3), 1);
|
||||||
|
assert_eq!(log2_floor(4), 2);
|
||||||
|
assert_eq!(log2_floor(5), 2);
|
||||||
|
assert_eq!(log2_floor(6), 2);
|
||||||
|
assert_eq!(log2_floor(7), 2);
|
||||||
|
assert_eq!(log2_floor(8), 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "multicore"))]
|
||||||
|
mod implementation {
|
||||||
|
use futures::{future, Future, IntoFuture, Poll};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Worker;
|
||||||
|
|
||||||
|
impl Worker {
|
||||||
|
pub fn new() -> Worker {
|
||||||
|
Worker
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_num_cpus(&self) -> u32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute<F, R>(&self, f: F) -> R::Future
|
||||||
|
where
|
||||||
|
F: FnOnce() -> R + Send + 'static,
|
||||||
|
R: IntoFuture + 'static,
|
||||||
|
R::Future: Send + 'static,
|
||||||
|
R::Item: Send + 'static,
|
||||||
|
R::Error: Send + 'static,
|
||||||
|
{
|
||||||
|
f().into_future()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope<F, R>(&self, elements: usize, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&DummyScope, usize) -> R,
|
||||||
|
{
|
||||||
|
f(&DummyScope, elements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WorkerFuture<T, E> {
|
||||||
|
future: future::FutureResult<T, E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
|
||||||
|
type Item = T;
|
||||||
|
type Error = E;
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
|
self.future.poll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DummyScope;
|
||||||
|
|
||||||
|
impl DummyScope {
|
||||||
|
pub fn spawn<F: FnOnce(&DummyScope)>(&self, f: F) {
|
||||||
|
f(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::implementation::*;
|
||||||
324
bellman/src/multiexp.rs
Normal file
324
bellman/src/multiexp.rs
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
use super::multicore::Worker;
|
||||||
|
use bit_vec::{self, BitVec};
|
||||||
|
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||||
|
use futures::Future;
|
||||||
|
use group::{CurveAffine, CurveProjective};
|
||||||
|
use std::io;
|
||||||
|
use std::iter;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use super::SynthesisError;
|
||||||
|
|
||||||
|
/// An object that builds a source of bases.
|
||||||
|
pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
|
||||||
|
type Source: Source<G>;
|
||||||
|
|
||||||
|
fn new(self) -> Self::Source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A source of bases, like an iterator.
|
||||||
|
pub trait Source<G: CurveAffine> {
|
||||||
|
/// Parses the element from the source. Fails if the point is at infinity.
|
||||||
|
fn add_assign_mixed(
|
||||||
|
&mut self,
|
||||||
|
to: &mut <G as CurveAffine>::Projective,
|
||||||
|
) -> Result<(), SynthesisError>;
|
||||||
|
|
||||||
|
/// Skips `amt` elements from the source, avoiding deserialization.
|
||||||
|
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
|
||||||
|
type Source = (Arc<Vec<G>>, usize);
|
||||||
|
|
||||||
|
fn new(self) -> (Arc<Vec<G>>, usize) {
|
||||||
|
(self.0.clone(), self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
||||||
|
fn add_assign_mixed(
|
||||||
|
&mut self,
|
||||||
|
to: &mut <G as CurveAffine>::Projective,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
|
if self.0.len() <= self.1 {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
"expected more bases from source",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.0[self.1].is_zero() {
|
||||||
|
return Err(SynthesisError::UnexpectedIdentity);
|
||||||
|
}
|
||||||
|
|
||||||
|
to.add_assign_mixed(&self.0[self.1]);
|
||||||
|
|
||||||
|
self.1 += 1;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
|
||||||
|
if self.0.len() <= self.1 {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
"expected more bases from source",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.1 += amt;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait QueryDensity {
|
||||||
|
/// Returns whether the base exists.
|
||||||
|
type Iter: Iterator<Item = bool>;
|
||||||
|
|
||||||
|
fn iter(self) -> Self::Iter;
|
||||||
|
fn get_query_size(self) -> Option<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct FullDensity;
|
||||||
|
|
||||||
|
impl AsRef<FullDensity> for FullDensity {
|
||||||
|
fn as_ref(&self) -> &FullDensity {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> QueryDensity for &'a FullDensity {
|
||||||
|
type Iter = iter::Repeat<bool>;
|
||||||
|
|
||||||
|
fn iter(self) -> Self::Iter {
|
||||||
|
iter::repeat(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_query_size(self) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DensityTracker {
|
||||||
|
bv: BitVec,
|
||||||
|
total_density: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> QueryDensity for &'a DensityTracker {
|
||||||
|
type Iter = bit_vec::Iter<'a>;
|
||||||
|
|
||||||
|
fn iter(self) -> Self::Iter {
|
||||||
|
self.bv.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_query_size(self) -> Option<usize> {
|
||||||
|
Some(self.bv.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DensityTracker {
|
||||||
|
pub fn new() -> DensityTracker {
|
||||||
|
DensityTracker {
|
||||||
|
bv: BitVec::new(),
|
||||||
|
total_density: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_element(&mut self) {
|
||||||
|
self.bv.push(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inc(&mut self, idx: usize) {
|
||||||
|
if !self.bv.get(idx).unwrap() {
|
||||||
|
self.bv.set(idx, true);
|
||||||
|
self.total_density += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_total_density(&self) -> usize {
|
||||||
|
self.total_density
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiexp_inner<Q, D, G, S>(
|
||||||
|
pool: &Worker,
|
||||||
|
bases: S,
|
||||||
|
density_map: D,
|
||||||
|
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
|
||||||
|
mut skip: u32,
|
||||||
|
c: u32,
|
||||||
|
handle_trivial: bool,
|
||||||
|
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||||
|
where
|
||||||
|
for<'a> &'a Q: QueryDensity,
|
||||||
|
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||||
|
G: CurveAffine,
|
||||||
|
S: SourceBuilder<G>,
|
||||||
|
{
|
||||||
|
// Perform this region of the multiexp
|
||||||
|
let this = {
|
||||||
|
let bases = bases.clone();
|
||||||
|
let exponents = exponents.clone();
|
||||||
|
let density_map = density_map.clone();
|
||||||
|
|
||||||
|
pool.compute(move || {
|
||||||
|
// Accumulate the result
|
||||||
|
let mut acc = G::Projective::zero();
|
||||||
|
|
||||||
|
// Build a source for the bases
|
||||||
|
let mut bases = bases.new();
|
||||||
|
|
||||||
|
// Create space for the buckets
|
||||||
|
let mut buckets = vec![<G as CurveAffine>::Projective::zero(); (1 << c) - 1];
|
||||||
|
|
||||||
|
let zero = <G::Engine as ScalarEngine>::Fr::zero().into_repr();
|
||||||
|
let one = <G::Engine as ScalarEngine>::Fr::one().into_repr();
|
||||||
|
|
||||||
|
// Sort the bases into buckets
|
||||||
|
for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) {
|
||||||
|
if density {
|
||||||
|
if exp == zero {
|
||||||
|
bases.skip(1)?;
|
||||||
|
} else if exp == one {
|
||||||
|
if handle_trivial {
|
||||||
|
bases.add_assign_mixed(&mut acc)?;
|
||||||
|
} else {
|
||||||
|
bases.skip(1)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut exp = exp;
|
||||||
|
exp.shr(skip);
|
||||||
|
let exp = exp.as_ref()[0] % (1 << c);
|
||||||
|
|
||||||
|
if exp != 0 {
|
||||||
|
bases.add_assign_mixed(&mut buckets[(exp - 1) as usize])?;
|
||||||
|
} else {
|
||||||
|
bases.skip(1)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Summation by parts
|
||||||
|
// e.g. 3a + 2b + 1c = a +
|
||||||
|
// (a) + b +
|
||||||
|
// ((a) + b) + c
|
||||||
|
let mut running_sum = G::Projective::zero();
|
||||||
|
for exp in buckets.into_iter().rev() {
|
||||||
|
running_sum.add_assign(&exp);
|
||||||
|
acc.add_assign(&running_sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(acc)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
skip += c;
|
||||||
|
|
||||||
|
if skip >= <G::Engine as ScalarEngine>::Fr::NUM_BITS {
|
||||||
|
// There isn't another region.
|
||||||
|
Box::new(this)
|
||||||
|
} else {
|
||||||
|
// There's another region more significant. Calculate and join it with
|
||||||
|
// this region recursively.
|
||||||
|
Box::new(
|
||||||
|
this.join(multiexp_inner(
|
||||||
|
pool,
|
||||||
|
bases,
|
||||||
|
density_map,
|
||||||
|
exponents,
|
||||||
|
skip,
|
||||||
|
c,
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
.map(move |(this, mut higher)| {
|
||||||
|
for _ in 0..c {
|
||||||
|
higher.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
higher.add_assign(&this);
|
||||||
|
|
||||||
|
higher
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform multi-exponentiation. The caller is responsible for ensuring the
|
||||||
|
/// query size is the same as the number of exponents.
|
||||||
|
pub fn multiexp<Q, D, G, S>(
|
||||||
|
pool: &Worker,
|
||||||
|
bases: S,
|
||||||
|
density_map: D,
|
||||||
|
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
|
||||||
|
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||||
|
where
|
||||||
|
for<'a> &'a Q: QueryDensity,
|
||||||
|
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||||
|
G: CurveAffine,
|
||||||
|
S: SourceBuilder<G>,
|
||||||
|
{
|
||||||
|
let c = if exponents.len() < 32 {
|
||||||
|
3u32
|
||||||
|
} else {
|
||||||
|
(f64::from(exponents.len() as u32)).ln().ceil() as u32
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(query_size) = density_map.as_ref().get_query_size() {
|
||||||
|
// If the density map has a known query size, it should not be
|
||||||
|
// inconsistent with the number of exponents.
|
||||||
|
|
||||||
|
assert!(query_size == exponents.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
multiexp_inner(pool, bases, density_map, exponents, 0, c, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "pairing")]
|
||||||
|
#[test]
|
||||||
|
fn test_with_bls12() {
|
||||||
|
fn naive_multiexp<G: CurveAffine>(
|
||||||
|
bases: Arc<Vec<G>>,
|
||||||
|
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>,
|
||||||
|
) -> G::Projective {
|
||||||
|
assert_eq!(bases.len(), exponents.len());
|
||||||
|
|
||||||
|
let mut acc = G::Projective::zero();
|
||||||
|
|
||||||
|
for (base, exp) in bases.iter().zip(exponents.iter()) {
|
||||||
|
acc.add_assign(&base.mul(*exp));
|
||||||
|
}
|
||||||
|
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
use pairing::{bls12_381::Bls12, Engine};
|
||||||
|
use rand;
|
||||||
|
|
||||||
|
const SAMPLES: usize = 1 << 14;
|
||||||
|
|
||||||
|
let rng = &mut rand::thread_rng();
|
||||||
|
let v = Arc::new(
|
||||||
|
(0..SAMPLES)
|
||||||
|
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng).into_repr())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
let g = Arc::new(
|
||||||
|
(0..SAMPLES)
|
||||||
|
.map(|_| <Bls12 as Engine>::G1::random(rng).into_affine())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let naive = naive_multiexp(g.clone(), v.clone());
|
||||||
|
|
||||||
|
let pool = Worker::new();
|
||||||
|
|
||||||
|
let fast = multiexp(&pool, (g, 0), FullDensity, v).wait().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(naive, fast);
|
||||||
|
}
|
||||||
229
bellman/tests/mimc.rs
Normal file
229
bellman/tests/mimc.rs
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
// For randomness (during paramgen and proof generation)
|
||||||
|
use rand::thread_rng;
|
||||||
|
|
||||||
|
// For benchmarking
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
// Bring in some tools for using pairing-friendly curves
|
||||||
|
use ff::{Field, ScalarEngine};
|
||||||
|
use pairing::Engine;
|
||||||
|
|
||||||
|
// We're going to use the BLS12-381 pairing-friendly elliptic curve.
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
|
||||||
|
// We'll use these interfaces to construct our circuit.
|
||||||
|
use bellman::{Circuit, ConstraintSystem, SynthesisError};
|
||||||
|
|
||||||
|
// We're going to use the Groth16 proving system.
|
||||||
|
use bellman::groth16::{
|
||||||
|
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MIMC_ROUNDS: usize = 322;
|
||||||
|
|
||||||
|
/// This is an implementation of MiMC, specifically a
|
||||||
|
/// variant named `LongsightF322p3` for BLS12-381.
|
||||||
|
/// See http://eprint.iacr.org/2016/492 for more
|
||||||
|
/// information about this construction.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) {
|
||||||
|
/// for i from 0 up to 321 {
|
||||||
|
/// xL, xR := xR + (xL + Ci)^3, xL
|
||||||
|
/// }
|
||||||
|
/// return xL
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
fn mimc<E: Engine>(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr {
|
||||||
|
assert_eq!(constants.len(), MIMC_ROUNDS);
|
||||||
|
|
||||||
|
for i in 0..MIMC_ROUNDS {
|
||||||
|
let mut tmp1 = xl;
|
||||||
|
tmp1.add_assign(&constants[i]);
|
||||||
|
let mut tmp2 = tmp1;
|
||||||
|
tmp2.square();
|
||||||
|
tmp2.mul_assign(&tmp1);
|
||||||
|
tmp2.add_assign(&xr);
|
||||||
|
xr = xl;
|
||||||
|
xl = tmp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
xl
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is our demo circuit for proving knowledge of the
|
||||||
|
/// preimage of a MiMC hash invocation.
|
||||||
|
struct MiMCDemo<'a, E: Engine> {
|
||||||
|
xl: Option<E::Fr>,
|
||||||
|
xr: Option<E::Fr>,
|
||||||
|
constants: &'a [E::Fr],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our demo circuit implements this `Circuit` trait which
|
||||||
|
/// is used during paramgen and proving in order to
|
||||||
|
/// synthesize the constraint system.
|
||||||
|
impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
|
||||||
|
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||||
|
assert_eq!(self.constants.len(), MIMC_ROUNDS);
|
||||||
|
|
||||||
|
// Allocate the first component of the preimage.
|
||||||
|
let mut xl_value = self.xl;
|
||||||
|
let mut xl = cs.alloc(
|
||||||
|
|| "preimage xl",
|
||||||
|
|| xl_value.ok_or(SynthesisError::AssignmentMissing),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Allocate the second component of the preimage.
|
||||||
|
let mut xr_value = self.xr;
|
||||||
|
let mut xr = cs.alloc(
|
||||||
|
|| "preimage xr",
|
||||||
|
|| xr_value.ok_or(SynthesisError::AssignmentMissing),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for i in 0..MIMC_ROUNDS {
|
||||||
|
// xL, xR := xR + (xL + Ci)^3, xL
|
||||||
|
let cs = &mut cs.namespace(|| format!("round {}", i));
|
||||||
|
|
||||||
|
// tmp = (xL + Ci)^2
|
||||||
|
let tmp_value = xl_value.map(|mut e| {
|
||||||
|
e.add_assign(&self.constants[i]);
|
||||||
|
e.square();
|
||||||
|
e
|
||||||
|
});
|
||||||
|
let tmp = cs.alloc(
|
||||||
|
|| "tmp",
|
||||||
|
|| tmp_value.ok_or(SynthesisError::AssignmentMissing),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "tmp = (xL + Ci)^2",
|
||||||
|
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||||
|
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||||
|
|lc| lc + tmp,
|
||||||
|
);
|
||||||
|
|
||||||
|
// new_xL = xR + (xL + Ci)^3
|
||||||
|
// new_xL = xR + tmp * (xL + Ci)
|
||||||
|
// new_xL - xR = tmp * (xL + Ci)
|
||||||
|
let new_xl_value = xl_value.map(|mut e| {
|
||||||
|
e.add_assign(&self.constants[i]);
|
||||||
|
e.mul_assign(&tmp_value.unwrap());
|
||||||
|
e.add_assign(&xr_value.unwrap());
|
||||||
|
e
|
||||||
|
});
|
||||||
|
|
||||||
|
let new_xl = if i == (MIMC_ROUNDS - 1) {
|
||||||
|
// This is the last round, xL is our image and so
|
||||||
|
// we allocate a public input.
|
||||||
|
cs.alloc_input(
|
||||||
|
|| "image",
|
||||||
|
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
cs.alloc(
|
||||||
|
|| "new_xl",
|
||||||
|
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "new_xL = xR + (xL + Ci)^3",
|
||||||
|
|lc| lc + tmp,
|
||||||
|
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||||
|
|lc| lc + new_xl - xr,
|
||||||
|
);
|
||||||
|
|
||||||
|
// xR = xL
|
||||||
|
xr = xl;
|
||||||
|
xr_value = xl_value;
|
||||||
|
|
||||||
|
// xL = new_xL
|
||||||
|
xl = new_xl;
|
||||||
|
xl_value = new_xl_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mimc() {
|
||||||
|
// This may not be cryptographically safe, use
|
||||||
|
// `OsRng` (for example) in production software.
|
||||||
|
let rng = &mut thread_rng();
|
||||||
|
|
||||||
|
// Generate the MiMC round constants
|
||||||
|
let constants = (0..MIMC_ROUNDS)
|
||||||
|
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
println!("Creating parameters...");
|
||||||
|
|
||||||
|
// Create parameters for our circuit
|
||||||
|
let params = {
|
||||||
|
let c = MiMCDemo::<Bls12> {
|
||||||
|
xl: None,
|
||||||
|
xr: None,
|
||||||
|
constants: &constants,
|
||||||
|
};
|
||||||
|
|
||||||
|
generate_random_parameters(c, rng).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare the verification key (for proof verification)
|
||||||
|
let pvk = prepare_verifying_key(¶ms.vk);
|
||||||
|
|
||||||
|
println!("Creating proofs...");
|
||||||
|
|
||||||
|
// Let's benchmark stuff!
|
||||||
|
const SAMPLES: u32 = 50;
|
||||||
|
let mut total_proving = Duration::new(0, 0);
|
||||||
|
let mut total_verifying = Duration::new(0, 0);
|
||||||
|
|
||||||
|
// Just a place to put the proof data, so we can
|
||||||
|
// benchmark deserialization.
|
||||||
|
let mut proof_vec = vec![];
|
||||||
|
|
||||||
|
for _ in 0..SAMPLES {
|
||||||
|
// Generate a random preimage and compute the image
|
||||||
|
let xl = <Bls12 as ScalarEngine>::Fr::random(rng);
|
||||||
|
let xr = <Bls12 as ScalarEngine>::Fr::random(rng);
|
||||||
|
let image = mimc::<Bls12>(xl, xr, &constants);
|
||||||
|
|
||||||
|
proof_vec.truncate(0);
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
{
|
||||||
|
// Create an instance of our circuit (with the
|
||||||
|
// witness)
|
||||||
|
let c = MiMCDemo {
|
||||||
|
xl: Some(xl),
|
||||||
|
xr: Some(xr),
|
||||||
|
constants: &constants,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a groth16 proof with our parameters.
|
||||||
|
let proof = create_random_proof(c, ¶ms, rng).unwrap();
|
||||||
|
|
||||||
|
proof.write(&mut proof_vec).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
total_proving += start.elapsed();
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let proof = Proof::read(&proof_vec[..]).unwrap();
|
||||||
|
// Check the proof
|
||||||
|
assert!(verify_proof(&pvk, &proof, &[image]).unwrap());
|
||||||
|
total_verifying += start.elapsed();
|
||||||
|
}
|
||||||
|
let proving_avg = total_proving / SAMPLES;
|
||||||
|
let proving_avg =
|
||||||
|
proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64);
|
||||||
|
|
||||||
|
let verifying_avg = total_verifying / SAMPLES;
|
||||||
|
let verifying_avg =
|
||||||
|
verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64);
|
||||||
|
|
||||||
|
println!("Average proving time: {:?} seconds", proving_avg);
|
||||||
|
println!("Average verifying time: {:?} seconds", verifying_avg);
|
||||||
|
}
|
||||||
3
ff/.gitignore
vendored
Normal file
3
ff/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
target/
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
23
ff/Cargo.toml
Normal file
23
ff/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "ff"
|
||||||
|
version = "0.5.2"
|
||||||
|
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||||
|
description = "Library for building and interfacing with finite fields"
|
||||||
|
readme = "README.md"
|
||||||
|
documentation = "https://docs.rs/ff/"
|
||||||
|
homepage = "https://github.com/ebfull/ff"
|
||||||
|
license = "MIT/Apache-2.0"
|
||||||
|
repository = "https://github.com/ebfull/ff"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
byteorder = "1"
|
||||||
|
ff_derive = { version = "^0.4.1", path = "ff_derive", optional = true }
|
||||||
|
rand_core = "0.5"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
derive = ["ff_derive"]
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
202
ff/LICENSE-APACHE
Normal file
202
ff/LICENSE-APACHE
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
21
ff/LICENSE-MIT
Normal file
21
ff/LICENSE-MIT
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017 Sean Bowe
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
67
ff/README.md
Normal file
67
ff/README.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# ff
|
||||||
|
|
||||||
|
`ff` is a finite field library written in pure Rust, with no `unsafe{}` code.
|
||||||
|
|
||||||
|
## Disclaimers
|
||||||
|
|
||||||
|
* This library does not provide constant-time guarantees.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Add the `ff` crate to your `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
ff = "0.5"
|
||||||
|
```
|
||||||
|
|
||||||
|
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits.
|
||||||
|
See the **[documentation](https://docs.rs/ff/)** for more.
|
||||||
|
|
||||||
|
### #![derive(PrimeField)]
|
||||||
|
|
||||||
|
If you need an implementation of a prime field, this library also provides a procedural
|
||||||
|
macro that will expand into an efficient implementation of a prime field when supplied
|
||||||
|
with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is
|
||||||
|
also quadratic nonresidue.
|
||||||
|
|
||||||
|
First, enable the `derive` crate feature:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
ff = { version = "0.4", features = ["derive"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
And then use the macro like so:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
extern crate rand;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate ff;
|
||||||
|
|
||||||
|
#[derive(PrimeField)]
|
||||||
|
#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"]
|
||||||
|
#[PrimeFieldGenerator = "7"]
|
||||||
|
struct Fp(FpRepr);
|
||||||
|
```
|
||||||
|
|
||||||
|
And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement
|
||||||
|
`SqrtField` if supported. The library implements `FpRepr` itself and derives
|
||||||
|
`PrimeFieldRepr` for it.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
### Contribution
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally
|
||||||
|
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||||
|
license, shall be dual licensed as above, without any additional terms or
|
||||||
|
conditions.
|
||||||
24
ff/ff_derive/Cargo.toml
Normal file
24
ff/ff_derive/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "ff_derive"
|
||||||
|
version = "0.4.1"
|
||||||
|
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||||
|
description = "Procedural macro library used to build custom prime field implementations"
|
||||||
|
documentation = "https://docs.rs/ff/"
|
||||||
|
homepage = "https://github.com/ebfull/ff"
|
||||||
|
license = "MIT/Apache-2.0"
|
||||||
|
repository = "https://github.com/ebfull/ff"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
num-bigint = "0.2"
|
||||||
|
num-traits = "0.2"
|
||||||
|
num-integer = "0.1"
|
||||||
|
proc-macro2 = "1"
|
||||||
|
quote = "1"
|
||||||
|
syn = "1"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "passively-maintained" }
|
||||||
1057
ff/ff_derive/src/lib.rs
Normal file
1057
ff/ff_derive/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
393
ff/src/lib.rs
Normal file
393
ff/src/lib.rs
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
//! This crate provides traits for working with finite fields.
|
||||||
|
|
||||||
|
// Catch documentation errors caused by code changes.
|
||||||
|
#![deny(intra_doc_link_resolution_failure)]
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
|
#[cfg(feature = "derive")]
|
||||||
|
pub use ff_derive::*;
|
||||||
|
|
||||||
|
use rand_core::RngCore;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
|
/// This trait represents an element of a field.
|
||||||
|
pub trait Field:
|
||||||
|
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
|
||||||
|
{
|
||||||
|
/// Returns an element chosen uniformly at random using a user-provided RNG.
|
||||||
|
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self;
|
||||||
|
|
||||||
|
/// Returns the zero element of the field, the additive identity.
|
||||||
|
fn zero() -> Self;
|
||||||
|
|
||||||
|
/// Returns the one element of the field, the multiplicative identity.
|
||||||
|
fn one() -> Self;
|
||||||
|
|
||||||
|
/// Returns true iff this element is zero.
|
||||||
|
fn is_zero(&self) -> bool;
|
||||||
|
|
||||||
|
/// Squares this element.
|
||||||
|
fn square(&mut self);
|
||||||
|
|
||||||
|
/// Doubles this element.
|
||||||
|
fn double(&mut self);
|
||||||
|
|
||||||
|
/// Negates this element.
|
||||||
|
fn negate(&mut self);
|
||||||
|
|
||||||
|
/// Adds another element to this element.
|
||||||
|
fn add_assign(&mut self, other: &Self);
|
||||||
|
|
||||||
|
/// Subtracts another element from this element.
|
||||||
|
fn sub_assign(&mut self, other: &Self);
|
||||||
|
|
||||||
|
/// Multiplies another element by this element.
|
||||||
|
fn mul_assign(&mut self, other: &Self);
|
||||||
|
|
||||||
|
/// Computes the multiplicative inverse of this element, if nonzero.
|
||||||
|
fn inverse(&self) -> Option<Self>;
|
||||||
|
|
||||||
|
/// Exponentiates this element by a power of the base prime modulus via
|
||||||
|
/// the Frobenius automorphism.
|
||||||
|
fn frobenius_map(&mut self, power: usize);
|
||||||
|
|
||||||
|
/// Exponentiates this element by a number represented with `u64` limbs,
|
||||||
|
/// least significant digit first.
|
||||||
|
fn pow<S: AsRef<[u64]>>(&self, exp: S) -> Self {
|
||||||
|
let mut res = Self::one();
|
||||||
|
|
||||||
|
let mut found_one = false;
|
||||||
|
|
||||||
|
for i in BitIterator::new(exp) {
|
||||||
|
if found_one {
|
||||||
|
res.square();
|
||||||
|
} else {
|
||||||
|
found_one = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i {
|
||||||
|
res.mul_assign(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This trait represents an element of a field that has a square root operation described for it.
|
||||||
|
pub trait SqrtField: Field {
|
||||||
|
/// Returns the Legendre symbol of the field element.
|
||||||
|
fn legendre(&self) -> LegendreSymbol;
|
||||||
|
|
||||||
|
/// Returns the square root of the field element, if it is
|
||||||
|
/// quadratic residue.
|
||||||
|
fn sqrt(&self) -> Option<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This trait represents a wrapper around a biginteger which can encode any element of a particular
|
||||||
|
/// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit
|
||||||
|
/// first.
|
||||||
|
pub trait PrimeFieldRepr:
|
||||||
|
Sized
|
||||||
|
+ Copy
|
||||||
|
+ Clone
|
||||||
|
+ Eq
|
||||||
|
+ Ord
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ Default
|
||||||
|
+ fmt::Debug
|
||||||
|
+ fmt::Display
|
||||||
|
+ 'static
|
||||||
|
+ AsRef<[u64]>
|
||||||
|
+ AsMut<[u64]>
|
||||||
|
+ From<u64>
|
||||||
|
{
|
||||||
|
/// Subtract another represetation from this one.
|
||||||
|
fn sub_noborrow(&mut self, other: &Self);
|
||||||
|
|
||||||
|
/// Add another representation to this one.
|
||||||
|
fn add_nocarry(&mut self, other: &Self);
|
||||||
|
|
||||||
|
/// Compute the number of bits needed to encode this number. Always a
|
||||||
|
/// multiple of 64.
|
||||||
|
fn num_bits(&self) -> u32;
|
||||||
|
|
||||||
|
/// Returns true iff this number is zero.
|
||||||
|
fn is_zero(&self) -> bool;
|
||||||
|
|
||||||
|
/// Returns true iff this number is odd.
|
||||||
|
fn is_odd(&self) -> bool;
|
||||||
|
|
||||||
|
/// Returns true iff this number is even.
|
||||||
|
fn is_even(&self) -> bool;
|
||||||
|
|
||||||
|
/// Performs a rightwise bitshift of this number, effectively dividing
|
||||||
|
/// it by 2.
|
||||||
|
fn div2(&mut self);
|
||||||
|
|
||||||
|
/// Performs a rightwise bitshift of this number by some amount.
|
||||||
|
fn shr(&mut self, amt: u32);
|
||||||
|
|
||||||
|
/// Performs a leftwise bitshift of this number, effectively multiplying
|
||||||
|
/// it by 2. Overflow is ignored.
|
||||||
|
fn mul2(&mut self);
|
||||||
|
|
||||||
|
/// Performs a leftwise bitshift of this number by some amount.
|
||||||
|
fn shl(&mut self, amt: u32);
|
||||||
|
|
||||||
|
/// Writes this `PrimeFieldRepr` as a big endian integer.
|
||||||
|
fn write_be<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
use byteorder::{BigEndian, WriteBytesExt};
|
||||||
|
|
||||||
|
for digit in self.as_ref().iter().rev() {
|
||||||
|
writer.write_u64::<BigEndian>(*digit)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a big endian integer into this representation.
|
||||||
|
fn read_be<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
|
|
||||||
|
for digit in self.as_mut().iter_mut().rev() {
|
||||||
|
*digit = reader.read_u64::<BigEndian>()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes this `PrimeFieldRepr` as a little endian integer.
|
||||||
|
fn write_le<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
use byteorder::{LittleEndian, WriteBytesExt};
|
||||||
|
|
||||||
|
for digit in self.as_ref().iter() {
|
||||||
|
writer.write_u64::<LittleEndian>(*digit)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a little endian integer into this representation.
|
||||||
|
fn read_le<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
|
|
||||||
|
for digit in self.as_mut().iter_mut() {
|
||||||
|
*digit = reader.read_u64::<LittleEndian>()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum LegendreSymbol {
|
||||||
|
Zero = 0,
|
||||||
|
QuadraticResidue = 1,
|
||||||
|
QuadraticNonResidue = -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a
|
||||||
|
/// `PrimeField` element.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PrimeFieldDecodingError {
|
||||||
|
/// The encoded value is not in the field
|
||||||
|
NotInField(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for PrimeFieldDecodingError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
PrimeFieldDecodingError::NotInField(..) => "not an element of the field",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PrimeFieldDecodingError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
match *self {
|
||||||
|
PrimeFieldDecodingError::NotInField(ref repr) => {
|
||||||
|
write!(f, "{} is not an element of the field", repr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This represents an element of a prime field.
|
||||||
|
pub trait PrimeField: Field {
|
||||||
|
/// The prime field can be converted back and forth into this biginteger
|
||||||
|
/// representation.
|
||||||
|
type Repr: PrimeFieldRepr + From<Self>;
|
||||||
|
|
||||||
|
/// Interpret a string of numbers as a (congruent) prime field element.
|
||||||
|
/// Does not accept unnecessary leading zeroes or a blank string.
|
||||||
|
fn from_str(s: &str) -> Option<Self> {
|
||||||
|
if s.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == "0" {
|
||||||
|
return Some(Self::zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = Self::zero();
|
||||||
|
|
||||||
|
let ten = Self::from_repr(Self::Repr::from(10)).unwrap();
|
||||||
|
|
||||||
|
let mut first_digit = true;
|
||||||
|
|
||||||
|
for c in s.chars() {
|
||||||
|
match c.to_digit(10) {
|
||||||
|
Some(c) => {
|
||||||
|
if first_digit {
|
||||||
|
if c == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
first_digit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.mul_assign(&ten);
|
||||||
|
res.add_assign(&Self::from_repr(Self::Repr::from(u64::from(c))).unwrap());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert this prime field element into a biginteger representation.
|
||||||
|
fn from_repr(_: Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
|
||||||
|
|
||||||
|
/// Convert a biginteger representation into a prime field element, if
|
||||||
|
/// the number is an element of the field.
|
||||||
|
fn into_repr(&self) -> Self::Repr;
|
||||||
|
|
||||||
|
/// Returns the field characteristic; the modulus.
|
||||||
|
fn char() -> Self::Repr;
|
||||||
|
|
||||||
|
/// How many bits are needed to represent an element of this field.
|
||||||
|
const NUM_BITS: u32;
|
||||||
|
|
||||||
|
/// How many bits of information can be reliably stored in the field element.
|
||||||
|
const CAPACITY: u32;
|
||||||
|
|
||||||
|
/// Returns the multiplicative generator of `char()` - 1 order. This element
|
||||||
|
/// must also be quadratic nonresidue.
|
||||||
|
fn multiplicative_generator() -> Self;
|
||||||
|
|
||||||
|
/// 2^s * t = `char()` - 1 with t odd.
|
||||||
|
const S: u32;
|
||||||
|
|
||||||
|
/// Returns the 2^s root of unity computed by exponentiating the `multiplicative_generator()`
|
||||||
|
/// by t.
|
||||||
|
fn root_of_unity() -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An "engine" is a collection of types (fields, elliptic curve groups, etc.)
|
||||||
|
/// with well-defined relationships. Specific relationships (for example, a
|
||||||
|
/// pairing-friendly curve) can be defined in a subtrait.
|
||||||
|
pub trait ScalarEngine: Sized + 'static + Clone {
|
||||||
|
/// This is the scalar field of the engine's groups.
|
||||||
|
type Fr: PrimeField + SqrtField;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BitIterator<E> {
|
||||||
|
t: E,
|
||||||
|
n: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: AsRef<[u64]>> BitIterator<E> {
|
||||||
|
pub fn new(t: E) -> Self {
|
||||||
|
let n = t.as_ref().len() * 64;
|
||||||
|
|
||||||
|
BitIterator { t, n }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: AsRef<[u64]>> Iterator for BitIterator<E> {
|
||||||
|
type Item = bool;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<bool> {
|
||||||
|
if self.n == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.n -= 1;
|
||||||
|
let part = self.n / 64;
|
||||||
|
let bit = self.n - (64 * part);
|
||||||
|
|
||||||
|
Some(self.t.as_ref()[part] & (1 << bit) > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bit_iterator() {
|
||||||
|
let mut a = BitIterator::new([0xa953d79b83f6ab59, 0x6dea2059e200bd39]);
|
||||||
|
let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001";
|
||||||
|
|
||||||
|
for e in expected.chars() {
|
||||||
|
assert!(a.next().unwrap() == (e == '1'));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(a.next().is_none());
|
||||||
|
|
||||||
|
let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001";
|
||||||
|
|
||||||
|
let mut a = BitIterator::new([
|
||||||
|
0x429d5f3ac3a3b759,
|
||||||
|
0xb10f4c66768b1c92,
|
||||||
|
0x92368b6d16ecd3b4,
|
||||||
|
0xa57ea85ae8775219,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for e in expected.chars() {
|
||||||
|
assert!(a.next().unwrap() == (e == '1'));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(a.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::arith_impl::*;
|
||||||
|
|
||||||
|
mod arith_impl {
|
||||||
|
/// Calculate a - b - borrow, returning the result and modifying
|
||||||
|
/// the borrow value.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 {
|
||||||
|
let tmp = (1u128 << 64) + u128::from(a) - u128::from(b) - u128::from(*borrow);
|
||||||
|
|
||||||
|
*borrow = if tmp >> 64 == 0 { 1 } else { 0 };
|
||||||
|
|
||||||
|
tmp as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate a + b + carry, returning the sum and modifying the
|
||||||
|
/// carry value.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 {
|
||||||
|
let tmp = u128::from(a) + u128::from(b) + u128::from(*carry);
|
||||||
|
|
||||||
|
*carry = (tmp >> 64) as u64;
|
||||||
|
|
||||||
|
tmp as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate a + (b * c) + carry, returning the least significant digit
|
||||||
|
/// and setting carry to the most significant digit.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
|
||||||
|
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c) + u128::from(*carry);
|
||||||
|
|
||||||
|
*carry = (tmp >> 64) as u64;
|
||||||
|
|
||||||
|
tmp as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
3
group/.gitignore
vendored
Normal file
3
group/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
14
group/COPYRIGHT
Normal file
14
group/COPYRIGHT
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
Copyrights in the "group" library are retained by their contributors. No
|
||||||
|
copyright assignment is required to contribute to the "group" library.
|
||||||
|
|
||||||
|
The "group" library is licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally
|
||||||
|
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||||
|
license, shall be dual licensed as above, without any additional terms or
|
||||||
|
conditions.
|
||||||
23
group/Cargo.toml
Normal file
23
group/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "group"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = [
|
||||||
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
|
"Jack Grigg <jack@z.cash>",
|
||||||
|
]
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT/Apache-2.0"
|
||||||
|
|
||||||
|
description = "Elliptic curve group traits and utilities"
|
||||||
|
documentation = "https://docs.rs/group/"
|
||||||
|
homepage = "https://github.com/ebfull/group"
|
||||||
|
repository = "https://github.com/ebfull/group"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
|
rand = "0.7"
|
||||||
|
rand_xorshift = "0.2"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
201
group/LICENSE-APACHE
Normal file
201
group/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
23
group/LICENSE-MIT
Normal file
23
group/LICENSE-MIT
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Permission is hereby granted, free of charge, to any
|
||||||
|
person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without
|
||||||
|
limitation the rights to use, copy, modify, merge,
|
||||||
|
publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software
|
||||||
|
is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice
|
||||||
|
shall be included in all copies or substantial portions
|
||||||
|
of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
20
group/README.md
Normal file
20
group/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# group [](https://crates.io/crates/group) #
|
||||||
|
|
||||||
|
`group` is a crate for working with groups over elliptic curves.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
### Contribution
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally
|
||||||
|
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||||
|
license, shall be dual licensed as above, without any additional terms or
|
||||||
|
conditions.
|
||||||
190
group/src/lib.rs
Normal file
190
group/src/lib.rs
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
// Catch documentation errors caused by code changes.
|
||||||
|
#![deny(intra_doc_link_resolution_failure)]
|
||||||
|
|
||||||
|
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
|
||||||
|
use rand::RngCore;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
pub mod tests;
|
||||||
|
|
||||||
|
mod wnaf;
|
||||||
|
pub use self::wnaf::Wnaf;
|
||||||
|
|
||||||
|
/// Projective representation of an elliptic curve point guaranteed to be
|
||||||
|
/// in the correct prime order subgroup.
|
||||||
|
pub trait CurveProjective:
|
||||||
|
PartialEq + Eq + Sized + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
|
||||||
|
{
|
||||||
|
type Engine: ScalarEngine<Fr = Self::Scalar>;
|
||||||
|
type Scalar: PrimeField + SqrtField;
|
||||||
|
type Base: SqrtField;
|
||||||
|
type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>;
|
||||||
|
|
||||||
|
/// Returns an element chosen uniformly at random using a user-provided RNG.
|
||||||
|
fn random<R: RngCore>(rng: &mut R) -> Self;
|
||||||
|
|
||||||
|
/// Returns the additive identity.
|
||||||
|
fn zero() -> Self;
|
||||||
|
|
||||||
|
/// Returns a fixed generator of unknown exponent.
|
||||||
|
fn one() -> Self;
|
||||||
|
|
||||||
|
/// Determines if this point is the point at infinity.
|
||||||
|
fn is_zero(&self) -> bool;
|
||||||
|
|
||||||
|
/// Normalizes a slice of projective elements so that
|
||||||
|
/// conversion to affine is cheap.
|
||||||
|
fn batch_normalization(v: &mut [Self]);
|
||||||
|
|
||||||
|
/// Checks if the point is already "normalized" so that
|
||||||
|
/// cheap affine conversion is possible.
|
||||||
|
fn is_normalized(&self) -> bool;
|
||||||
|
|
||||||
|
/// Doubles this element.
|
||||||
|
fn double(&mut self);
|
||||||
|
|
||||||
|
/// Adds another element to this element.
|
||||||
|
fn add_assign(&mut self, other: &Self);
|
||||||
|
|
||||||
|
/// Subtracts another element from this element.
|
||||||
|
fn sub_assign(&mut self, other: &Self) {
|
||||||
|
let mut tmp = *other;
|
||||||
|
tmp.negate();
|
||||||
|
self.add_assign(&tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an affine element to this element.
|
||||||
|
fn add_assign_mixed(&mut self, other: &Self::Affine);
|
||||||
|
|
||||||
|
/// Negates this element.
|
||||||
|
fn negate(&mut self);
|
||||||
|
|
||||||
|
/// Performs scalar multiplication of this element.
|
||||||
|
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S);
|
||||||
|
|
||||||
|
/// Converts this element into its affine representation.
|
||||||
|
fn into_affine(&self) -> Self::Affine;
|
||||||
|
|
||||||
|
/// Recommends a wNAF window table size given a scalar. Always returns a number
|
||||||
|
/// between 2 and 22, inclusive.
|
||||||
|
fn recommended_wnaf_for_scalar(scalar: <Self::Scalar as PrimeField>::Repr) -> usize;
|
||||||
|
|
||||||
|
/// Recommends a wNAF window size given the number of scalars you intend to multiply
|
||||||
|
/// a base by. Always returns a number between 2 and 22, inclusive.
|
||||||
|
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Affine representation of an elliptic curve point guaranteed to be
|
||||||
|
/// in the correct prime order subgroup.
|
||||||
|
pub trait CurveAffine:
|
||||||
|
Copy + Clone + Sized + Send + Sync + fmt::Debug + fmt::Display + PartialEq + Eq + 'static
|
||||||
|
{
|
||||||
|
type Engine: ScalarEngine<Fr = Self::Scalar>;
|
||||||
|
type Scalar: PrimeField + SqrtField;
|
||||||
|
type Base: SqrtField;
|
||||||
|
type Projective: CurveProjective<Affine = Self, Scalar = Self::Scalar>;
|
||||||
|
type Uncompressed: EncodedPoint<Affine = Self>;
|
||||||
|
type Compressed: EncodedPoint<Affine = Self>;
|
||||||
|
|
||||||
|
/// Returns the additive identity.
|
||||||
|
fn zero() -> Self;
|
||||||
|
|
||||||
|
/// Returns a fixed generator of unknown exponent.
|
||||||
|
fn one() -> Self;
|
||||||
|
|
||||||
|
/// Determines if this point represents the point at infinity; the
|
||||||
|
/// additive identity.
|
||||||
|
fn is_zero(&self) -> bool;
|
||||||
|
|
||||||
|
/// Negates this element.
|
||||||
|
fn negate(&mut self);
|
||||||
|
|
||||||
|
/// Performs scalar multiplication of this element with mixed addition.
|
||||||
|
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective;
|
||||||
|
|
||||||
|
/// Converts this element into its affine representation.
|
||||||
|
fn into_projective(&self) -> Self::Projective;
|
||||||
|
|
||||||
|
/// Converts this element into its compressed encoding, so long as it's not
|
||||||
|
/// the point at infinity.
|
||||||
|
fn into_compressed(&self) -> Self::Compressed {
|
||||||
|
<Self::Compressed as EncodedPoint>::from_affine(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts this element into its uncompressed encoding, so long as it's not
|
||||||
|
/// the point at infinity.
|
||||||
|
fn into_uncompressed(&self) -> Self::Uncompressed {
|
||||||
|
<Self::Uncompressed as EncodedPoint>::from_affine(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An encoded elliptic curve point, which should essentially wrap a `[u8; N]`.
|
||||||
|
pub trait EncodedPoint:
|
||||||
|
Sized + Send + Sync + AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + 'static
|
||||||
|
{
|
||||||
|
type Affine: CurveAffine;
|
||||||
|
|
||||||
|
/// Creates an empty representation.
|
||||||
|
fn empty() -> Self;
|
||||||
|
|
||||||
|
/// Returns the number of bytes consumed by this representation.
|
||||||
|
fn size() -> usize;
|
||||||
|
|
||||||
|
/// Converts an `EncodedPoint` into a `CurveAffine` element,
|
||||||
|
/// if the encoding represents a valid element.
|
||||||
|
fn into_affine(&self) -> Result<Self::Affine, GroupDecodingError>;
|
||||||
|
|
||||||
|
/// Converts an `EncodedPoint` into a `CurveAffine` element,
|
||||||
|
/// without guaranteeing that the encoding represents a valid
|
||||||
|
/// element. This is useful when the caller knows the encoding is
|
||||||
|
/// valid already.
|
||||||
|
///
|
||||||
|
/// If the encoding is invalid, this can break API invariants,
|
||||||
|
/// so caution is strongly encouraged.
|
||||||
|
fn into_affine_unchecked(&self) -> Result<Self::Affine, GroupDecodingError>;
|
||||||
|
|
||||||
|
/// Creates an `EncodedPoint` from an affine point, as long as the
|
||||||
|
/// point is not the point at infinity.
|
||||||
|
fn from_affine(affine: Self::Affine) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error that may occur when trying to decode an `EncodedPoint`.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum GroupDecodingError {
|
||||||
|
/// The coordinate(s) do not lie on the curve.
|
||||||
|
NotOnCurve,
|
||||||
|
/// The element is not part of the r-order subgroup.
|
||||||
|
NotInSubgroup,
|
||||||
|
/// One of the coordinates could not be decoded
|
||||||
|
CoordinateDecodingError(&'static str, PrimeFieldDecodingError),
|
||||||
|
/// The compression mode of the encoded element was not as expected
|
||||||
|
UnexpectedCompressionMode,
|
||||||
|
/// The encoding contained bits that should not have been set
|
||||||
|
UnexpectedInformation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for GroupDecodingError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve",
|
||||||
|
GroupDecodingError::NotInSubgroup => "the element is not part of an r-order subgroup",
|
||||||
|
GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded",
|
||||||
|
GroupDecodingError::UnexpectedCompressionMode => {
|
||||||
|
"encoding has unexpected compression mode"
|
||||||
|
}
|
||||||
|
GroupDecodingError::UnexpectedInformation => "encoding has unexpected information",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for GroupDecodingError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
match *self {
|
||||||
|
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
|
||||||
|
write!(f, "{} decoding error: {}", description, err)
|
||||||
|
}
|
||||||
|
_ => write!(f, "{}", self.description()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
449
group/src/tests/mod.rs
Normal file
449
group/src/tests/mod.rs
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
use ff::{Field, PrimeField};
|
||||||
|
use rand::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use crate::{CurveAffine, CurveProjective, EncodedPoint};
|
||||||
|
|
||||||
|
pub fn curve_tests<G: CurveProjective>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Negation edge case with zero.
|
||||||
|
{
|
||||||
|
let mut z = G::zero();
|
||||||
|
z.negate();
|
||||||
|
assert!(z.is_zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doubling edge case with zero.
|
||||||
|
{
|
||||||
|
let mut z = G::zero();
|
||||||
|
z.double();
|
||||||
|
assert!(z.is_zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addition edge cases with zero
|
||||||
|
{
|
||||||
|
let mut r = G::random(&mut rng);
|
||||||
|
let rcopy = r;
|
||||||
|
r.add_assign(&G::zero());
|
||||||
|
assert_eq!(r, rcopy);
|
||||||
|
r.add_assign_mixed(&G::Affine::zero());
|
||||||
|
assert_eq!(r, rcopy);
|
||||||
|
|
||||||
|
let mut z = G::zero();
|
||||||
|
z.add_assign(&G::zero());
|
||||||
|
assert!(z.is_zero());
|
||||||
|
z.add_assign_mixed(&G::Affine::zero());
|
||||||
|
assert!(z.is_zero());
|
||||||
|
|
||||||
|
let mut z2 = z;
|
||||||
|
z2.add_assign(&r);
|
||||||
|
|
||||||
|
z.add_assign_mixed(&r.into_affine());
|
||||||
|
|
||||||
|
assert_eq!(z, z2);
|
||||||
|
assert_eq!(z, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transformations
|
||||||
|
{
|
||||||
|
let a = G::random(&mut rng);
|
||||||
|
let b = a.into_affine().into_projective();
|
||||||
|
let c = a
|
||||||
|
.into_affine()
|
||||||
|
.into_projective()
|
||||||
|
.into_affine()
|
||||||
|
.into_projective();
|
||||||
|
assert_eq!(a, b);
|
||||||
|
assert_eq!(b, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
random_addition_tests::<G>();
|
||||||
|
random_multiplication_tests::<G>();
|
||||||
|
random_doubling_tests::<G>();
|
||||||
|
random_negation_tests::<G>();
|
||||||
|
random_transformation_tests::<G>();
|
||||||
|
random_wnaf_tests::<G>();
|
||||||
|
random_encoding_tests::<G::Affine>();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_wnaf_tests<G: CurveProjective>() {
|
||||||
|
use crate::wnaf::*;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut table = vec![];
|
||||||
|
let mut wnaf = vec![];
|
||||||
|
|
||||||
|
for w in 2..14 {
|
||||||
|
for _ in 0..100 {
|
||||||
|
let g = G::random(&mut rng);
|
||||||
|
let s = G::Scalar::random(&mut rng).into_repr();
|
||||||
|
let mut g1 = g;
|
||||||
|
g1.mul_assign(s);
|
||||||
|
|
||||||
|
wnaf_table(&mut table, g, w);
|
||||||
|
wnaf_form(&mut wnaf, s, w);
|
||||||
|
let g2 = wnaf_exp(&table, &wnaf);
|
||||||
|
|
||||||
|
assert_eq!(g1, g2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
fn only_compiles_if_send<S: Send>(_: &S) {}
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
let g = G::random(&mut rng);
|
||||||
|
let s = G::Scalar::random(&mut rng).into_repr();
|
||||||
|
let mut g1 = g;
|
||||||
|
g1.mul_assign(s);
|
||||||
|
|
||||||
|
let g2 = {
|
||||||
|
let mut wnaf = Wnaf::new();
|
||||||
|
wnaf.base(g, 1).scalar(s)
|
||||||
|
};
|
||||||
|
let g3 = {
|
||||||
|
let mut wnaf = Wnaf::new();
|
||||||
|
wnaf.scalar(s).base(g)
|
||||||
|
};
|
||||||
|
let g4 = {
|
||||||
|
let mut wnaf = Wnaf::new();
|
||||||
|
let mut shared = wnaf.base(g, 1).shared();
|
||||||
|
|
||||||
|
only_compiles_if_send(&shared);
|
||||||
|
|
||||||
|
shared.scalar(s)
|
||||||
|
};
|
||||||
|
let g5 = {
|
||||||
|
let mut wnaf = Wnaf::new();
|
||||||
|
let mut shared = wnaf.scalar(s).shared();
|
||||||
|
|
||||||
|
only_compiles_if_send(&shared);
|
||||||
|
|
||||||
|
shared.base(g)
|
||||||
|
};
|
||||||
|
|
||||||
|
let g6 = {
|
||||||
|
let mut wnaf = Wnaf::new();
|
||||||
|
{
|
||||||
|
// Populate the vectors.
|
||||||
|
wnaf.base(G::random(&mut rng), 1)
|
||||||
|
.scalar(G::Scalar::random(&mut rng).into_repr());
|
||||||
|
}
|
||||||
|
wnaf.base(g, 1).scalar(s)
|
||||||
|
};
|
||||||
|
let g7 = {
|
||||||
|
let mut wnaf = Wnaf::new();
|
||||||
|
{
|
||||||
|
// Populate the vectors.
|
||||||
|
wnaf.base(G::random(&mut rng), 1)
|
||||||
|
.scalar(G::Scalar::random(&mut rng).into_repr());
|
||||||
|
}
|
||||||
|
wnaf.scalar(s).base(g)
|
||||||
|
};
|
||||||
|
let g8 = {
|
||||||
|
let mut wnaf = Wnaf::new();
|
||||||
|
{
|
||||||
|
// Populate the vectors.
|
||||||
|
wnaf.base(G::random(&mut rng), 1)
|
||||||
|
.scalar(G::Scalar::random(&mut rng).into_repr());
|
||||||
|
}
|
||||||
|
let mut shared = wnaf.base(g, 1).shared();
|
||||||
|
|
||||||
|
only_compiles_if_send(&shared);
|
||||||
|
|
||||||
|
shared.scalar(s)
|
||||||
|
};
|
||||||
|
let g9 = {
|
||||||
|
let mut wnaf = Wnaf::new();
|
||||||
|
{
|
||||||
|
// Populate the vectors.
|
||||||
|
wnaf.base(G::random(&mut rng), 1)
|
||||||
|
.scalar(G::Scalar::random(&mut rng).into_repr());
|
||||||
|
}
|
||||||
|
let mut shared = wnaf.scalar(s).shared();
|
||||||
|
|
||||||
|
only_compiles_if_send(&shared);
|
||||||
|
|
||||||
|
shared.base(g)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(g1, g2);
|
||||||
|
assert_eq!(g1, g3);
|
||||||
|
assert_eq!(g1, g4);
|
||||||
|
assert_eq!(g1, g5);
|
||||||
|
assert_eq!(g1, g6);
|
||||||
|
assert_eq!(g1, g7);
|
||||||
|
assert_eq!(g1, g8);
|
||||||
|
assert_eq!(g1, g9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_negation_tests<G: CurveProjective>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let r = G::random(&mut rng);
|
||||||
|
|
||||||
|
let s = G::Scalar::random(&mut rng);
|
||||||
|
let mut sneg = s;
|
||||||
|
sneg.negate();
|
||||||
|
|
||||||
|
let mut t1 = r;
|
||||||
|
t1.mul_assign(s);
|
||||||
|
|
||||||
|
let mut t2 = r;
|
||||||
|
t2.mul_assign(sneg);
|
||||||
|
|
||||||
|
let mut t3 = t1;
|
||||||
|
t3.add_assign(&t2);
|
||||||
|
assert!(t3.is_zero());
|
||||||
|
|
||||||
|
let mut t4 = t1;
|
||||||
|
t4.add_assign_mixed(&t2.into_affine());
|
||||||
|
assert!(t4.is_zero());
|
||||||
|
|
||||||
|
t1.negate();
|
||||||
|
assert_eq!(t1, t2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_doubling_tests<G: CurveProjective>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let mut a = G::random(&mut rng);
|
||||||
|
let mut b = G::random(&mut rng);
|
||||||
|
|
||||||
|
// 2(a + b)
|
||||||
|
let mut tmp1 = a;
|
||||||
|
tmp1.add_assign(&b);
|
||||||
|
tmp1.double();
|
||||||
|
|
||||||
|
// 2a + 2b
|
||||||
|
a.double();
|
||||||
|
b.double();
|
||||||
|
|
||||||
|
let mut tmp2 = a;
|
||||||
|
tmp2.add_assign(&b);
|
||||||
|
|
||||||
|
let mut tmp3 = a;
|
||||||
|
tmp3.add_assign_mixed(&b.into_affine());
|
||||||
|
|
||||||
|
assert_eq!(tmp1, tmp2);
|
||||||
|
assert_eq!(tmp1, tmp3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_multiplication_tests<G: CurveProjective>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let mut a = G::random(&mut rng);
|
||||||
|
let mut b = G::random(&mut rng);
|
||||||
|
let a_affine = a.into_affine();
|
||||||
|
let b_affine = b.into_affine();
|
||||||
|
|
||||||
|
let s = G::Scalar::random(&mut rng);
|
||||||
|
|
||||||
|
// s ( a + b )
|
||||||
|
let mut tmp1 = a;
|
||||||
|
tmp1.add_assign(&b);
|
||||||
|
tmp1.mul_assign(s);
|
||||||
|
|
||||||
|
// sa + sb
|
||||||
|
a.mul_assign(s);
|
||||||
|
b.mul_assign(s);
|
||||||
|
|
||||||
|
let mut tmp2 = a;
|
||||||
|
tmp2.add_assign(&b);
|
||||||
|
|
||||||
|
// Affine multiplication
|
||||||
|
let mut tmp3 = a_affine.mul(s);
|
||||||
|
tmp3.add_assign(&b_affine.mul(s));
|
||||||
|
|
||||||
|
assert_eq!(tmp1, tmp2);
|
||||||
|
assert_eq!(tmp1, tmp3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_addition_tests<G: CurveProjective>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let a = G::random(&mut rng);
|
||||||
|
let b = G::random(&mut rng);
|
||||||
|
let c = G::random(&mut rng);
|
||||||
|
let a_affine = a.into_affine();
|
||||||
|
let b_affine = b.into_affine();
|
||||||
|
let c_affine = c.into_affine();
|
||||||
|
|
||||||
|
// a + a should equal the doubling
|
||||||
|
{
|
||||||
|
let mut aplusa = a;
|
||||||
|
aplusa.add_assign(&a);
|
||||||
|
|
||||||
|
let mut aplusamixed = a;
|
||||||
|
aplusamixed.add_assign_mixed(&a.into_affine());
|
||||||
|
|
||||||
|
let mut adouble = a;
|
||||||
|
adouble.double();
|
||||||
|
|
||||||
|
assert_eq!(aplusa, adouble);
|
||||||
|
assert_eq!(aplusa, aplusamixed);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tmp = vec![G::zero(); 6];
|
||||||
|
|
||||||
|
// (a + b) + c
|
||||||
|
tmp[0] = a;
|
||||||
|
tmp[0].add_assign(&b);
|
||||||
|
tmp[0].add_assign(&c);
|
||||||
|
|
||||||
|
// a + (b + c)
|
||||||
|
tmp[1] = b;
|
||||||
|
tmp[1].add_assign(&c);
|
||||||
|
tmp[1].add_assign(&a);
|
||||||
|
|
||||||
|
// (a + c) + b
|
||||||
|
tmp[2] = a;
|
||||||
|
tmp[2].add_assign(&c);
|
||||||
|
tmp[2].add_assign(&b);
|
||||||
|
|
||||||
|
// Mixed addition
|
||||||
|
|
||||||
|
// (a + b) + c
|
||||||
|
tmp[3] = a_affine.into_projective();
|
||||||
|
tmp[3].add_assign_mixed(&b_affine);
|
||||||
|
tmp[3].add_assign_mixed(&c_affine);
|
||||||
|
|
||||||
|
// a + (b + c)
|
||||||
|
tmp[4] = b_affine.into_projective();
|
||||||
|
tmp[4].add_assign_mixed(&c_affine);
|
||||||
|
tmp[4].add_assign_mixed(&a_affine);
|
||||||
|
|
||||||
|
// (a + c) + b
|
||||||
|
tmp[5] = a_affine.into_projective();
|
||||||
|
tmp[5].add_assign_mixed(&c_affine);
|
||||||
|
tmp[5].add_assign_mixed(&b_affine);
|
||||||
|
|
||||||
|
// Comparisons
|
||||||
|
for i in 0..6 {
|
||||||
|
for j in 0..6 {
|
||||||
|
assert_eq!(tmp[i], tmp[j]);
|
||||||
|
assert_eq!(tmp[i].into_affine(), tmp[j].into_affine());
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(tmp[i] != a);
|
||||||
|
assert!(tmp[i] != b);
|
||||||
|
assert!(tmp[i] != c);
|
||||||
|
|
||||||
|
assert!(a != tmp[i]);
|
||||||
|
assert!(b != tmp[i]);
|
||||||
|
assert!(c != tmp[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_transformation_tests<G: CurveProjective>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let g = G::random(&mut rng);
|
||||||
|
let g_affine = g.into_affine();
|
||||||
|
let g_projective = g_affine.into_projective();
|
||||||
|
assert_eq!(g, g_projective);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch normalization
|
||||||
|
for _ in 0..10 {
|
||||||
|
let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for i in &v {
|
||||||
|
assert!(!i.is_normalized());
|
||||||
|
}
|
||||||
|
|
||||||
|
use rand::distributions::{Distribution, Uniform};
|
||||||
|
let between = Uniform::new(0, 1000);
|
||||||
|
// Sprinkle in some normalized points
|
||||||
|
for _ in 0..5 {
|
||||||
|
v[between.sample(&mut rng)] = G::zero();
|
||||||
|
}
|
||||||
|
for _ in 0..5 {
|
||||||
|
let s = between.sample(&mut rng);
|
||||||
|
v[s] = v[s].into_affine().into_projective();
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_v = v
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.into_affine().into_projective())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
G::batch_normalization(&mut v);
|
||||||
|
|
||||||
|
for i in &v {
|
||||||
|
assert!(i.is_normalized());
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(v, expected_v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_encoding_tests<G: CurveAffine>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
G::zero().into_uncompressed().into_affine().unwrap(),
|
||||||
|
G::zero()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
G::zero().into_compressed().into_affine().unwrap(),
|
||||||
|
G::zero()
|
||||||
|
);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let mut r = G::Projective::random(&mut rng).into_affine();
|
||||||
|
|
||||||
|
let uncompressed = r.into_uncompressed();
|
||||||
|
let de_uncompressed = uncompressed.into_affine().unwrap();
|
||||||
|
assert_eq!(de_uncompressed, r);
|
||||||
|
|
||||||
|
let compressed = r.into_compressed();
|
||||||
|
let de_compressed = compressed.into_affine().unwrap();
|
||||||
|
assert_eq!(de_compressed, r);
|
||||||
|
|
||||||
|
r.negate();
|
||||||
|
|
||||||
|
let compressed = r.into_compressed();
|
||||||
|
let de_compressed = compressed.into_affine().unwrap();
|
||||||
|
assert_eq!(de_compressed, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
181
group/src/wnaf.rs
Normal file
181
group/src/wnaf.rs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
|
|
||||||
|
use super::CurveProjective;
|
||||||
|
|
||||||
|
/// Replaces the contents of `table` with a w-NAF window table for the given window size.
|
||||||
|
pub(crate) fn wnaf_table<G: CurveProjective>(table: &mut Vec<G>, mut base: G, window: usize) {
|
||||||
|
table.truncate(0);
|
||||||
|
table.reserve(1 << (window - 1));
|
||||||
|
|
||||||
|
let mut dbl = base;
|
||||||
|
dbl.double();
|
||||||
|
|
||||||
|
for _ in 0..(1 << (window - 1)) {
|
||||||
|
table.push(base);
|
||||||
|
base.add_assign(&dbl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces the contents of `wnaf` with the w-NAF representation of a scalar.
|
||||||
|
pub(crate) fn wnaf_form<S: PrimeFieldRepr>(wnaf: &mut Vec<i64>, mut c: S, window: usize) {
|
||||||
|
wnaf.truncate(0);
|
||||||
|
|
||||||
|
while !c.is_zero() {
|
||||||
|
let mut u;
|
||||||
|
if c.is_odd() {
|
||||||
|
u = (c.as_ref()[0] % (1 << (window + 1))) as i64;
|
||||||
|
|
||||||
|
if u > (1 << window) {
|
||||||
|
u -= 1 << (window + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if u > 0 {
|
||||||
|
c.sub_noborrow(&S::from(u as u64));
|
||||||
|
} else {
|
||||||
|
c.add_nocarry(&S::from((-u) as u64));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
u = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
wnaf.push(u);
|
||||||
|
|
||||||
|
c.div2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar.
|
||||||
|
///
|
||||||
|
/// This function must be provided a `table` and `wnaf` that were constructed with
|
||||||
|
/// the same window size; otherwise, it may panic or produce invalid results.
|
||||||
|
pub(crate) fn wnaf_exp<G: CurveProjective>(table: &[G], wnaf: &[i64]) -> G {
|
||||||
|
let mut result = G::zero();
|
||||||
|
|
||||||
|
let mut found_one = false;
|
||||||
|
|
||||||
|
for n in wnaf.iter().rev() {
|
||||||
|
if found_one {
|
||||||
|
result.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
if *n != 0 {
|
||||||
|
found_one = true;
|
||||||
|
|
||||||
|
if *n > 0 {
|
||||||
|
result.add_assign(&table[(n / 2) as usize]);
|
||||||
|
} else {
|
||||||
|
result.sub_assign(&table[((-n) / 2) as usize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A "w-ary non-adjacent form" exponentiation context.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Wnaf<W, B, S> {
|
||||||
|
base: B,
|
||||||
|
scalar: S,
|
||||||
|
window_size: W,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveProjective> Wnaf<(), Vec<G>, Vec<i64>> {
|
||||||
|
/// Construct a new wNAF context without allocating.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Wnaf {
|
||||||
|
base: vec![],
|
||||||
|
scalar: vec![],
|
||||||
|
window_size: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that
|
||||||
|
/// can perform exponentiations with `.scalar(..)`.
|
||||||
|
pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf<usize, &[G], &mut Vec<i64>> {
|
||||||
|
// Compute the appropriate window size based on the number of scalars.
|
||||||
|
let window_size = G::recommended_wnaf_for_num_scalars(num_scalars);
|
||||||
|
|
||||||
|
// Compute a wNAF table for the provided base and window size.
|
||||||
|
wnaf_table(&mut self.base, base, window_size);
|
||||||
|
|
||||||
|
// Return a Wnaf object that immutably borrows the computed base storage location,
|
||||||
|
// but mutably borrows the scalar storage location.
|
||||||
|
Wnaf {
|
||||||
|
base: &self.base[..],
|
||||||
|
scalar: &mut self.scalar,
|
||||||
|
window_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform
|
||||||
|
/// exponentiations with `.base(..)`.
|
||||||
|
pub fn scalar(
|
||||||
|
&mut self,
|
||||||
|
scalar: <<G as CurveProjective>::Scalar as PrimeField>::Repr,
|
||||||
|
) -> Wnaf<usize, &mut Vec<G>, &[i64]> {
|
||||||
|
// Compute the appropriate window size for the scalar.
|
||||||
|
let window_size = G::recommended_wnaf_for_scalar(scalar);
|
||||||
|
|
||||||
|
// Compute the wNAF form of the scalar.
|
||||||
|
wnaf_form(&mut self.scalar, scalar, window_size);
|
||||||
|
|
||||||
|
// Return a Wnaf object that mutably borrows the base storage location, but
|
||||||
|
// immutably borrows the computed wNAF form scalar location.
|
||||||
|
Wnaf {
|
||||||
|
base: &mut self.base,
|
||||||
|
scalar: &self.scalar[..],
|
||||||
|
window_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, G: CurveProjective> Wnaf<usize, &'a [G], &'a mut Vec<i64>> {
|
||||||
|
/// Constructs new space for the scalar representation while borrowing
|
||||||
|
/// the computed window table, for sending the window table across threads.
|
||||||
|
pub fn shared(&self) -> Wnaf<usize, &'a [G], Vec<i64>> {
|
||||||
|
Wnaf {
|
||||||
|
base: self.base,
|
||||||
|
scalar: vec![],
|
||||||
|
window_size: self.window_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, G: CurveProjective> Wnaf<usize, &'a mut Vec<G>, &'a [i64]> {
|
||||||
|
/// Constructs new space for the window table while borrowing
|
||||||
|
/// the computed scalar representation, for sending the scalar representation
|
||||||
|
/// across threads.
|
||||||
|
pub fn shared(&self) -> Wnaf<usize, Vec<G>, &'a [i64]> {
|
||||||
|
Wnaf {
|
||||||
|
base: vec![],
|
||||||
|
scalar: self.scalar,
|
||||||
|
window_size: self.window_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B, S: AsRef<[i64]>> Wnaf<usize, B, S> {
|
||||||
|
/// Performs exponentiation given a base.
|
||||||
|
pub fn base<G: CurveProjective>(&mut self, base: G) -> G
|
||||||
|
where
|
||||||
|
B: AsMut<Vec<G>>,
|
||||||
|
{
|
||||||
|
wnaf_table(self.base.as_mut(), base, self.window_size);
|
||||||
|
wnaf_exp(self.base.as_mut(), self.scalar.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B, S: AsMut<Vec<i64>>> Wnaf<usize, B, S> {
|
||||||
|
/// Performs exponentiation given a scalar.
|
||||||
|
pub fn scalar<G: CurveProjective>(
|
||||||
|
&mut self,
|
||||||
|
scalar: <<G as CurveProjective>::Scalar as PrimeField>::Repr,
|
||||||
|
) -> G
|
||||||
|
where
|
||||||
|
B: AsRef<[G]>,
|
||||||
|
{
|
||||||
|
wnaf_form(self.scalar.as_mut(), scalar, self.window_size);
|
||||||
|
wnaf_exp(self.base.as_ref(), self.scalar.as_mut())
|
||||||
|
}
|
||||||
|
}
|
||||||
36
librustzcash/Cargo.toml
Normal file
36
librustzcash/Cargo.toml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
[package]
|
||||||
|
name = "librustzcash"
|
||||||
|
description = "Rust FFI used by the zcashd binary. Not an official API."
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = [
|
||||||
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
|
"Jack Grigg <jack@z.cash>",
|
||||||
|
"Jay Graber <jay@z.cash>",
|
||||||
|
"Simon Liu <simon@z.cash>"
|
||||||
|
]
|
||||||
|
homepage = "https://github.com/zcash/librustzcash"
|
||||||
|
repository = "https://github.com/zcash/librustzcash"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "rustzcash"
|
||||||
|
path = "src/rustzcash.rs"
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bellman = { version = "0.2.0", path = "../bellman" }
|
||||||
|
blake2b_simd = "0.5"
|
||||||
|
blake2s_simd = "0.5"
|
||||||
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
|
libc = "0.2"
|
||||||
|
pairing = { version = "0.15.0", path = "../pairing" }
|
||||||
|
lazy_static = "1"
|
||||||
|
rand_core = "0.5.1"
|
||||||
|
zcash_history = { version = "0.0.1", path = "../zcash_history" }
|
||||||
|
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||||
|
zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" }
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "deprecated" }
|
||||||
25
librustzcash/README.md
Normal file
25
librustzcash/README.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# librustzcash
|
||||||
|
|
||||||
|
`librustzcash` is an FFI library crate that exposes the Zcash Rust components to
|
||||||
|
the `zcashd` full node.
|
||||||
|
|
||||||
|
The FFI API does not have any stability guarantees, and will change as required
|
||||||
|
by `zcashd`.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
### Contribution
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally
|
||||||
|
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||||
|
license, shall be dual licensed as above, without any additional terms or
|
||||||
|
conditions.
|
||||||
|
|
||||||
@@ -4,6 +4,12 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#ifdef WIN32
|
||||||
|
typedef uint16_t codeunit;
|
||||||
|
#else
|
||||||
|
typedef uint8_t codeunit;
|
||||||
|
#endif
|
||||||
|
|
||||||
void librustzcash_to_scalar(const unsigned char *input, unsigned char *result);
|
void librustzcash_to_scalar(const unsigned char *input, unsigned char *result);
|
||||||
|
|
||||||
void librustzcash_ask_to_ak(const unsigned char *ask, unsigned char *result);
|
void librustzcash_ask_to_ak(const unsigned char *ask, unsigned char *result);
|
||||||
@@ -19,11 +25,14 @@ extern "C" {
|
|||||||
/// Loads the zk-SNARK parameters into memory and saves
|
/// Loads the zk-SNARK parameters into memory and saves
|
||||||
/// paths as necessary. Only called once.
|
/// paths as necessary. Only called once.
|
||||||
void librustzcash_init_zksnark_params(
|
void librustzcash_init_zksnark_params(
|
||||||
const char* spend_path,
|
const codeunit* spend_path,
|
||||||
|
size_t spend_path_len,
|
||||||
const char* spend_hash,
|
const char* spend_hash,
|
||||||
const char* output_path,
|
const codeunit* output_path,
|
||||||
|
size_t output_path_len,
|
||||||
const char* output_hash,
|
const char* output_hash,
|
||||||
const char* sprout_path,
|
const codeunit* sprout_path,
|
||||||
|
size_t sprout_path_len,
|
||||||
const char* sprout_hash
|
const char* sprout_hash
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -103,8 +112,7 @@ extern "C" {
|
|||||||
bool librustzcash_sapling_output_proof(
|
bool librustzcash_sapling_output_proof(
|
||||||
void *ctx,
|
void *ctx,
|
||||||
const unsigned char *esk,
|
const unsigned char *esk,
|
||||||
const unsigned char *diversifier,
|
const unsigned char *payment_address,
|
||||||
const unsigned char *pk_d,
|
|
||||||
const unsigned char *rcm,
|
const unsigned char *rcm,
|
||||||
const uint64_t value,
|
const uint64_t value,
|
||||||
unsigned char *cv,
|
unsigned char *cv,
|
||||||
@@ -299,6 +307,33 @@ extern "C" {
|
|||||||
unsigned char *j_ret,
|
unsigned char *j_ret,
|
||||||
unsigned char *addr_ret
|
unsigned char *addr_ret
|
||||||
);
|
);
|
||||||
|
|
||||||
|
uint32_t librustzcash_mmr_append(
|
||||||
|
uint32_t cbranch,
|
||||||
|
uint32_t t_len,
|
||||||
|
const uint32_t *ni_ptr,
|
||||||
|
const unsigned char *n_ptr,
|
||||||
|
size_t p_len,
|
||||||
|
const unsigned char *nn_ptr,
|
||||||
|
unsigned char *rt_ret,
|
||||||
|
unsigned char *buf_ret
|
||||||
|
);
|
||||||
|
|
||||||
|
uint32_t librustzcash_mmr_delete(
|
||||||
|
uint32_t cbranch,
|
||||||
|
uint32_t t_len,
|
||||||
|
const uint32_t *ni_ptr,
|
||||||
|
const unsigned char *n_ptr,
|
||||||
|
size_t p_len,
|
||||||
|
size_t e_len,
|
||||||
|
unsigned char *rt_ret
|
||||||
|
);
|
||||||
|
|
||||||
|
uint32_t librustzcash_mmr_hash_node(
|
||||||
|
uint32_t cbranch,
|
||||||
|
const unsigned char *n_ptr,
|
||||||
|
unsigned char *h_ret
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // LIBRUSTZCASH_INCLUDE_H_
|
#endif // LIBRUSTZCASH_INCLUDE_H_
|
||||||
1342
librustzcash/src/rustzcash.rs
Normal file
1342
librustzcash/src/rustzcash.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
|||||||
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use pairing::{PrimeField, PrimeFieldRepr};
|
use rand_core::{OsRng, RngCore};
|
||||||
use rand::{OsRng, Rng};
|
use zcash_primitives::jubjub::{edwards, JubjubBls12};
|
||||||
use sapling_crypto::jubjub::{edwards, JubjubBls12};
|
use zcash_primitives::primitives::{Diversifier, ViewingKey};
|
||||||
use sapling_crypto::primitives::{Diversifier, ViewingKey};
|
|
||||||
|
|
||||||
use {
|
use crate::{
|
||||||
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
|
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
|
||||||
librustzcash_sapling_ka_derivepublic,
|
librustzcash_sapling_ka_derivepublic,
|
||||||
};
|
};
|
||||||
@@ -12,7 +12,7 @@ use {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_key_agreement() {
|
fn test_key_agreement() {
|
||||||
let params = JubjubBls12::new();
|
let params = JubjubBls12::new();
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng;
|
||||||
|
|
||||||
// Create random viewing key
|
// Create random viewing key
|
||||||
let vk = ViewingKey::<Bls12> {
|
let vk = ViewingKey::<Bls12> {
|
||||||
@@ -22,7 +22,9 @@ fn test_key_agreement() {
|
|||||||
|
|
||||||
// Create a random address with the viewing key
|
// Create a random address with the viewing key
|
||||||
let addr = loop {
|
let addr = loop {
|
||||||
match vk.into_payment_address(Diversifier(rng.gen()), ¶ms) {
|
let mut d = [0; 11];
|
||||||
|
rng.fill_bytes(&mut d);
|
||||||
|
match vk.to_payment_address(Diversifier(d), ¶ms) {
|
||||||
Some(a) => break a,
|
Some(a) => break a,
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
@@ -44,7 +46,7 @@ fn test_key_agreement() {
|
|||||||
|
|
||||||
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
||||||
let mut addr_pk_d = [0u8; 32];
|
let mut addr_pk_d = [0u8; 32];
|
||||||
addr.pk_d.write(&mut addr_pk_d[..]).unwrap();
|
addr.pk_d().write(&mut addr_pk_d[..]).unwrap();
|
||||||
|
|
||||||
assert!(librustzcash_sapling_ka_agree(
|
assert!(librustzcash_sapling_ka_agree(
|
||||||
&addr_pk_d,
|
&addr_pk_d,
|
||||||
@@ -56,7 +58,7 @@ fn test_key_agreement() {
|
|||||||
// using the diversifier and esk.
|
// using the diversifier and esk.
|
||||||
let mut epk = [0u8; 32];
|
let mut epk = [0u8; 32];
|
||||||
assert!(librustzcash_sapling_ka_derivepublic(
|
assert!(librustzcash_sapling_ka_derivepublic(
|
||||||
&addr.diversifier.0,
|
&addr.diversifier().0,
|
||||||
&esk,
|
&esk,
|
||||||
&mut epk
|
&mut epk
|
||||||
));
|
));
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
use pairing::{bls12_381::Bls12, PrimeField, PrimeFieldRepr};
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
use sapling_crypto::{
|
use pairing::bls12_381::Bls12;
|
||||||
|
use zcash_primitives::{
|
||||||
jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams},
|
jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams},
|
||||||
primitives::{Diversifier, ProofGenerationKey},
|
primitives::{Diversifier, ProofGenerationKey},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::JUBJUB;
|
use super::JUBJUB;
|
||||||
|
|
||||||
use {
|
use crate::{
|
||||||
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
|
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
|
||||||
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
|
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
|
||||||
};
|
};
|
||||||
@@ -27,6 +28,8 @@ fn key_components() {
|
|||||||
note_v: u64,
|
note_v: u64,
|
||||||
note_r: [u8; 32],
|
note_r: [u8; 32],
|
||||||
note_cm: [u8; 32],
|
note_cm: [u8; 32],
|
||||||
|
note_pos: u64,
|
||||||
|
note_nf: [u8; 32],
|
||||||
};
|
};
|
||||||
|
|
||||||
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
|
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
|
||||||
@@ -86,6 +89,12 @@ fn key_components() {
|
|||||||
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
|
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
|
||||||
0xdd, 0x07, 0x64, 0x39,
|
0xdd, 0x07, 0x64, 0x39,
|
||||||
],
|
],
|
||||||
|
note_pos: 0,
|
||||||
|
note_nf: [
|
||||||
|
0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86,
|
||||||
|
0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27,
|
||||||
|
0x47, 0xab, 0x40, 0x63,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -142,6 +151,12 @@ fn key_components() {
|
|||||||
0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72,
|
0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72,
|
||||||
0xca, 0xd4, 0x69, 0x50,
|
0xca, 0xd4, 0x69, 0x50,
|
||||||
],
|
],
|
||||||
|
note_pos: 763714296,
|
||||||
|
note_nf: [
|
||||||
|
0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2,
|
||||||
|
0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17,
|
||||||
|
0x4c, 0x2e, 0x9d, 0x93,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -198,6 +213,12 @@ fn key_components() {
|
|||||||
0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f,
|
0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f,
|
||||||
0x4e, 0x55, 0xf1, 0x51,
|
0x4e, 0x55, 0xf1, 0x51,
|
||||||
],
|
],
|
||||||
|
note_pos: 1527428592,
|
||||||
|
note_nf: [
|
||||||
|
0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f,
|
||||||
|
0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f,
|
||||||
|
0x59, 0xae, 0x1d, 0x14,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -254,6 +275,12 @@ fn key_components() {
|
|||||||
0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa,
|
0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa,
|
||||||
0x60, 0xd1, 0x9b, 0x6c,
|
0x60, 0xd1, 0x9b, 0x6c,
|
||||||
],
|
],
|
||||||
|
note_pos: 2291142888,
|
||||||
|
note_nf: [
|
||||||
|
0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56,
|
||||||
|
0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75,
|
||||||
|
0x8b, 0xec, 0x47, 0xa1,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -310,6 +337,12 @@ fn key_components() {
|
|||||||
0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b,
|
0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b,
|
||||||
0xc7, 0x1b, 0x7f, 0x36,
|
0xc7, 0x1b, 0x7f, 0x36,
|
||||||
],
|
],
|
||||||
|
note_pos: 3054857184,
|
||||||
|
note_nf: [
|
||||||
|
0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f,
|
||||||
|
0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03,
|
||||||
|
0x01, 0xbf, 0x3d, 0x13,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -366,6 +399,12 @@ fn key_components() {
|
|||||||
0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69,
|
0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69,
|
||||||
0xde, 0x1a, 0x5b, 0x4c,
|
0xde, 0x1a, 0x5b, 0x4c,
|
||||||
],
|
],
|
||||||
|
note_pos: 3818571480,
|
||||||
|
note_nf: [
|
||||||
|
0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2,
|
||||||
|
0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20,
|
||||||
|
0xb6, 0xc9, 0xa7, 0xe1,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -422,6 +461,12 @@ fn key_components() {
|
|||||||
0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b,
|
0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b,
|
||||||
0x78, 0x3a, 0x1e, 0x55,
|
0x78, 0x3a, 0x1e, 0x55,
|
||||||
],
|
],
|
||||||
|
note_pos: 287318480,
|
||||||
|
note_nf: [
|
||||||
|
0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae,
|
||||||
|
0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4,
|
||||||
|
0x07, 0xd5, 0x1e, 0x11,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -478,6 +523,12 @@ fn key_components() {
|
|||||||
0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36,
|
0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36,
|
||||||
0xf7, 0x8c, 0x2b, 0x23,
|
0xf7, 0x8c, 0x2b, 0x23,
|
||||||
],
|
],
|
||||||
|
note_pos: 1051032776,
|
||||||
|
note_nf: [
|
||||||
|
0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f,
|
||||||
|
0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc,
|
||||||
|
0x8b, 0xb6, 0x98, 0x90,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -534,6 +585,12 @@ fn key_components() {
|
|||||||
0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07,
|
0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07,
|
||||||
0x8f, 0xea, 0x4d, 0x04,
|
0x8f, 0xea, 0x4d, 0x04,
|
||||||
],
|
],
|
||||||
|
note_pos: 1814747072,
|
||||||
|
note_nf: [
|
||||||
|
0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20,
|
||||||
|
0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe,
|
||||||
|
0xb1, 0x7c, 0xd6, 0x20,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -590,6 +647,12 @@ fn key_components() {
|
|||||||
0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63,
|
0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63,
|
||||||
0xe4, 0x1d, 0xeb, 0x37,
|
0xe4, 0x1d, 0xeb, 0x37,
|
||||||
],
|
],
|
||||||
|
note_pos: 2578461368,
|
||||||
|
note_nf: [
|
||||||
|
0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe,
|
||||||
|
0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71,
|
||||||
|
0x0d, 0x93, 0xc9, 0xe9,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -615,7 +678,7 @@ fn key_components() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pgk = ProofGenerationKey { ak, nsk };
|
let pgk = ProofGenerationKey { ak, nsk };
|
||||||
let fvk = pgk.into_viewing_key(&JUBJUB);
|
let fvk = pgk.to_viewing_key(&JUBJUB);
|
||||||
{
|
{
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
fvk.nk.write(&mut vec).unwrap();
|
fvk.nk.write(&mut vec).unwrap();
|
||||||
@@ -641,10 +704,10 @@ fn key_components() {
|
|||||||
let diversifier = Diversifier(tv.default_d);
|
let diversifier = Diversifier(tv.default_d);
|
||||||
assert!(librustzcash_check_diversifier(&tv.default_d));
|
assert!(librustzcash_check_diversifier(&tv.default_d));
|
||||||
|
|
||||||
let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap();
|
let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap();
|
||||||
{
|
{
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
addr.pk_d.write(&mut vec).unwrap();
|
addr.pk_d().write(&mut vec).unwrap();
|
||||||
assert_eq!(&vec, &tv.default_pk_d);
|
assert_eq!(&vec, &tv.default_pk_d);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -662,5 +725,7 @@ fn key_components() {
|
|||||||
note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap();
|
note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap();
|
||||||
assert_eq!(&vec, &tv.note_cm);
|
assert_eq!(&vec, &tv.note_cm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_eq!(note.nf(&fvk, tv.note_pos, &JUBJUB), tv.note_nf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
225
librustzcash/src/tests/mmr.rs
Normal file
225
librustzcash/src/tests/mmr.rs
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
use zcash_history::{Entry, EntryLink, NodeData};
|
||||||
|
|
||||||
|
use crate::{librustzcash_mmr_append, librustzcash_mmr_delete};
|
||||||
|
|
||||||
|
const NODE_DATA_16L: &[u8] = include_bytes!("./res/tree16.dat");
|
||||||
|
const NODE_DATA_1023L: &[u8] = include_bytes!("./res/tree1023.dat");
|
||||||
|
|
||||||
|
struct TreeView {
|
||||||
|
peaks: Vec<(u32, Entry)>,
|
||||||
|
extra: Vec<(u32, Entry)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draft(into: &mut Vec<(u32, Entry)>, nodes: &[NodeData], peak_pos: usize, h: u32) {
|
||||||
|
let node_data = nodes[peak_pos - 1].clone();
|
||||||
|
let peak: Entry = match h {
|
||||||
|
0 => node_data.into(),
|
||||||
|
_ => Entry::new(
|
||||||
|
node_data,
|
||||||
|
EntryLink::Stored((peak_pos - (1 << h) - 1) as u32),
|
||||||
|
EntryLink::Stored((peak_pos - 2) as u32),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
into.push(((peak_pos - 1) as u32, peak));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_tree(nodes: &[NodeData]) -> TreeView {
|
||||||
|
assert!(!nodes.is_empty());
|
||||||
|
|
||||||
|
// integer log2 of (nodes.len()+1), -1
|
||||||
|
let mut h = (32 - ((nodes.len() + 1) as u32).leading_zeros() - 1) - 1;
|
||||||
|
let mut peak_pos = (1 << (h + 1)) - 1;
|
||||||
|
let mut peaks = Vec::new();
|
||||||
|
|
||||||
|
// used later
|
||||||
|
let mut last_peak_pos = 0;
|
||||||
|
let mut last_peak_h = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if peak_pos > nodes.len() {
|
||||||
|
// left child, -2^h
|
||||||
|
peak_pos -= 1 << h;
|
||||||
|
h -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if peak_pos <= nodes.len() {
|
||||||
|
draft(&mut peaks, nodes, peak_pos, h);
|
||||||
|
|
||||||
|
// save to be used in next loop
|
||||||
|
last_peak_pos = peak_pos;
|
||||||
|
last_peak_h = h;
|
||||||
|
|
||||||
|
// right sibling
|
||||||
|
peak_pos += (1 << (h + 1)) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if h == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for deletion, everything on the right slope of the last peak should be pre-loaded
|
||||||
|
let mut extra = Vec::new();
|
||||||
|
let mut h = last_peak_h;
|
||||||
|
let mut peak_pos = last_peak_pos;
|
||||||
|
|
||||||
|
while h > 0 {
|
||||||
|
let left_pos = peak_pos - (1 << h);
|
||||||
|
let right_pos = peak_pos - 1;
|
||||||
|
h -= 1;
|
||||||
|
|
||||||
|
// drafting left child
|
||||||
|
draft(&mut extra, nodes, left_pos, h);
|
||||||
|
|
||||||
|
// drafting right child
|
||||||
|
draft(&mut extra, nodes, right_pos, h);
|
||||||
|
|
||||||
|
// continuing on right slope
|
||||||
|
peak_pos = right_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeView { peaks, extra }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preload_tree_append(nodes: &[NodeData]) -> (Vec<u32>, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>) {
|
||||||
|
assert!(!nodes.is_empty());
|
||||||
|
|
||||||
|
let tree_view = prepare_tree(nodes);
|
||||||
|
|
||||||
|
let mut indices = Vec::new();
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
|
||||||
|
for (idx, entry) in tree_view.peaks.into_iter() {
|
||||||
|
let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE];
|
||||||
|
entry
|
||||||
|
.write(&mut &mut buf[..])
|
||||||
|
.expect("Cannot fail if enough buffer length");
|
||||||
|
indices.push(idx);
|
||||||
|
bytes.push(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
(indices, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// also returns number of peaks
|
||||||
|
fn preload_tree_delete(
|
||||||
|
nodes: &[NodeData],
|
||||||
|
) -> (Vec<u32>, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>, usize) {
|
||||||
|
assert!(!nodes.is_empty());
|
||||||
|
|
||||||
|
let tree_view = prepare_tree(nodes);
|
||||||
|
|
||||||
|
let mut indices = Vec::new();
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
|
||||||
|
let peak_count = tree_view.peaks.len();
|
||||||
|
|
||||||
|
for (idx, entry) in tree_view
|
||||||
|
.peaks
|
||||||
|
.into_iter()
|
||||||
|
.chain(tree_view.extra.into_iter())
|
||||||
|
{
|
||||||
|
let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE];
|
||||||
|
entry
|
||||||
|
.write(&mut &mut buf[..])
|
||||||
|
.expect("Cannot fail if enough buffer length");
|
||||||
|
indices.push(idx);
|
||||||
|
bytes.push(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
(indices, bytes, peak_count)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_nodes(bytes: &'static [u8]) -> Vec<NodeData> {
|
||||||
|
let mut res = Vec::new();
|
||||||
|
let mut cursor = std::io::Cursor::new(bytes);
|
||||||
|
while (cursor.position() as usize) < bytes.len() {
|
||||||
|
let node_data = zcash_history::NodeData::read(0, &mut cursor)
|
||||||
|
.expect("Statically checked to be correct");
|
||||||
|
res.push(node_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn append() {
|
||||||
|
let nodes = load_nodes(NODE_DATA_16L);
|
||||||
|
let (indices, peaks) = preload_tree_append(&nodes);
|
||||||
|
|
||||||
|
let mut rt_ret = [0u8; 32];
|
||||||
|
|
||||||
|
let mut buf_ret = Vec::<[u8; zcash_history::MAX_NODE_DATA_SIZE]>::with_capacity(32);
|
||||||
|
|
||||||
|
let mut new_node_data = [0u8; zcash_history::MAX_NODE_DATA_SIZE];
|
||||||
|
let new_node = NodeData {
|
||||||
|
consensus_branch_id: 0,
|
||||||
|
subtree_commitment: [0u8; 32],
|
||||||
|
start_time: 101,
|
||||||
|
end_time: 110,
|
||||||
|
start_target: 190,
|
||||||
|
end_target: 200,
|
||||||
|
start_sapling_root: [0u8; 32],
|
||||||
|
end_sapling_root: [0u8; 32],
|
||||||
|
subtree_total_work: Default::default(),
|
||||||
|
start_height: 10,
|
||||||
|
end_height: 10,
|
||||||
|
sapling_tx: 13,
|
||||||
|
};
|
||||||
|
new_node
|
||||||
|
.write(&mut &mut new_node_data[..])
|
||||||
|
.expect("Failed to write node data");
|
||||||
|
|
||||||
|
let result = librustzcash_mmr_append(
|
||||||
|
0,
|
||||||
|
nodes.len() as u32,
|
||||||
|
indices.as_ptr(),
|
||||||
|
peaks.as_ptr(),
|
||||||
|
peaks.len(),
|
||||||
|
&new_node_data,
|
||||||
|
&mut rt_ret,
|
||||||
|
buf_ret.as_mut_ptr(),
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
buf_ret.set_len(result as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(result, 2);
|
||||||
|
|
||||||
|
let new_node_1 =
|
||||||
|
NodeData::from_bytes(0, &buf_ret[0][..]).expect("Failed to reconstruct return node #1");
|
||||||
|
|
||||||
|
let new_node_2 =
|
||||||
|
NodeData::from_bytes(0, &buf_ret[1][..]).expect("Failed to reconstruct return node #2");
|
||||||
|
|
||||||
|
assert_eq!(new_node_1.start_height, 10);
|
||||||
|
assert_eq!(new_node_1.end_height, 10);
|
||||||
|
|
||||||
|
// this is combined new node (which is `new_node_1`) + the one which was there before (for block #9)
|
||||||
|
assert_eq!(new_node_2.start_height, 9);
|
||||||
|
assert_eq!(new_node_2.end_height, 10);
|
||||||
|
assert_eq!(new_node_2.sapling_tx, 27);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delete() {
|
||||||
|
let nodes = load_nodes(NODE_DATA_1023L);
|
||||||
|
let (indices, nodes, peak_count) = preload_tree_delete(&nodes);
|
||||||
|
|
||||||
|
let mut rt_ret = [0u8; 32];
|
||||||
|
|
||||||
|
let result = librustzcash_mmr_delete(
|
||||||
|
0,
|
||||||
|
nodes.len() as u32,
|
||||||
|
indices.as_ptr(),
|
||||||
|
nodes.as_ptr(),
|
||||||
|
peak_count,
|
||||||
|
indices.len() - peak_count,
|
||||||
|
&mut rt_ret,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Deleting from full tree of 9 height would result in cascade deleting of 10 nodes
|
||||||
|
assert_eq!(result, 10);
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
use sapling_crypto::jubjub::{FixedGenerators, JubjubParams};
|
use zcash_primitives::jubjub::{FixedGenerators, JubjubParams};
|
||||||
|
|
||||||
use super::JUBJUB;
|
use super::JUBJUB;
|
||||||
|
|
||||||
mod key_agreement;
|
mod key_agreement;
|
||||||
mod key_components;
|
mod key_components;
|
||||||
|
mod mmr;
|
||||||
mod notes;
|
mod notes;
|
||||||
mod signatures;
|
mod signatures;
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use librustzcash_sapling_compute_cm;
|
use crate::librustzcash_sapling_compute_cm;
|
||||||
use librustzcash_sapling_compute_nf;
|
use crate::librustzcash_sapling_compute_nf;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn notes() {
|
fn notes() {
|
||||||
BIN
librustzcash/src/tests/res/tree1023.dat
Normal file
BIN
librustzcash/src/tests/res/tree1023.dat
Normal file
Binary file not shown.
BIN
librustzcash/src/tests/res/tree16.dat
Normal file
BIN
librustzcash/src/tests/res/tree16.dat
Normal file
Binary file not shown.
@@ -1,8 +1,7 @@
|
|||||||
use pairing::{bls12_381::Bls12, PrimeField, PrimeFieldRepr};
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
use sapling_crypto::{
|
use pairing::bls12_381::Bls12;
|
||||||
jubjub::{FixedGenerators, JubjubEngine},
|
use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine};
|
||||||
redjubjub::{PrivateKey, PublicKey, Signature},
|
use zcash_primitives::redjubjub::{PrivateKey, PublicKey, Signature};
|
||||||
};
|
|
||||||
|
|
||||||
use super::JUBJUB;
|
use super::JUBJUB;
|
||||||
|
|
||||||
3
pairing/.gitignore
vendored
Normal file
3
pairing/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
target/
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
14
pairing/COPYRIGHT
Normal file
14
pairing/COPYRIGHT
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
Copyrights in the "pairing" library are retained by their contributors. No
|
||||||
|
copyright assignment is required to contribute to the "pairing" library.
|
||||||
|
|
||||||
|
The "pairing" library is licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally
|
||||||
|
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||||
|
license, shall be dual licensed as above, without any additional terms or
|
||||||
|
conditions.
|
||||||
34
pairing/Cargo.toml
Normal file
34
pairing/Cargo.toml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[package]
|
||||||
|
name = "pairing"
|
||||||
|
|
||||||
|
# Remember to change version string in README.md.
|
||||||
|
version = "0.15.1"
|
||||||
|
authors = [
|
||||||
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
|
"Jack Grigg <jack@z.cash>",
|
||||||
|
]
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT/Apache-2.0"
|
||||||
|
|
||||||
|
description = "Pairing-friendly elliptic curve library"
|
||||||
|
documentation = "https://docs.rs/pairing/"
|
||||||
|
homepage = "https://github.com/ebfull/pairing"
|
||||||
|
repository = "https://github.com/ebfull/pairing"
|
||||||
|
edition ="2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
byteorder = "1"
|
||||||
|
ff = { version = "^0.5.2", path = "../ff", features = ["derive"] }
|
||||||
|
group = { version = "0.2.0", path = "../group" }
|
||||||
|
rand_core = "0.5"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand_xorshift = "0.2"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
unstable-features = ["expose-arith"]
|
||||||
|
expose-arith = []
|
||||||
|
default = []
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
201
pairing/LICENSE-APACHE
Normal file
201
pairing/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
23
pairing/LICENSE-MIT
Normal file
23
pairing/LICENSE-MIT
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Permission is hereby granted, free of charge, to any
|
||||||
|
person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without
|
||||||
|
limitation the rights to use, copy, modify, merge,
|
||||||
|
publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software
|
||||||
|
is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice
|
||||||
|
shall be included in all copies or substantial portions
|
||||||
|
of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
39
pairing/README.md
Normal file
39
pairing/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# pairing [](https://crates.io/crates/pairing) #
|
||||||
|
|
||||||
|
`pairing` is a crate for using pairing-friendly elliptic curves.
|
||||||
|
|
||||||
|
Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html)
|
||||||
|
construction is implemented.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
`pairing` is being refactored into a generic library for working with
|
||||||
|
pairing-friendly curves. After the refactor, `pairing` will provide basic traits
|
||||||
|
for pairing-friendly elliptic curve constructions, while specific curves will be
|
||||||
|
in separate crates.
|
||||||
|
|
||||||
|
## [Documentation](https://docs.rs/pairing/)
|
||||||
|
|
||||||
|
Bring the `pairing` crate into your project just as you normally would.
|
||||||
|
|
||||||
|
## Security Warnings
|
||||||
|
|
||||||
|
This library does not make any guarantees about constant-time operations, memory
|
||||||
|
access patterns, or resistance to side-channel attacks.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
### Contribution
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally
|
||||||
|
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||||
|
license, shall be dual licensed as above, without any additional terms or
|
||||||
|
conditions.
|
||||||
149
pairing/benches/bls12_381/ec.rs
Normal file
149
pairing/benches/bls12_381/ec.rs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
mod g1 {
|
||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use ff::Field;
|
||||||
|
use group::CurveProjective;
|
||||||
|
use pairing::bls12_381::*;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(G1, Fr)> = (0..SAMPLES)
|
||||||
|
.map(|_| (G1::random(&mut rng), Fr::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.mul_assign(v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_g1_add_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(G1, G1)> = (0..SAMPLES)
|
||||||
|
.map(|_| (G1::random(&mut rng), G1::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.add_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(G1, G1Affine)> = (0..SAMPLES)
|
||||||
|
.map(|_| (G1::random(&mut rng), G1::random(&mut rng).into()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.add_assign_mixed(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod g2 {
|
||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use ff::Field;
|
||||||
|
use group::CurveProjective;
|
||||||
|
use pairing::bls12_381::*;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(G2, Fr)> = (0..SAMPLES)
|
||||||
|
.map(|_| (G2::random(&mut rng), Fr::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.mul_assign(v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_g2_add_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(G2, G2)> = (0..SAMPLES)
|
||||||
|
.map(|_| (G2::random(&mut rng), G2::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.add_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(G2, G2Affine)> = (0..SAMPLES)
|
||||||
|
.map(|_| (G2::random(&mut rng), G2::random(&mut rng).into()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.add_assign_mixed(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
317
pairing/benches/bls12_381/fq.rs
Normal file
317
pairing/benches/bls12_381/fq.rs
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||||
|
use pairing::bls12_381::*;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||||
|
.map(|_| {
|
||||||
|
let mut tmp1 = Fq::random(&mut rng).into_repr();
|
||||||
|
let mut tmp2 = Fq::random(&mut rng).into_repr();
|
||||||
|
// Shave a few bits off to avoid overflow.
|
||||||
|
for _ in 0..3 {
|
||||||
|
tmp1.div2();
|
||||||
|
tmp2.div2();
|
||||||
|
}
|
||||||
|
(tmp1, tmp2)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.add_nocarry(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||||
|
.map(|_| {
|
||||||
|
let tmp1 = Fq::random(&mut rng).into_repr();
|
||||||
|
let mut tmp2 = tmp1;
|
||||||
|
// Ensure tmp2 is smaller than tmp1.
|
||||||
|
for _ in 0..10 {
|
||||||
|
tmp2.div2();
|
||||||
|
}
|
||||||
|
(tmp1, tmp2)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.sub_noborrow(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fq::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let tmp = v[count].num_bits();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fq::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count];
|
||||||
|
tmp.mul2();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fq::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count];
|
||||||
|
tmp.div2();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.add_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.sub_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.mul_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_square(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count];
|
||||||
|
tmp.square();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
v[count].inverse()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_negate(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count];
|
||||||
|
tmp.negate();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq> = (0..SAMPLES)
|
||||||
|
.map(|_| {
|
||||||
|
let mut tmp = Fq::random(&mut rng);
|
||||||
|
tmp.square();
|
||||||
|
tmp
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
v[count].sqrt()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
v[count].into_repr()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fq::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
Fq::from_repr(v[count])
|
||||||
|
});
|
||||||
|
}
|
||||||
110
pairing/benches/bls12_381/fq12.rs
Normal file
110
pairing/benches/bls12_381/fq12.rs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use ff::Field;
|
||||||
|
use pairing::bls12_381::*;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.add_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.sub_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.mul_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count];
|
||||||
|
tmp.square();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq12_inverse(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let tmp = v[count].inverse();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
129
pairing/benches/bls12_381/fq2.rs
Normal file
129
pairing/benches/bls12_381/fq2.rs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use ff::{Field, SqrtField};
|
||||||
|
use pairing::bls12_381::*;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.add_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.sub_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.mul_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count];
|
||||||
|
tmp.square();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let tmp = v[count].inverse();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let tmp = v[count].sqrt();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
317
pairing/benches/bls12_381/fr.rs
Normal file
317
pairing/benches/bls12_381/fr.rs
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||||
|
use pairing::bls12_381::*;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||||
|
.map(|_| {
|
||||||
|
let mut tmp1 = Fr::random(&mut rng).into_repr();
|
||||||
|
let mut tmp2 = Fr::random(&mut rng).into_repr();
|
||||||
|
// Shave a few bits off to avoid overflow.
|
||||||
|
for _ in 0..3 {
|
||||||
|
tmp1.div2();
|
||||||
|
tmp2.div2();
|
||||||
|
}
|
||||||
|
(tmp1, tmp2)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.add_nocarry(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||||
|
.map(|_| {
|
||||||
|
let tmp1 = Fr::random(&mut rng).into_repr();
|
||||||
|
let mut tmp2 = tmp1;
|
||||||
|
// Ensure tmp2 is smaller than tmp1.
|
||||||
|
for _ in 0..10 {
|
||||||
|
tmp2.div2();
|
||||||
|
}
|
||||||
|
(tmp1, tmp2)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.sub_noborrow(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fr::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let tmp = v[count].num_bits();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fr::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count];
|
||||||
|
tmp.mul2();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fr::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count];
|
||||||
|
tmp.div2();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.add_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.sub_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||||
|
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count].0;
|
||||||
|
tmp.mul_assign(&v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_square(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count];
|
||||||
|
tmp.square();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
v[count].inverse()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_negate(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let mut tmp = v[count];
|
||||||
|
tmp.negate();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fr> = (0..SAMPLES)
|
||||||
|
.map(|_| {
|
||||||
|
let mut tmp = Fr::random(&mut rng);
|
||||||
|
tmp.square();
|
||||||
|
tmp
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
v[count].sqrt()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
v[count].into_repr()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fr::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
Fr::from_repr(v[count])
|
||||||
|
});
|
||||||
|
}
|
||||||
124
pairing/benches/bls12_381/mod.rs
Normal file
124
pairing/benches/bls12_381/mod.rs
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
mod ec;
|
||||||
|
mod fq;
|
||||||
|
mod fq12;
|
||||||
|
mod fq2;
|
||||||
|
mod fr;
|
||||||
|
|
||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use group::CurveProjective;
|
||||||
|
use pairing::bls12_381::*;
|
||||||
|
use pairing::{Engine, PairingCurveAffine};
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let tmp = G1Affine::from(v[count]).prepare();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::random(&mut rng)).collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let tmp = G2Affine::from(v[count]).prepare();
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES)
|
||||||
|
.map(|_| {
|
||||||
|
(
|
||||||
|
G1Affine::from(G1::random(&mut rng)).prepare(),
|
||||||
|
G2Affine::from(G2::random(&mut rng)).prepare(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<Fq12> = (0..SAMPLES)
|
||||||
|
.map(|_| {
|
||||||
|
(
|
||||||
|
G1Affine::from(G1::random(&mut rng)).prepare(),
|
||||||
|
G2Affine::from(G2::random(&mut rng)).prepare(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)]))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let tmp = Bls12::final_exponentiation(&v[count]);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_pairing_full(b: &mut ::test::Bencher) {
|
||||||
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let v: Vec<(G1, G2)> = (0..SAMPLES)
|
||||||
|
.map(|_| (G1::random(&mut rng), G2::random(&mut rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
let tmp = Bls12::pairing(v[count].0, v[count].1);
|
||||||
|
count = (count + 1) % SAMPLES;
|
||||||
|
tmp
|
||||||
|
});
|
||||||
|
}
|
||||||
10
pairing/benches/pairing_benches.rs
Normal file
10
pairing/benches/pairing_benches.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate ff;
|
||||||
|
extern crate group;
|
||||||
|
extern crate pairing;
|
||||||
|
extern crate rand_core;
|
||||||
|
extern crate rand_xorshift;
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
mod bls12_381;
|
||||||
71
pairing/src/bls12_381/README.md
Normal file
71
pairing/src/bls12_381/README.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# BLS12-381
|
||||||
|
|
||||||
|
This is an implementation of the BLS12-381 pairing-friendly elliptic curve construction.
|
||||||
|
|
||||||
|
## BLS12 Parameterization
|
||||||
|
|
||||||
|
BLS12 curves are parameterized by a value *x* such that the base field modulus *q* and subgroup *r* can be computed by:
|
||||||
|
|
||||||
|
* q = (x - 1)<sup>2</sup> ((x<sup>4</sup> - x<sup>2</sup> + 1) / 3) + x
|
||||||
|
* r = (x<sup>4</sup> - x<sup>2</sup> + 1)
|
||||||
|
|
||||||
|
Given primes *q* and *r* parameterized as above, we can easily construct an elliptic curve over the prime field F<sub>*q*</sub> which contains a subgroup of order *r* such that *r* | (*q*<sup>12</sup> - 1), giving it an embedding degree of 12. Instantiating its sextic twist over an extension field F<sub>q<sup>2</sup></sub> gives rise to an efficient bilinear pairing function between elements of the order *r* subgroups of either curves, into an order *r* multiplicative subgroup of F<sub>q<sup>12</sup></sub>.
|
||||||
|
|
||||||
|
In zk-SNARK schemes, we require F<sub>r</sub> with large 2<sup>n</sup> roots of unity for performing efficient fast-fourier transforms. As such, guaranteeing that large 2<sup>n</sup> | (r - 1), or equivalently that *x* has a large 2<sup>n</sup> factor, gives rise to BLS12 curves suitable for zk-SNARKs.
|
||||||
|
|
||||||
|
Due to recent research, it is estimated by many that *q* should be approximately 384 bits to target 128-bit security. Conveniently, *r* is approximately 256 bits when *q* is approximately 384 bits, making BLS12 curves ideal for 128-bit security. It also makes them ideal for many zk-SNARK applications, as the scalar field can be used for keying material such as embedded curve constructions.
|
||||||
|
|
||||||
|
Many curves match our descriptions, but we require some extra properties for efficiency purposes:
|
||||||
|
|
||||||
|
* *q* should be smaller than 2<sup>383</sup>, and *r* should be smaller than 2<sup>255</sup>, so that the most significant bit is unset when using 64-bit or 32-bit limbs. This allows for cheap reductions.
|
||||||
|
* F<sub>q<sup>12</sup></sub> is typically constructed using towers of extension fields. As a byproduct of [research](https://eprint.iacr.org/2011/465.pdf) for BLS curves of embedding degree 24, we can identify subfamilies of BLS12 curves (for our purposes, where x mod 72 = {16, 64}) that produce efficient extension field towers and twisting isomorphisms.
|
||||||
|
* We desire *x* of small Hamming weight, to increase the performance of the pairing function.
|
||||||
|
|
||||||
|
## BLS12-381 Instantiation
|
||||||
|
|
||||||
|
The BLS12-381 construction is instantiated by `x = -0xd201000000010000`, which produces the largest `q` and smallest Hamming weight of `x` that meets the above requirements. This produces:
|
||||||
|
|
||||||
|
* q = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` (381 bits)
|
||||||
|
* r = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` (255 bits)
|
||||||
|
|
||||||
|
Our extension field tower is constructed as follows:
|
||||||
|
|
||||||
|
1. F<sub>q<sup>2</sup></sub> is constructed as F<sub>q</sub>(u) / (u<sup>2</sup> - β) where β = -1.
|
||||||
|
2. F<sub>q<sup>6</sup></sub> is constructed as F<sub>q<sup>2</sup></sub>(v) / (v<sup>3</sup> - ξ) where ξ = u + 1
|
||||||
|
3. F<sub>q<sup>12</sup></sub> is constructed as F<sub>q<sup>6</sup></sub>(w) / (w<sup>2</sup> - γ) where γ = v
|
||||||
|
|
||||||
|
Now, we instantiate the elliptic curve E(F<sub>q</sub>) : y<sup>2</sup> = x<sup>3</sup> + 4, and the elliptic curve E'(F<sub>q<sup>2</sup></sub>) : y<sup>2</sup> = x<sup>3</sup> + 4(u + 1).
|
||||||
|
|
||||||
|
The group G<sub>1</sub> is the *r* order subgroup of E, which has cofactor (x - 1)<sup>2</sup> / 3. The group G<sub>2</sub> is the *r* order subgroup of E', which has cofactor (x<sup>8</sup> - 4x<sup>7</sup> + 5x<sup>6</sup> - 4x<sup>4</sup> + 6x<sup>3</sup> - 4x<sup>2</sup> - 4x + 13) / 9.
|
||||||
|
|
||||||
|
### Generators
|
||||||
|
|
||||||
|
The generators of G<sub>1</sub> and G<sub>2</sub> are computed by finding the lexicographically smallest valid `x`-coordinate, and its lexicographically smallest `y`-coordinate and scaling it by the cofactor such that the result is not the point at infinity.
|
||||||
|
|
||||||
|
#### G1
|
||||||
|
|
||||||
|
```
|
||||||
|
x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
|
||||||
|
y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
|
||||||
|
```
|
||||||
|
|
||||||
|
#### G2
|
||||||
|
|
||||||
|
```
|
||||||
|
x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160
|
||||||
|
y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
|
||||||
|
```
|
||||||
|
|
||||||
|
### Serialization
|
||||||
|
|
||||||
|
* Fq elements are encoded in big-endian form. They occupy 48 bytes in this form.
|
||||||
|
* Fq2 elements are encoded in big-endian form, meaning that the Fq element c0 + c1 * u is represented by the Fq element c1 followed by the Fq element c0. This means Fq2 elements occupy 96 bytes in this form.
|
||||||
|
* The group G1 uses Fq elements for coordinates. The group G2 uses Fq2 elements for coordinates.
|
||||||
|
* G1 and G2 elements can be encoded in uncompressed form (the x-coordinate followed by the y-coordinate) or in compressed form (just the x-coordinate). G1 elements occupy 96 bytes in uncompressed form, and 48 bytes in compressed form. G2 elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed form.
|
||||||
|
|
||||||
|
The most-significant three bits of a G1 or G2 encoding should be masked away before the coordinate(s) are interpreted. These bits are used to unambiguously represent the underlying element:
|
||||||
|
|
||||||
|
* The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form.
|
||||||
|
* The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero.
|
||||||
|
* The third-most significant bit is set if (and only if) this point is in compressed form _and_ it is not the point at infinity _and_ its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate.
|
||||||
|
|
||||||
2079
pairing/src/bls12_381/ec.rs
Normal file
2079
pairing/src/bls12_381/ec.rs
Normal file
File diff suppressed because it is too large
Load Diff
2285
pairing/src/bls12_381/fq.rs
Normal file
2285
pairing/src/bls12_381/fq.rs
Normal file
File diff suppressed because it is too large
Load Diff
192
pairing/src/bls12_381/fq12.rs
Normal file
192
pairing/src/bls12_381/fq12.rs
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
use super::fq::FROBENIUS_COEFF_FQ12_C1;
|
||||||
|
use super::fq2::Fq2;
|
||||||
|
use super::fq6::Fq6;
|
||||||
|
use ff::Field;
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
/// An element of Fq12, represented by c0 + c1 * w.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Fq12 {
|
||||||
|
pub c0: Fq6,
|
||||||
|
pub c1: Fq6,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for Fq12 {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
|
write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fq12 {
|
||||||
|
pub fn conjugate(&mut self) {
|
||||||
|
self.c1.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) {
|
||||||
|
let mut aa = self.c0;
|
||||||
|
aa.mul_by_01(c0, c1);
|
||||||
|
let mut bb = self.c1;
|
||||||
|
bb.mul_by_1(c4);
|
||||||
|
let mut o = *c1;
|
||||||
|
o.add_assign(c4);
|
||||||
|
self.c1.add_assign(&self.c0);
|
||||||
|
self.c1.mul_by_01(c0, &o);
|
||||||
|
self.c1.sub_assign(&aa);
|
||||||
|
self.c1.sub_assign(&bb);
|
||||||
|
self.c0 = bb;
|
||||||
|
self.c0.mul_by_nonresidue();
|
||||||
|
self.c0.add_assign(&aa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Field for Fq12 {
|
||||||
|
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||||
|
Fq12 {
|
||||||
|
c0: Fq6::random(rng),
|
||||||
|
c1: Fq6::random(rng),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zero() -> Self {
|
||||||
|
Fq12 {
|
||||||
|
c0: Fq6::zero(),
|
||||||
|
c1: Fq6::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
Fq12 {
|
||||||
|
c0: Fq6::one(),
|
||||||
|
c1: Fq6::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.c0.is_zero() && self.c1.is_zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn double(&mut self) {
|
||||||
|
self.c0.double();
|
||||||
|
self.c1.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate(&mut self) {
|
||||||
|
self.c0.negate();
|
||||||
|
self.c1.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_assign(&mut self, other: &Self) {
|
||||||
|
self.c0.add_assign(&other.c0);
|
||||||
|
self.c1.add_assign(&other.c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub_assign(&mut self, other: &Self) {
|
||||||
|
self.c0.sub_assign(&other.c0);
|
||||||
|
self.c1.sub_assign(&other.c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frobenius_map(&mut self, power: usize) {
|
||||||
|
self.c0.frobenius_map(power);
|
||||||
|
self.c1.frobenius_map(power);
|
||||||
|
|
||||||
|
self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
|
||||||
|
self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
|
||||||
|
self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(&mut self) {
|
||||||
|
let mut ab = self.c0;
|
||||||
|
ab.mul_assign(&self.c1);
|
||||||
|
let mut c0c1 = self.c0;
|
||||||
|
c0c1.add_assign(&self.c1);
|
||||||
|
let mut c0 = self.c1;
|
||||||
|
c0.mul_by_nonresidue();
|
||||||
|
c0.add_assign(&self.c0);
|
||||||
|
c0.mul_assign(&c0c1);
|
||||||
|
c0.sub_assign(&ab);
|
||||||
|
self.c1 = ab;
|
||||||
|
self.c1.add_assign(&ab);
|
||||||
|
ab.mul_by_nonresidue();
|
||||||
|
c0.sub_assign(&ab);
|
||||||
|
self.c0 = c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul_assign(&mut self, other: &Self) {
|
||||||
|
let mut aa = self.c0;
|
||||||
|
aa.mul_assign(&other.c0);
|
||||||
|
let mut bb = self.c1;
|
||||||
|
bb.mul_assign(&other.c1);
|
||||||
|
let mut o = other.c0;
|
||||||
|
o.add_assign(&other.c1);
|
||||||
|
self.c1.add_assign(&self.c0);
|
||||||
|
self.c1.mul_assign(&o);
|
||||||
|
self.c1.sub_assign(&aa);
|
||||||
|
self.c1.sub_assign(&bb);
|
||||||
|
self.c0 = bb;
|
||||||
|
self.c0.mul_by_nonresidue();
|
||||||
|
self.c0.add_assign(&aa);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inverse(&self) -> Option<Self> {
|
||||||
|
let mut c0s = self.c0;
|
||||||
|
c0s.square();
|
||||||
|
let mut c1s = self.c1;
|
||||||
|
c1s.square();
|
||||||
|
c1s.mul_by_nonresidue();
|
||||||
|
c0s.sub_assign(&c1s);
|
||||||
|
|
||||||
|
c0s.inverse().map(|t| {
|
||||||
|
let mut tmp = Fq12 { c0: t, c1: t };
|
||||||
|
tmp.c0.mul_assign(&self.c0);
|
||||||
|
tmp.c1.mul_assign(&self.c1);
|
||||||
|
tmp.c1.negate();
|
||||||
|
|
||||||
|
tmp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_core::SeedableRng;
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq12_mul_by_014() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let c0 = Fq2::random(&mut rng);
|
||||||
|
let c1 = Fq2::random(&mut rng);
|
||||||
|
let c5 = Fq2::random(&mut rng);
|
||||||
|
let mut a = Fq12::random(&mut rng);
|
||||||
|
let mut b = a;
|
||||||
|
|
||||||
|
a.mul_by_014(&c0, &c1, &c5);
|
||||||
|
b.mul_assign(&Fq12 {
|
||||||
|
c0: Fq6 {
|
||||||
|
c0,
|
||||||
|
c1,
|
||||||
|
c2: Fq2::zero(),
|
||||||
|
},
|
||||||
|
c1: Fq6 {
|
||||||
|
c0: Fq2::zero(),
|
||||||
|
c1: c5,
|
||||||
|
c2: Fq2::zero(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fq12_field_tests() {
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
crate::tests::field::random_field_tests::<Fq12>();
|
||||||
|
crate::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
|
||||||
|
}
|
||||||
964
pairing/src/bls12_381/fq2.rs
Normal file
964
pairing/src/bls12_381/fq2.rs
Normal file
@@ -0,0 +1,964 @@
|
|||||||
|
use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE};
|
||||||
|
use ff::{Field, SqrtField};
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
/// An element of Fq2, represented by c0 + c1 * u.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Fq2 {
|
||||||
|
pub c0: Fq,
|
||||||
|
pub c1: Fq,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for Fq2 {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
|
write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Fq2` elements are ordered lexicographically.
|
||||||
|
impl Ord for Fq2 {
|
||||||
|
#[inline(always)]
|
||||||
|
fn cmp(&self, other: &Fq2) -> Ordering {
|
||||||
|
match self.c1.cmp(&other.c1) {
|
||||||
|
Ordering::Greater => Ordering::Greater,
|
||||||
|
Ordering::Less => Ordering::Less,
|
||||||
|
Ordering::Equal => self.c0.cmp(&other.c0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Fq2 {
|
||||||
|
#[inline(always)]
|
||||||
|
fn partial_cmp(&self, other: &Fq2) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fq2 {
|
||||||
|
/// Multiply this element by the cubic and quadratic nonresidue 1 + u.
|
||||||
|
pub fn mul_by_nonresidue(&mut self) {
|
||||||
|
let t0 = self.c0;
|
||||||
|
self.c0.sub_assign(&self.c1);
|
||||||
|
self.c1.add_assign(&t0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Norm of Fq2 as extension field in i over Fq
|
||||||
|
pub fn norm(&self) -> Fq {
|
||||||
|
let mut t0 = self.c0;
|
||||||
|
let mut t1 = self.c1;
|
||||||
|
t0.square();
|
||||||
|
t1.square();
|
||||||
|
t1.add_assign(&t0);
|
||||||
|
|
||||||
|
t1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Field for Fq2 {
|
||||||
|
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::random(rng),
|
||||||
|
c1: Fq::random(rng),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zero() -> Self {
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::zero(),
|
||||||
|
c1: Fq::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::one(),
|
||||||
|
c1: Fq::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.c0.is_zero() && self.c1.is_zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(&mut self) {
|
||||||
|
let mut ab = self.c0;
|
||||||
|
ab.mul_assign(&self.c1);
|
||||||
|
let mut c0c1 = self.c0;
|
||||||
|
c0c1.add_assign(&self.c1);
|
||||||
|
let mut c0 = self.c1;
|
||||||
|
c0.negate();
|
||||||
|
c0.add_assign(&self.c0);
|
||||||
|
c0.mul_assign(&c0c1);
|
||||||
|
c0.sub_assign(&ab);
|
||||||
|
self.c1 = ab;
|
||||||
|
self.c1.add_assign(&ab);
|
||||||
|
c0.add_assign(&ab);
|
||||||
|
self.c0 = c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn double(&mut self) {
|
||||||
|
self.c0.double();
|
||||||
|
self.c1.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate(&mut self) {
|
||||||
|
self.c0.negate();
|
||||||
|
self.c1.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_assign(&mut self, other: &Self) {
|
||||||
|
self.c0.add_assign(&other.c0);
|
||||||
|
self.c1.add_assign(&other.c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub_assign(&mut self, other: &Self) {
|
||||||
|
self.c0.sub_assign(&other.c0);
|
||||||
|
self.c1.sub_assign(&other.c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul_assign(&mut self, other: &Self) {
|
||||||
|
let mut aa = self.c0;
|
||||||
|
aa.mul_assign(&other.c0);
|
||||||
|
let mut bb = self.c1;
|
||||||
|
bb.mul_assign(&other.c1);
|
||||||
|
let mut o = other.c0;
|
||||||
|
o.add_assign(&other.c1);
|
||||||
|
self.c1.add_assign(&self.c0);
|
||||||
|
self.c1.mul_assign(&o);
|
||||||
|
self.c1.sub_assign(&aa);
|
||||||
|
self.c1.sub_assign(&bb);
|
||||||
|
self.c0 = aa;
|
||||||
|
self.c0.sub_assign(&bb);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inverse(&self) -> Option<Self> {
|
||||||
|
let mut t1 = self.c1;
|
||||||
|
t1.square();
|
||||||
|
let mut t0 = self.c0;
|
||||||
|
t0.square();
|
||||||
|
t0.add_assign(&t1);
|
||||||
|
t0.inverse().map(|t| {
|
||||||
|
let mut tmp = Fq2 {
|
||||||
|
c0: self.c0,
|
||||||
|
c1: self.c1,
|
||||||
|
};
|
||||||
|
tmp.c0.mul_assign(&t);
|
||||||
|
tmp.c1.mul_assign(&t);
|
||||||
|
tmp.c1.negate();
|
||||||
|
|
||||||
|
tmp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frobenius_map(&mut self, power: usize) {
|
||||||
|
self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SqrtField for Fq2 {
|
||||||
|
fn legendre(&self) -> ::ff::LegendreSymbol {
|
||||||
|
self.norm().legendre()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqrt(&self) -> Option<Self> {
|
||||||
|
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf
|
||||||
|
|
||||||
|
if self.is_zero() {
|
||||||
|
Some(Self::zero())
|
||||||
|
} else {
|
||||||
|
// a1 = self^((q - 3) / 4)
|
||||||
|
let mut a1 = self.pow([
|
||||||
|
0xee7fbfffffffeaaa,
|
||||||
|
0x7aaffffac54ffff,
|
||||||
|
0xd9cc34a83dac3d89,
|
||||||
|
0xd91dd2e13ce144af,
|
||||||
|
0x92c6e9ed90d2eb35,
|
||||||
|
0x680447a8e5ff9a6,
|
||||||
|
]);
|
||||||
|
let mut alpha = a1;
|
||||||
|
alpha.square();
|
||||||
|
alpha.mul_assign(self);
|
||||||
|
let mut a0 = alpha;
|
||||||
|
a0.frobenius_map(1);
|
||||||
|
a0.mul_assign(&alpha);
|
||||||
|
|
||||||
|
let neg1 = Fq2 {
|
||||||
|
c0: NEGATIVE_ONE,
|
||||||
|
c1: Fq::zero(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if a0 == neg1 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
a1.mul_assign(self);
|
||||||
|
|
||||||
|
if alpha == neg1 {
|
||||||
|
a1.mul_assign(&Fq2 {
|
||||||
|
c0: Fq::zero(),
|
||||||
|
c1: Fq::one(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
alpha.add_assign(&Fq2::one());
|
||||||
|
// alpha = alpha^((q - 1) / 2)
|
||||||
|
alpha = alpha.pow([
|
||||||
|
0xdcff7fffffffd555,
|
||||||
|
0xf55ffff58a9ffff,
|
||||||
|
0xb39869507b587b12,
|
||||||
|
0xb23ba5c279c2895f,
|
||||||
|
0x258dd3db21a5d66b,
|
||||||
|
0xd0088f51cbff34d,
|
||||||
|
]);
|
||||||
|
a1.mul_assign(&alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(a1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_ordering() {
|
||||||
|
let mut a = Fq2 {
|
||||||
|
c0: Fq::zero(),
|
||||||
|
c1: Fq::zero(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut b = a.clone();
|
||||||
|
|
||||||
|
assert!(a.cmp(&b) == Ordering::Equal);
|
||||||
|
b.c0.add_assign(&Fq::one());
|
||||||
|
assert!(a.cmp(&b) == Ordering::Less);
|
||||||
|
a.c0.add_assign(&Fq::one());
|
||||||
|
assert!(a.cmp(&b) == Ordering::Equal);
|
||||||
|
b.c1.add_assign(&Fq::one());
|
||||||
|
assert!(a.cmp(&b) == Ordering::Less);
|
||||||
|
a.c0.add_assign(&Fq::one());
|
||||||
|
assert!(a.cmp(&b) == Ordering::Less);
|
||||||
|
a.c1.add_assign(&Fq::one());
|
||||||
|
assert!(a.cmp(&b) == Ordering::Greater);
|
||||||
|
b.c0.add_assign(&Fq::one());
|
||||||
|
assert!(a.cmp(&b) == Ordering::Equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_basics() {
|
||||||
|
assert_eq!(
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::zero(),
|
||||||
|
c1: Fq::zero(),
|
||||||
|
},
|
||||||
|
Fq2::zero()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::one(),
|
||||||
|
c1: Fq::zero(),
|
||||||
|
},
|
||||||
|
Fq2::one()
|
||||||
|
);
|
||||||
|
assert!(Fq2::zero().is_zero());
|
||||||
|
assert!(!Fq2::one().is_zero());
|
||||||
|
assert!(!Fq2 {
|
||||||
|
c0: Fq::zero(),
|
||||||
|
c1: Fq::one(),
|
||||||
|
}
|
||||||
|
.is_zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_squaring() {
|
||||||
|
use super::fq::FqRepr;
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
let mut a = Fq2 {
|
||||||
|
c0: Fq::one(),
|
||||||
|
c1: Fq::one(),
|
||||||
|
}; // u + 1
|
||||||
|
a.square();
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::zero(),
|
||||||
|
c1: Fq::from_repr(FqRepr::from(2)).unwrap(),
|
||||||
|
}
|
||||||
|
); // 2u
|
||||||
|
|
||||||
|
let mut a = Fq2 {
|
||||||
|
c0: Fq::zero(),
|
||||||
|
c1: Fq::one(),
|
||||||
|
}; // u
|
||||||
|
a.square();
|
||||||
|
assert_eq!(a, {
|
||||||
|
let mut neg1 = Fq::one();
|
||||||
|
neg1.negate();
|
||||||
|
Fq2 {
|
||||||
|
c0: neg1,
|
||||||
|
c1: Fq::zero(),
|
||||||
|
}
|
||||||
|
}); // -1
|
||||||
|
|
||||||
|
let mut a = Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x9c2c6309bbf8b598,
|
||||||
|
0x4eef5c946536f602,
|
||||||
|
0x90e34aab6fb6a6bd,
|
||||||
|
0xf7f295a94e58ae7c,
|
||||||
|
0x41b76dcc1c3fbe5e,
|
||||||
|
0x7080c5fa1d8e042,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x38f473b3c870a4ab,
|
||||||
|
0x6ad3291177c8c7e5,
|
||||||
|
0xdac5a4c911a4353e,
|
||||||
|
0xbfb99020604137a0,
|
||||||
|
0xfc58a7b7be815407,
|
||||||
|
0x10d1615e75250a21,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
a.square();
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0xf262c28c538bcf68,
|
||||||
|
0xb9f2a66eae1073ba,
|
||||||
|
0xdc46ab8fad67ae0,
|
||||||
|
0xcb674157618da176,
|
||||||
|
0x4cf17b5893c3d327,
|
||||||
|
0x7eac81369c43361
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0xc1579cf58e980cf8,
|
||||||
|
0xa23eb7e12dd54d98,
|
||||||
|
0xe75138bce4cec7aa,
|
||||||
|
0x38d0d7275a9689e1,
|
||||||
|
0x739c983042779a65,
|
||||||
|
0x1542a61c8a8db994
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_mul() {
|
||||||
|
use super::fq::FqRepr;
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
let mut a = Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x85c9f989e1461f03,
|
||||||
|
0xa2e33c333449a1d6,
|
||||||
|
0x41e461154a7354a3,
|
||||||
|
0x9ee53e7e84d7532e,
|
||||||
|
0x1c202d8ed97afb45,
|
||||||
|
0x51d3f9253e2516f,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0xa7348a8b511aedcf,
|
||||||
|
0x143c215d8176b319,
|
||||||
|
0x4cc48081c09b8903,
|
||||||
|
0x9533e4a9a5158be,
|
||||||
|
0x7a5e1ecb676d65f9,
|
||||||
|
0x180c3ee46656b008,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
a.mul_assign(&Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0xe21f9169805f537e,
|
||||||
|
0xfc87e62e179c285d,
|
||||||
|
0x27ece175be07a531,
|
||||||
|
0xcd460f9f0c23e430,
|
||||||
|
0x6c9110292bfa409,
|
||||||
|
0x2c93a72eb8af83e,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x4b1c3f936d8992d4,
|
||||||
|
0x1d2a72916dba4c8a,
|
||||||
|
0x8871c508658d1e5f,
|
||||||
|
0x57a06d3135a752ae,
|
||||||
|
0x634cd3c6c565096d,
|
||||||
|
0x19e17334d4e93558,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x95b5127e6360c7e4,
|
||||||
|
0xde29c31a19a6937e,
|
||||||
|
0xf61a96dacf5a39bc,
|
||||||
|
0x5511fe4d84ee5f78,
|
||||||
|
0x5310a202d92f9963,
|
||||||
|
0x1751afbe166e5399
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x84af0e1bd630117a,
|
||||||
|
0x6c63cd4da2c2aa7,
|
||||||
|
0x5ba6e5430e883d40,
|
||||||
|
0xc975106579c275ee,
|
||||||
|
0x33a9ac82ce4c5083,
|
||||||
|
0x1ef1a36c201589d
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_inverse() {
|
||||||
|
use super::fq::FqRepr;
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
assert!(Fq2::zero().inverse().is_none());
|
||||||
|
|
||||||
|
let a = Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x85c9f989e1461f03,
|
||||||
|
0xa2e33c333449a1d6,
|
||||||
|
0x41e461154a7354a3,
|
||||||
|
0x9ee53e7e84d7532e,
|
||||||
|
0x1c202d8ed97afb45,
|
||||||
|
0x51d3f9253e2516f,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0xa7348a8b511aedcf,
|
||||||
|
0x143c215d8176b319,
|
||||||
|
0x4cc48081c09b8903,
|
||||||
|
0x9533e4a9a5158be,
|
||||||
|
0x7a5e1ecb676d65f9,
|
||||||
|
0x180c3ee46656b008,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
let a = a.inverse().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x70300f9bcb9e594,
|
||||||
|
0xe5ecda5fdafddbb2,
|
||||||
|
0x64bef617d2915a8f,
|
||||||
|
0xdfba703293941c30,
|
||||||
|
0xa6c3d8f9586f2636,
|
||||||
|
0x1351ef01941b70c4
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x8c39fd76a8312cb4,
|
||||||
|
0x15d7b6b95defbff0,
|
||||||
|
0x947143f89faedee9,
|
||||||
|
0xcbf651a0f367afb2,
|
||||||
|
0xdf4e54f0d3ef15a6,
|
||||||
|
0x103bdf241afb0019
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_addition() {
|
||||||
|
use super::fq::FqRepr;
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
let mut a = Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x2d0078036923ffc7,
|
||||||
|
0x11e59ea221a3b6d2,
|
||||||
|
0x8b1a52e0a90f59ed,
|
||||||
|
0xb966ce3bc2108b13,
|
||||||
|
0xccc649c4b9532bf3,
|
||||||
|
0xf8d295b2ded9dc,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x977df6efcdaee0db,
|
||||||
|
0x946ae52d684fa7ed,
|
||||||
|
0xbe203411c66fb3a5,
|
||||||
|
0xb3f8afc0ee248cad,
|
||||||
|
0x4e464dea5bcfd41e,
|
||||||
|
0x12d1137b8a6a837,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
a.add_assign(&Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x619a02d78dc70ef2,
|
||||||
|
0xb93adfc9119e33e8,
|
||||||
|
0x4bf0b99a9f0dca12,
|
||||||
|
0x3b88899a42a6318f,
|
||||||
|
0x986a4a62fa82a49d,
|
||||||
|
0x13ce433fa26027f5,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x66323bf80b58b9b9,
|
||||||
|
0xa1379b6facf6e596,
|
||||||
|
0x402aef1fb797e32f,
|
||||||
|
0x2236f55246d0d44d,
|
||||||
|
0x4c8c1800eb104566,
|
||||||
|
0x11d6e20e986c2085,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x8e9a7adaf6eb0eb9,
|
||||||
|
0xcb207e6b3341eaba,
|
||||||
|
0xd70b0c7b481d23ff,
|
||||||
|
0xf4ef57d604b6bca2,
|
||||||
|
0x65309427b3d5d090,
|
||||||
|
0x14c715d5553f01d2
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0xfdb032e7d9079a94,
|
||||||
|
0x35a2809d15468d83,
|
||||||
|
0xfe4b23317e0796d5,
|
||||||
|
0xd62fa51334f560fa,
|
||||||
|
0x9ad265eb46e01984,
|
||||||
|
0x1303f3465112c8bc
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_subtraction() {
|
||||||
|
use super::fq::FqRepr;
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
let mut a = Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x2d0078036923ffc7,
|
||||||
|
0x11e59ea221a3b6d2,
|
||||||
|
0x8b1a52e0a90f59ed,
|
||||||
|
0xb966ce3bc2108b13,
|
||||||
|
0xccc649c4b9532bf3,
|
||||||
|
0xf8d295b2ded9dc,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x977df6efcdaee0db,
|
||||||
|
0x946ae52d684fa7ed,
|
||||||
|
0xbe203411c66fb3a5,
|
||||||
|
0xb3f8afc0ee248cad,
|
||||||
|
0x4e464dea5bcfd41e,
|
||||||
|
0x12d1137b8a6a837,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
a.sub_assign(&Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x619a02d78dc70ef2,
|
||||||
|
0xb93adfc9119e33e8,
|
||||||
|
0x4bf0b99a9f0dca12,
|
||||||
|
0x3b88899a42a6318f,
|
||||||
|
0x986a4a62fa82a49d,
|
||||||
|
0x13ce433fa26027f5,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x66323bf80b58b9b9,
|
||||||
|
0xa1379b6facf6e596,
|
||||||
|
0x402aef1fb797e32f,
|
||||||
|
0x2236f55246d0d44d,
|
||||||
|
0x4c8c1800eb104566,
|
||||||
|
0x11d6e20e986c2085,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x8565752bdb5c9b80,
|
||||||
|
0x7756bed7c15982e9,
|
||||||
|
0xa65a6be700b285fe,
|
||||||
|
0xe255902672ef6c43,
|
||||||
|
0x7f77a718021c342d,
|
||||||
|
0x72ba14049fe9881
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0xeb4abaf7c255d1cd,
|
||||||
|
0x11df49bc6cacc256,
|
||||||
|
0xe52617930588c69a,
|
||||||
|
0xf63905f39ad8cb1f,
|
||||||
|
0x4cd5dd9fb40b3b8f,
|
||||||
|
0x957411359ba6e4c
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_negation() {
|
||||||
|
use super::fq::FqRepr;
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
let mut a = Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x2d0078036923ffc7,
|
||||||
|
0x11e59ea221a3b6d2,
|
||||||
|
0x8b1a52e0a90f59ed,
|
||||||
|
0xb966ce3bc2108b13,
|
||||||
|
0xccc649c4b9532bf3,
|
||||||
|
0xf8d295b2ded9dc,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x977df6efcdaee0db,
|
||||||
|
0x946ae52d684fa7ed,
|
||||||
|
0xbe203411c66fb3a5,
|
||||||
|
0xb3f8afc0ee248cad,
|
||||||
|
0x4e464dea5bcfd41e,
|
||||||
|
0x12d1137b8a6a837,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
a.negate();
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x8cfe87fc96dbaae4,
|
||||||
|
0xcc6615c8fb0492d,
|
||||||
|
0xdc167fc04da19c37,
|
||||||
|
0xab107d49317487ab,
|
||||||
|
0x7e555df189f880e3,
|
||||||
|
0x19083f5486a10cbd
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x228109103250c9d0,
|
||||||
|
0x8a411ad149045812,
|
||||||
|
0xa9109e8f3041427e,
|
||||||
|
0xb07e9bc405608611,
|
||||||
|
0xfcd559cbe77bd8b8,
|
||||||
|
0x18d400b280d93e62
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_doubling() {
|
||||||
|
use super::fq::FqRepr;
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
let mut a = Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x2d0078036923ffc7,
|
||||||
|
0x11e59ea221a3b6d2,
|
||||||
|
0x8b1a52e0a90f59ed,
|
||||||
|
0xb966ce3bc2108b13,
|
||||||
|
0xccc649c4b9532bf3,
|
||||||
|
0xf8d295b2ded9dc,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x977df6efcdaee0db,
|
||||||
|
0x946ae52d684fa7ed,
|
||||||
|
0xbe203411c66fb3a5,
|
||||||
|
0xb3f8afc0ee248cad,
|
||||||
|
0x4e464dea5bcfd41e,
|
||||||
|
0x12d1137b8a6a837,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
a.double();
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x5a00f006d247ff8e,
|
||||||
|
0x23cb3d4443476da4,
|
||||||
|
0x1634a5c1521eb3da,
|
||||||
|
0x72cd9c7784211627,
|
||||||
|
0x998c938972a657e7,
|
||||||
|
0x1f1a52b65bdb3b9
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x2efbeddf9b5dc1b6,
|
||||||
|
0x28d5ca5ad09f4fdb,
|
||||||
|
0x7c4068238cdf674b,
|
||||||
|
0x67f15f81dc49195b,
|
||||||
|
0x9c8c9bd4b79fa83d,
|
||||||
|
0x25a226f714d506e
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_frobenius_map() {
|
||||||
|
use super::fq::FqRepr;
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
let mut a = Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x2d0078036923ffc7,
|
||||||
|
0x11e59ea221a3b6d2,
|
||||||
|
0x8b1a52e0a90f59ed,
|
||||||
|
0xb966ce3bc2108b13,
|
||||||
|
0xccc649c4b9532bf3,
|
||||||
|
0xf8d295b2ded9dc,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x977df6efcdaee0db,
|
||||||
|
0x946ae52d684fa7ed,
|
||||||
|
0xbe203411c66fb3a5,
|
||||||
|
0xb3f8afc0ee248cad,
|
||||||
|
0x4e464dea5bcfd41e,
|
||||||
|
0x12d1137b8a6a837,
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
a.frobenius_map(0);
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x2d0078036923ffc7,
|
||||||
|
0x11e59ea221a3b6d2,
|
||||||
|
0x8b1a52e0a90f59ed,
|
||||||
|
0xb966ce3bc2108b13,
|
||||||
|
0xccc649c4b9532bf3,
|
||||||
|
0xf8d295b2ded9dc
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x977df6efcdaee0db,
|
||||||
|
0x946ae52d684fa7ed,
|
||||||
|
0xbe203411c66fb3a5,
|
||||||
|
0xb3f8afc0ee248cad,
|
||||||
|
0x4e464dea5bcfd41e,
|
||||||
|
0x12d1137b8a6a837
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
a.frobenius_map(1);
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x2d0078036923ffc7,
|
||||||
|
0x11e59ea221a3b6d2,
|
||||||
|
0x8b1a52e0a90f59ed,
|
||||||
|
0xb966ce3bc2108b13,
|
||||||
|
0xccc649c4b9532bf3,
|
||||||
|
0xf8d295b2ded9dc
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x228109103250c9d0,
|
||||||
|
0x8a411ad149045812,
|
||||||
|
0xa9109e8f3041427e,
|
||||||
|
0xb07e9bc405608611,
|
||||||
|
0xfcd559cbe77bd8b8,
|
||||||
|
0x18d400b280d93e62
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
a.frobenius_map(1);
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x2d0078036923ffc7,
|
||||||
|
0x11e59ea221a3b6d2,
|
||||||
|
0x8b1a52e0a90f59ed,
|
||||||
|
0xb966ce3bc2108b13,
|
||||||
|
0xccc649c4b9532bf3,
|
||||||
|
0xf8d295b2ded9dc
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x977df6efcdaee0db,
|
||||||
|
0x946ae52d684fa7ed,
|
||||||
|
0xbe203411c66fb3a5,
|
||||||
|
0xb3f8afc0ee248cad,
|
||||||
|
0x4e464dea5bcfd41e,
|
||||||
|
0x12d1137b8a6a837
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
a.frobenius_map(2);
|
||||||
|
assert_eq!(
|
||||||
|
a,
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x2d0078036923ffc7,
|
||||||
|
0x11e59ea221a3b6d2,
|
||||||
|
0x8b1a52e0a90f59ed,
|
||||||
|
0xb966ce3bc2108b13,
|
||||||
|
0xccc649c4b9532bf3,
|
||||||
|
0xf8d295b2ded9dc
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0x977df6efcdaee0db,
|
||||||
|
0x946ae52d684fa7ed,
|
||||||
|
0xbe203411c66fb3a5,
|
||||||
|
0xb3f8afc0ee248cad,
|
||||||
|
0x4e464dea5bcfd41e,
|
||||||
|
0x12d1137b8a6a837
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_sqrt() {
|
||||||
|
use super::fq::FqRepr;
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x476b4c309720e227,
|
||||||
|
0x34c2d04faffdab6,
|
||||||
|
0xa57e6fc1bab51fd9,
|
||||||
|
0xdb4a116b5bf74aa1,
|
||||||
|
0x1e58b2159dfe10e2,
|
||||||
|
0x7ca7da1f13606ac
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0xfa8de88b7516d2c3,
|
||||||
|
0x371a75ed14f41629,
|
||||||
|
0x4cec2dca577a3eb6,
|
||||||
|
0x212611bca4e99121,
|
||||||
|
0x8ee5394d77afb3d,
|
||||||
|
0xec92336650e49d5
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
.sqrt()
|
||||||
|
.unwrap(),
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0x40b299b2704258c5,
|
||||||
|
0x6ef7de92e8c68b63,
|
||||||
|
0x6d2ddbe552203e82,
|
||||||
|
0x8d7f1f723d02c1d3,
|
||||||
|
0x881b3e01b611c070,
|
||||||
|
0x10f6963bbad2ebc5
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0xc099534fc209e752,
|
||||||
|
0x7670594665676447,
|
||||||
|
0x28a20faed211efe7,
|
||||||
|
0x6b852aeaf2afcb1b,
|
||||||
|
0xa4c93b08105d71a9,
|
||||||
|
0x8d7cfff94216330
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr([
|
||||||
|
0xb9f78429d1517a6b,
|
||||||
|
0x1eabfffeb153ffff,
|
||||||
|
0x6730d2a0f6b0f624,
|
||||||
|
0x64774b84f38512bf,
|
||||||
|
0x4b1ba7b6434bacd7,
|
||||||
|
0x1a0111ea397fe69a
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
c1: Fq::zero(),
|
||||||
|
}
|
||||||
|
.sqrt()
|
||||||
|
.unwrap(),
|
||||||
|
Fq2 {
|
||||||
|
c0: Fq::zero(),
|
||||||
|
c1: Fq::from_repr(FqRepr([
|
||||||
|
0xb9fefffffd4357a3,
|
||||||
|
0x1eabfffeb153ffff,
|
||||||
|
0x6730d2a0f6b0f624,
|
||||||
|
0x64774b84f38512bf,
|
||||||
|
0x4b1ba7b6434bacd7,
|
||||||
|
0x1a0111ea397fe69a
|
||||||
|
]))
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_legendre() {
|
||||||
|
use ff::LegendreSymbol::*;
|
||||||
|
|
||||||
|
assert_eq!(Zero, Fq2::zero().legendre());
|
||||||
|
// i^2 = -1
|
||||||
|
let mut m1 = Fq2::one();
|
||||||
|
m1.negate();
|
||||||
|
assert_eq!(QuadraticResidue, m1.legendre());
|
||||||
|
m1.mul_by_nonresidue();
|
||||||
|
assert_eq!(QuadraticNonResidue, m1.legendre());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_core::SeedableRng;
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq2_mul_nonresidue() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let nqr = Fq2 {
|
||||||
|
c0: Fq::one(),
|
||||||
|
c1: Fq::one(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let mut a = Fq2::random(&mut rng);
|
||||||
|
let mut b = a;
|
||||||
|
a.mul_by_nonresidue();
|
||||||
|
b.mul_assign(&nqr);
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fq2_field_tests() {
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
crate::tests::field::random_field_tests::<Fq2>();
|
||||||
|
crate::tests::field::random_sqrt_tests::<Fq2>();
|
||||||
|
crate::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
|
||||||
|
}
|
||||||
383
pairing/src/bls12_381/fq6.rs
Normal file
383
pairing/src/bls12_381/fq6.rs
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2};
|
||||||
|
use super::fq2::Fq2;
|
||||||
|
use ff::Field;
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2).
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Fq6 {
|
||||||
|
pub c0: Fq2,
|
||||||
|
pub c1: Fq2,
|
||||||
|
pub c2: Fq2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for Fq6 {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
|
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fq6 {
|
||||||
|
/// Multiply by quadratic nonresidue v.
|
||||||
|
pub fn mul_by_nonresidue(&mut self) {
|
||||||
|
use std::mem::swap;
|
||||||
|
swap(&mut self.c0, &mut self.c1);
|
||||||
|
swap(&mut self.c0, &mut self.c2);
|
||||||
|
|
||||||
|
self.c0.mul_by_nonresidue();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mul_by_1(&mut self, c1: &Fq2) {
|
||||||
|
let mut b_b = self.c1;
|
||||||
|
b_b.mul_assign(c1);
|
||||||
|
|
||||||
|
let mut t1 = *c1;
|
||||||
|
{
|
||||||
|
let mut tmp = self.c1;
|
||||||
|
tmp.add_assign(&self.c2);
|
||||||
|
|
||||||
|
t1.mul_assign(&tmp);
|
||||||
|
t1.sub_assign(&b_b);
|
||||||
|
t1.mul_by_nonresidue();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut t2 = *c1;
|
||||||
|
{
|
||||||
|
let mut tmp = self.c0;
|
||||||
|
tmp.add_assign(&self.c1);
|
||||||
|
|
||||||
|
t2.mul_assign(&tmp);
|
||||||
|
t2.sub_assign(&b_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c0 = t1;
|
||||||
|
self.c1 = t2;
|
||||||
|
self.c2 = b_b;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mul_by_01(&mut self, c0: &Fq2, c1: &Fq2) {
|
||||||
|
let mut a_a = self.c0;
|
||||||
|
let mut b_b = self.c1;
|
||||||
|
a_a.mul_assign(c0);
|
||||||
|
b_b.mul_assign(c1);
|
||||||
|
|
||||||
|
let mut t1 = *c1;
|
||||||
|
{
|
||||||
|
let mut tmp = self.c1;
|
||||||
|
tmp.add_assign(&self.c2);
|
||||||
|
|
||||||
|
t1.mul_assign(&tmp);
|
||||||
|
t1.sub_assign(&b_b);
|
||||||
|
t1.mul_by_nonresidue();
|
||||||
|
t1.add_assign(&a_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut t3 = *c0;
|
||||||
|
{
|
||||||
|
let mut tmp = self.c0;
|
||||||
|
tmp.add_assign(&self.c2);
|
||||||
|
|
||||||
|
t3.mul_assign(&tmp);
|
||||||
|
t3.sub_assign(&a_a);
|
||||||
|
t3.add_assign(&b_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut t2 = *c0;
|
||||||
|
t2.add_assign(c1);
|
||||||
|
{
|
||||||
|
let mut tmp = self.c0;
|
||||||
|
tmp.add_assign(&self.c1);
|
||||||
|
|
||||||
|
t2.mul_assign(&tmp);
|
||||||
|
t2.sub_assign(&a_a);
|
||||||
|
t2.sub_assign(&b_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c0 = t1;
|
||||||
|
self.c1 = t2;
|
||||||
|
self.c2 = t3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Field for Fq6 {
|
||||||
|
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||||
|
Fq6 {
|
||||||
|
c0: Fq2::random(rng),
|
||||||
|
c1: Fq2::random(rng),
|
||||||
|
c2: Fq2::random(rng),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zero() -> Self {
|
||||||
|
Fq6 {
|
||||||
|
c0: Fq2::zero(),
|
||||||
|
c1: Fq2::zero(),
|
||||||
|
c2: Fq2::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
Fq6 {
|
||||||
|
c0: Fq2::one(),
|
||||||
|
c1: Fq2::zero(),
|
||||||
|
c2: Fq2::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn double(&mut self) {
|
||||||
|
self.c0.double();
|
||||||
|
self.c1.double();
|
||||||
|
self.c2.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate(&mut self) {
|
||||||
|
self.c0.negate();
|
||||||
|
self.c1.negate();
|
||||||
|
self.c2.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_assign(&mut self, other: &Self) {
|
||||||
|
self.c0.add_assign(&other.c0);
|
||||||
|
self.c1.add_assign(&other.c1);
|
||||||
|
self.c2.add_assign(&other.c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub_assign(&mut self, other: &Self) {
|
||||||
|
self.c0.sub_assign(&other.c0);
|
||||||
|
self.c1.sub_assign(&other.c1);
|
||||||
|
self.c2.sub_assign(&other.c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frobenius_map(&mut self, power: usize) {
|
||||||
|
self.c0.frobenius_map(power);
|
||||||
|
self.c1.frobenius_map(power);
|
||||||
|
self.c2.frobenius_map(power);
|
||||||
|
|
||||||
|
self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]);
|
||||||
|
self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(&mut self) {
|
||||||
|
let mut s0 = self.c0;
|
||||||
|
s0.square();
|
||||||
|
let mut ab = self.c0;
|
||||||
|
ab.mul_assign(&self.c1);
|
||||||
|
let mut s1 = ab;
|
||||||
|
s1.double();
|
||||||
|
let mut s2 = self.c0;
|
||||||
|
s2.sub_assign(&self.c1);
|
||||||
|
s2.add_assign(&self.c2);
|
||||||
|
s2.square();
|
||||||
|
let mut bc = self.c1;
|
||||||
|
bc.mul_assign(&self.c2);
|
||||||
|
let mut s3 = bc;
|
||||||
|
s3.double();
|
||||||
|
let mut s4 = self.c2;
|
||||||
|
s4.square();
|
||||||
|
|
||||||
|
self.c0 = s3;
|
||||||
|
self.c0.mul_by_nonresidue();
|
||||||
|
self.c0.add_assign(&s0);
|
||||||
|
|
||||||
|
self.c1 = s4;
|
||||||
|
self.c1.mul_by_nonresidue();
|
||||||
|
self.c1.add_assign(&s1);
|
||||||
|
|
||||||
|
self.c2 = s1;
|
||||||
|
self.c2.add_assign(&s2);
|
||||||
|
self.c2.add_assign(&s3);
|
||||||
|
self.c2.sub_assign(&s0);
|
||||||
|
self.c2.sub_assign(&s4);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul_assign(&mut self, other: &Self) {
|
||||||
|
let mut a_a = self.c0;
|
||||||
|
let mut b_b = self.c1;
|
||||||
|
let mut c_c = self.c2;
|
||||||
|
a_a.mul_assign(&other.c0);
|
||||||
|
b_b.mul_assign(&other.c1);
|
||||||
|
c_c.mul_assign(&other.c2);
|
||||||
|
|
||||||
|
let mut t1 = other.c1;
|
||||||
|
t1.add_assign(&other.c2);
|
||||||
|
{
|
||||||
|
let mut tmp = self.c1;
|
||||||
|
tmp.add_assign(&self.c2);
|
||||||
|
|
||||||
|
t1.mul_assign(&tmp);
|
||||||
|
t1.sub_assign(&b_b);
|
||||||
|
t1.sub_assign(&c_c);
|
||||||
|
t1.mul_by_nonresidue();
|
||||||
|
t1.add_assign(&a_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut t3 = other.c0;
|
||||||
|
t3.add_assign(&other.c2);
|
||||||
|
{
|
||||||
|
let mut tmp = self.c0;
|
||||||
|
tmp.add_assign(&self.c2);
|
||||||
|
|
||||||
|
t3.mul_assign(&tmp);
|
||||||
|
t3.sub_assign(&a_a);
|
||||||
|
t3.add_assign(&b_b);
|
||||||
|
t3.sub_assign(&c_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut t2 = other.c0;
|
||||||
|
t2.add_assign(&other.c1);
|
||||||
|
{
|
||||||
|
let mut tmp = self.c0;
|
||||||
|
tmp.add_assign(&self.c1);
|
||||||
|
|
||||||
|
t2.mul_assign(&tmp);
|
||||||
|
t2.sub_assign(&a_a);
|
||||||
|
t2.sub_assign(&b_b);
|
||||||
|
c_c.mul_by_nonresidue();
|
||||||
|
t2.add_assign(&c_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c0 = t1;
|
||||||
|
self.c1 = t2;
|
||||||
|
self.c2 = t3;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inverse(&self) -> Option<Self> {
|
||||||
|
let mut c0 = self.c2;
|
||||||
|
c0.mul_by_nonresidue();
|
||||||
|
c0.mul_assign(&self.c1);
|
||||||
|
c0.negate();
|
||||||
|
{
|
||||||
|
let mut c0s = self.c0;
|
||||||
|
c0s.square();
|
||||||
|
c0.add_assign(&c0s);
|
||||||
|
}
|
||||||
|
let mut c1 = self.c2;
|
||||||
|
c1.square();
|
||||||
|
c1.mul_by_nonresidue();
|
||||||
|
{
|
||||||
|
let mut c01 = self.c0;
|
||||||
|
c01.mul_assign(&self.c1);
|
||||||
|
c1.sub_assign(&c01);
|
||||||
|
}
|
||||||
|
let mut c2 = self.c1;
|
||||||
|
c2.square();
|
||||||
|
{
|
||||||
|
let mut c02 = self.c0;
|
||||||
|
c02.mul_assign(&self.c2);
|
||||||
|
c2.sub_assign(&c02);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tmp1 = self.c2;
|
||||||
|
tmp1.mul_assign(&c1);
|
||||||
|
let mut tmp2 = self.c1;
|
||||||
|
tmp2.mul_assign(&c2);
|
||||||
|
tmp1.add_assign(&tmp2);
|
||||||
|
tmp1.mul_by_nonresidue();
|
||||||
|
tmp2 = self.c0;
|
||||||
|
tmp2.mul_assign(&c0);
|
||||||
|
tmp1.add_assign(&tmp2);
|
||||||
|
|
||||||
|
match tmp1.inverse() {
|
||||||
|
Some(t) => {
|
||||||
|
let mut tmp = Fq6 {
|
||||||
|
c0: t,
|
||||||
|
c1: t,
|
||||||
|
c2: t,
|
||||||
|
};
|
||||||
|
tmp.c0.mul_assign(&c0);
|
||||||
|
tmp.c1.mul_assign(&c1);
|
||||||
|
tmp.c2.mul_assign(&c2);
|
||||||
|
|
||||||
|
Some(tmp)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_core::SeedableRng;
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq6_mul_nonresidue() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let nqr = Fq6 {
|
||||||
|
c0: Fq2::zero(),
|
||||||
|
c1: Fq2::one(),
|
||||||
|
c2: Fq2::zero(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let mut a = Fq6::random(&mut rng);
|
||||||
|
let mut b = a;
|
||||||
|
a.mul_by_nonresidue();
|
||||||
|
b.mul_assign(&nqr);
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq6_mul_by_1() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let c1 = Fq2::random(&mut rng);
|
||||||
|
let mut a = Fq6::random(&mut rng);
|
||||||
|
let mut b = a;
|
||||||
|
|
||||||
|
a.mul_by_1(&c1);
|
||||||
|
b.mul_assign(&Fq6 {
|
||||||
|
c0: Fq2::zero(),
|
||||||
|
c1,
|
||||||
|
c2: Fq2::zero(),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fq6_mul_by_01() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let c0 = Fq2::random(&mut rng);
|
||||||
|
let c1 = Fq2::random(&mut rng);
|
||||||
|
let mut a = Fq6::random(&mut rng);
|
||||||
|
let mut b = a;
|
||||||
|
|
||||||
|
a.mul_by_01(&c0, &c1);
|
||||||
|
b.mul_assign(&Fq6 {
|
||||||
|
c0,
|
||||||
|
c1,
|
||||||
|
c2: Fq2::zero(),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fq6_field_tests() {
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
crate::tests::field::random_field_tests::<Fq6>();
|
||||||
|
crate::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
|
||||||
|
}
|
||||||
1027
pairing/src/bls12_381/fr.rs
Normal file
1027
pairing/src/bls12_381/fr.rs
Normal file
File diff suppressed because it is too large
Load Diff
373
pairing/src/bls12_381/mod.rs
Normal file
373
pairing/src/bls12_381/mod.rs
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
//! An implementation of the BLS12-381 pairing-friendly elliptic curve
|
||||||
|
//! construction.
|
||||||
|
|
||||||
|
mod ec;
|
||||||
|
mod fq;
|
||||||
|
mod fq12;
|
||||||
|
mod fq2;
|
||||||
|
mod fq6;
|
||||||
|
mod fr;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
pub use self::ec::{
|
||||||
|
G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2Affine, G2Compressed, G2Prepared,
|
||||||
|
G2Uncompressed, G1, G2,
|
||||||
|
};
|
||||||
|
pub use self::fq::{Fq, FqRepr};
|
||||||
|
pub use self::fq12::Fq12;
|
||||||
|
pub use self::fq2::Fq2;
|
||||||
|
pub use self::fq6::Fq6;
|
||||||
|
pub use self::fr::{Fr, FrRepr};
|
||||||
|
|
||||||
|
use super::{Engine, PairingCurveAffine};
|
||||||
|
|
||||||
|
use ff::{BitIterator, Field, ScalarEngine};
|
||||||
|
use group::CurveAffine;
|
||||||
|
|
||||||
|
// The BLS parameter x for BLS12-381 is -0xd201000000010000
|
||||||
|
const BLS_X: u64 = 0xd201000000010000;
|
||||||
|
const BLS_X_IS_NEGATIVE: bool = true;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Bls12;
|
||||||
|
|
||||||
|
impl ScalarEngine for Bls12 {
|
||||||
|
type Fr = Fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Engine for Bls12 {
|
||||||
|
type G1 = G1;
|
||||||
|
type G1Affine = G1Affine;
|
||||||
|
type G2 = G2;
|
||||||
|
type G2Affine = G2Affine;
|
||||||
|
type Fq = Fq;
|
||||||
|
type Fqe = Fq2;
|
||||||
|
type Fqk = Fq12;
|
||||||
|
|
||||||
|
fn miller_loop<'a, I>(i: I) -> Self::Fqk
|
||||||
|
where
|
||||||
|
I: IntoIterator<
|
||||||
|
Item = &'a (
|
||||||
|
&'a <Self::G1Affine as PairingCurveAffine>::Prepared,
|
||||||
|
&'a <Self::G2Affine as PairingCurveAffine>::Prepared,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
{
|
||||||
|
let mut pairs = vec![];
|
||||||
|
for &(p, q) in i {
|
||||||
|
if !p.is_zero() && !q.is_zero() {
|
||||||
|
pairs.push((p, q.coeffs.iter()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Twisting isomorphism from E to E'
|
||||||
|
fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) {
|
||||||
|
let mut c0 = coeffs.0;
|
||||||
|
let mut c1 = coeffs.1;
|
||||||
|
|
||||||
|
c0.c0.mul_assign(&p.y);
|
||||||
|
c0.c1.mul_assign(&p.y);
|
||||||
|
|
||||||
|
c1.c0.mul_assign(&p.x);
|
||||||
|
c1.c1.mul_assign(&p.x);
|
||||||
|
|
||||||
|
// Sparse multiplication in Fq12
|
||||||
|
f.mul_by_014(&coeffs.2, &c1, &c0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut f = Fq12::one();
|
||||||
|
|
||||||
|
let mut found_one = false;
|
||||||
|
for i in BitIterator::new(&[BLS_X >> 1]) {
|
||||||
|
if !found_one {
|
||||||
|
found_one = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for &mut (p, ref mut coeffs) in &mut pairs {
|
||||||
|
ell(&mut f, coeffs.next().unwrap(), &p.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if i {
|
||||||
|
for &mut (p, ref mut coeffs) in &mut pairs {
|
||||||
|
ell(&mut f, coeffs.next().unwrap(), &p.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.square();
|
||||||
|
}
|
||||||
|
|
||||||
|
for &mut (p, ref mut coeffs) in &mut pairs {
|
||||||
|
ell(&mut f, coeffs.next().unwrap(), &p.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if BLS_X_IS_NEGATIVE {
|
||||||
|
f.conjugate();
|
||||||
|
}
|
||||||
|
|
||||||
|
f
|
||||||
|
}
|
||||||
|
|
||||||
|
fn final_exponentiation(r: &Fq12) -> Option<Fq12> {
|
||||||
|
let mut f1 = *r;
|
||||||
|
f1.conjugate();
|
||||||
|
|
||||||
|
match r.inverse() {
|
||||||
|
Some(mut f2) => {
|
||||||
|
let mut r = f1;
|
||||||
|
r.mul_assign(&f2);
|
||||||
|
f2 = r;
|
||||||
|
r.frobenius_map(2);
|
||||||
|
r.mul_assign(&f2);
|
||||||
|
|
||||||
|
fn exp_by_x(f: &mut Fq12, x: u64) {
|
||||||
|
*f = f.pow(&[x]);
|
||||||
|
if BLS_X_IS_NEGATIVE {
|
||||||
|
f.conjugate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut x = BLS_X;
|
||||||
|
let mut y0 = r;
|
||||||
|
y0.square();
|
||||||
|
let mut y1 = y0;
|
||||||
|
exp_by_x(&mut y1, x);
|
||||||
|
x >>= 1;
|
||||||
|
let mut y2 = y1;
|
||||||
|
exp_by_x(&mut y2, x);
|
||||||
|
x <<= 1;
|
||||||
|
let mut y3 = r;
|
||||||
|
y3.conjugate();
|
||||||
|
y1.mul_assign(&y3);
|
||||||
|
y1.conjugate();
|
||||||
|
y1.mul_assign(&y2);
|
||||||
|
y2 = y1;
|
||||||
|
exp_by_x(&mut y2, x);
|
||||||
|
y3 = y2;
|
||||||
|
exp_by_x(&mut y3, x);
|
||||||
|
y1.conjugate();
|
||||||
|
y3.mul_assign(&y1);
|
||||||
|
y1.conjugate();
|
||||||
|
y1.frobenius_map(3);
|
||||||
|
y2.frobenius_map(2);
|
||||||
|
y1.mul_assign(&y2);
|
||||||
|
y2 = y3;
|
||||||
|
exp_by_x(&mut y2, x);
|
||||||
|
y2.mul_assign(&y0);
|
||||||
|
y2.mul_assign(&r);
|
||||||
|
y1.mul_assign(&y2);
|
||||||
|
y2 = y3;
|
||||||
|
y2.frobenius_map(1);
|
||||||
|
y1.mul_assign(&y2);
|
||||||
|
|
||||||
|
Some(y1)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl G2Prepared {
|
||||||
|
pub fn is_zero(&self) -> bool {
|
||||||
|
self.infinity
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_affine(q: G2Affine) -> Self {
|
||||||
|
if q.is_zero() {
|
||||||
|
return G2Prepared {
|
||||||
|
coeffs: vec![],
|
||||||
|
infinity: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn doubling_step(r: &mut G2) -> (Fq2, Fq2, Fq2) {
|
||||||
|
// Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf
|
||||||
|
let mut tmp0 = r.x;
|
||||||
|
tmp0.square();
|
||||||
|
|
||||||
|
let mut tmp1 = r.y;
|
||||||
|
tmp1.square();
|
||||||
|
|
||||||
|
let mut tmp2 = tmp1;
|
||||||
|
tmp2.square();
|
||||||
|
|
||||||
|
let mut tmp3 = tmp1;
|
||||||
|
tmp3.add_assign(&r.x);
|
||||||
|
tmp3.square();
|
||||||
|
tmp3.sub_assign(&tmp0);
|
||||||
|
tmp3.sub_assign(&tmp2);
|
||||||
|
tmp3.double();
|
||||||
|
|
||||||
|
let mut tmp4 = tmp0;
|
||||||
|
tmp4.double();
|
||||||
|
tmp4.add_assign(&tmp0);
|
||||||
|
|
||||||
|
let mut tmp6 = r.x;
|
||||||
|
tmp6.add_assign(&tmp4);
|
||||||
|
|
||||||
|
let mut tmp5 = tmp4;
|
||||||
|
tmp5.square();
|
||||||
|
|
||||||
|
let mut zsquared = r.z;
|
||||||
|
zsquared.square();
|
||||||
|
|
||||||
|
r.x = tmp5;
|
||||||
|
r.x.sub_assign(&tmp3);
|
||||||
|
r.x.sub_assign(&tmp3);
|
||||||
|
|
||||||
|
r.z.add_assign(&r.y);
|
||||||
|
r.z.square();
|
||||||
|
r.z.sub_assign(&tmp1);
|
||||||
|
r.z.sub_assign(&zsquared);
|
||||||
|
|
||||||
|
r.y = tmp3;
|
||||||
|
r.y.sub_assign(&r.x);
|
||||||
|
r.y.mul_assign(&tmp4);
|
||||||
|
|
||||||
|
tmp2.double();
|
||||||
|
tmp2.double();
|
||||||
|
tmp2.double();
|
||||||
|
|
||||||
|
r.y.sub_assign(&tmp2);
|
||||||
|
|
||||||
|
tmp3 = tmp4;
|
||||||
|
tmp3.mul_assign(&zsquared);
|
||||||
|
tmp3.double();
|
||||||
|
tmp3.negate();
|
||||||
|
|
||||||
|
tmp6.square();
|
||||||
|
tmp6.sub_assign(&tmp0);
|
||||||
|
tmp6.sub_assign(&tmp5);
|
||||||
|
|
||||||
|
tmp1.double();
|
||||||
|
tmp1.double();
|
||||||
|
|
||||||
|
tmp6.sub_assign(&tmp1);
|
||||||
|
|
||||||
|
tmp0 = r.z;
|
||||||
|
tmp0.mul_assign(&zsquared);
|
||||||
|
tmp0.double();
|
||||||
|
|
||||||
|
(tmp0, tmp3, tmp6)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addition_step(r: &mut G2, q: &G2Affine) -> (Fq2, Fq2, Fq2) {
|
||||||
|
// Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf
|
||||||
|
let mut zsquared = r.z;
|
||||||
|
zsquared.square();
|
||||||
|
|
||||||
|
let mut ysquared = q.y;
|
||||||
|
ysquared.square();
|
||||||
|
|
||||||
|
let mut t0 = zsquared;
|
||||||
|
t0.mul_assign(&q.x);
|
||||||
|
|
||||||
|
let mut t1 = q.y;
|
||||||
|
t1.add_assign(&r.z);
|
||||||
|
t1.square();
|
||||||
|
t1.sub_assign(&ysquared);
|
||||||
|
t1.sub_assign(&zsquared);
|
||||||
|
t1.mul_assign(&zsquared);
|
||||||
|
|
||||||
|
let mut t2 = t0;
|
||||||
|
t2.sub_assign(&r.x);
|
||||||
|
|
||||||
|
let mut t3 = t2;
|
||||||
|
t3.square();
|
||||||
|
|
||||||
|
let mut t4 = t3;
|
||||||
|
t4.double();
|
||||||
|
t4.double();
|
||||||
|
|
||||||
|
let mut t5 = t4;
|
||||||
|
t5.mul_assign(&t2);
|
||||||
|
|
||||||
|
let mut t6 = t1;
|
||||||
|
t6.sub_assign(&r.y);
|
||||||
|
t6.sub_assign(&r.y);
|
||||||
|
|
||||||
|
let mut t9 = t6;
|
||||||
|
t9.mul_assign(&q.x);
|
||||||
|
|
||||||
|
let mut t7 = t4;
|
||||||
|
t7.mul_assign(&r.x);
|
||||||
|
|
||||||
|
r.x = t6;
|
||||||
|
r.x.square();
|
||||||
|
r.x.sub_assign(&t5);
|
||||||
|
r.x.sub_assign(&t7);
|
||||||
|
r.x.sub_assign(&t7);
|
||||||
|
|
||||||
|
r.z.add_assign(&t2);
|
||||||
|
r.z.square();
|
||||||
|
r.z.sub_assign(&zsquared);
|
||||||
|
r.z.sub_assign(&t3);
|
||||||
|
|
||||||
|
let mut t10 = q.y;
|
||||||
|
t10.add_assign(&r.z);
|
||||||
|
|
||||||
|
let mut t8 = t7;
|
||||||
|
t8.sub_assign(&r.x);
|
||||||
|
t8.mul_assign(&t6);
|
||||||
|
|
||||||
|
t0 = r.y;
|
||||||
|
t0.mul_assign(&t5);
|
||||||
|
t0.double();
|
||||||
|
|
||||||
|
r.y = t8;
|
||||||
|
r.y.sub_assign(&t0);
|
||||||
|
|
||||||
|
t10.square();
|
||||||
|
t10.sub_assign(&ysquared);
|
||||||
|
|
||||||
|
let mut ztsquared = r.z;
|
||||||
|
ztsquared.square();
|
||||||
|
|
||||||
|
t10.sub_assign(&ztsquared);
|
||||||
|
|
||||||
|
t9.double();
|
||||||
|
t9.sub_assign(&t10);
|
||||||
|
|
||||||
|
t10 = r.z;
|
||||||
|
t10.double();
|
||||||
|
|
||||||
|
t6.negate();
|
||||||
|
|
||||||
|
t1 = t6;
|
||||||
|
t1.double();
|
||||||
|
|
||||||
|
(t10, t1, t9)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut coeffs = vec![];
|
||||||
|
let mut r: G2 = q.into();
|
||||||
|
|
||||||
|
let mut found_one = false;
|
||||||
|
for i in BitIterator::new([BLS_X >> 1]) {
|
||||||
|
if !found_one {
|
||||||
|
found_one = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
coeffs.push(doubling_step(&mut r));
|
||||||
|
|
||||||
|
if i {
|
||||||
|
coeffs.push(addition_step(&mut r, &q));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coeffs.push(doubling_step(&mut r));
|
||||||
|
|
||||||
|
G2Prepared {
|
||||||
|
coeffs,
|
||||||
|
infinity: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bls12_engine_tests() {
|
||||||
|
crate::tests::engine::engine_tests::<Bls12>();
|
||||||
|
}
|
||||||
BIN
pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat
Normal file
BIN
pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat
Normal file
Binary file not shown.
Binary file not shown.
BIN
pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat
Normal file
BIN
pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat
Normal file
Binary file not shown.
Binary file not shown.
614
pairing/src/bls12_381/tests/mod.rs
Normal file
614
pairing/src/bls12_381/tests/mod.rs
Normal file
@@ -0,0 +1,614 @@
|
|||||||
|
use ff::PrimeFieldRepr;
|
||||||
|
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pairing_result_against_relic() {
|
||||||
|
/*
|
||||||
|
Sent to me from Diego Aranha (author of RELIC library):
|
||||||
|
|
||||||
|
1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6
|
||||||
|
089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F
|
||||||
|
1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87
|
||||||
|
193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F
|
||||||
|
01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5
|
||||||
|
018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6
|
||||||
|
19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D
|
||||||
|
06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A
|
||||||
|
11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57
|
||||||
|
03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2
|
||||||
|
04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF
|
||||||
|
0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631
|
||||||
|
*/
|
||||||
|
|
||||||
|
assert_eq!(Bls12::pairing(G1::one(), G2::one()), Fq12 {
|
||||||
|
c0: Fq6 {
|
||||||
|
c0: Fq2 {
|
||||||
|
c0: Fq::from_str("2819105605953691245277803056322684086884703000473961065716485506033588504203831029066448642358042597501014294104502").unwrap(),
|
||||||
|
c1: Fq::from_str("1323968232986996742571315206151405965104242542339680722164220900812303524334628370163366153839984196298685227734799").unwrap()
|
||||||
|
},
|
||||||
|
c1: Fq2 {
|
||||||
|
c0: Fq::from_str("2987335049721312504428602988447616328830341722376962214011674875969052835043875658579425548512925634040144704192135").unwrap(),
|
||||||
|
c1: Fq::from_str("3879723582452552452538684314479081967502111497413076598816163759028842927668327542875108457755966417881797966271311").unwrap()
|
||||||
|
},
|
||||||
|
c2: Fq2 {
|
||||||
|
c0: Fq::from_str("261508182517997003171385743374653339186059518494239543139839025878870012614975302676296704930880982238308326681253").unwrap(),
|
||||||
|
c1: Fq::from_str("231488992246460459663813598342448669854473942105054381511346786719005883340876032043606739070883099647773793170614").unwrap()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
c1: Fq6 {
|
||||||
|
c0: Fq2 {
|
||||||
|
c0: Fq::from_str("3993582095516422658773669068931361134188738159766715576187490305611759126554796569868053818105850661142222948198557").unwrap(),
|
||||||
|
c1: Fq::from_str("1074773511698422344502264006159859710502164045911412750831641680783012525555872467108249271286757399121183508900634").unwrap()
|
||||||
|
},
|
||||||
|
c1: Fq2 {
|
||||||
|
c0: Fq::from_str("2727588299083545686739024317998512740561167011046940249988557419323068809019137624943703910267790601287073339193943").unwrap(),
|
||||||
|
c1: Fq::from_str("493643299814437640914745677854369670041080344349607504656543355799077485536288866009245028091988146107059514546594").unwrap()
|
||||||
|
},
|
||||||
|
c2: Fq2 {
|
||||||
|
c0: Fq::from_str("734401332196641441839439105942623141234148957972407782257355060229193854324927417865401895596108124443575283868655").unwrap(),
|
||||||
|
c1: Fq::from_str("2348330098288556420918672502923664952620152483128593484301759394583320358354186482723629999370241674973832318248497").unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_vectors<G: CurveProjective, E: EncodedPoint<Affine = G::Affine>>(expected: &[u8]) {
|
||||||
|
let mut e = G::zero();
|
||||||
|
|
||||||
|
let mut v = vec![];
|
||||||
|
{
|
||||||
|
let mut expected = expected;
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let e_affine = e.into_affine();
|
||||||
|
let encoded = E::from_affine(e_affine);
|
||||||
|
v.extend_from_slice(encoded.as_ref());
|
||||||
|
|
||||||
|
let mut decoded = E::empty();
|
||||||
|
decoded.as_mut().copy_from_slice(&expected[0..E::size()]);
|
||||||
|
expected = &expected[E::size()..];
|
||||||
|
let decoded = decoded.into_affine().unwrap();
|
||||||
|
assert_eq!(e_affine, decoded);
|
||||||
|
|
||||||
|
e.add_assign(&G::one());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(&v[..], expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_g1_uncompressed_valid_vectors() {
|
||||||
|
test_vectors::<G1, G1Uncompressed>(include_bytes!("g1_uncompressed_valid_test_vectors.dat"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_g1_compressed_valid_vectors() {
|
||||||
|
test_vectors::<G1, G1Compressed>(include_bytes!("g1_compressed_valid_test_vectors.dat"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_g2_uncompressed_valid_vectors() {
|
||||||
|
test_vectors::<G2, G2Uncompressed>(include_bytes!("g2_uncompressed_valid_test_vectors.dat"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_g2_compressed_valid_vectors() {
|
||||||
|
test_vectors::<G2, G2Compressed>(include_bytes!("g2_compressed_valid_test_vectors.dat"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_g1_uncompressed_invalid_vectors() {
|
||||||
|
{
|
||||||
|
let z = G1Affine::zero().into_uncompressed();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[0] |= 0b1000_0000;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because we expected an uncompressed point");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[0] |= 0b0010_0000;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..G1Uncompressed::size() {
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[i] |= 0b0000_0001;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let o = G1Affine::one().into_uncompressed();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
o.as_mut()[0] |= 0b1000_0000;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because we expected an uncompressed point");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let m = Fq::char();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||||
|
assert_eq!(coordinate, "x coordinate");
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||||
|
assert_eq!(coordinate, "y coordinate");
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let m = Fq::zero().into_repr();
|
||||||
|
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because it isn't on the curve")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
let mut x = Fq::one();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut x3b = x;
|
||||||
|
x3b.square();
|
||||||
|
x3b.mul_assign(&x);
|
||||||
|
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||||
|
|
||||||
|
if let Some(y) = x3b.sqrt() {
|
||||||
|
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||||
|
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"should have rejected the point because it isn't in the correct subgroup"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x.add_assign(&Fq::one());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_g2_uncompressed_invalid_vectors() {
|
||||||
|
{
|
||||||
|
let z = G2Affine::zero().into_uncompressed();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[0] |= 0b1000_0000;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because we expected an uncompressed point");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[0] |= 0b0010_0000;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..G2Uncompressed::size() {
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[i] |= 0b0000_0001;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let o = G2Affine::one().into_uncompressed();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
o.as_mut()[0] |= 0b1000_0000;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because we expected an uncompressed point");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let m = Fq::char();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||||
|
assert_eq!(coordinate, "x coordinate (c1)");
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||||
|
assert_eq!(coordinate, "x coordinate (c0)");
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[96..]).unwrap();
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||||
|
assert_eq!(coordinate, "y coordinate (c1)");
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[144..]).unwrap();
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||||
|
assert_eq!(coordinate, "y coordinate (c0)");
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let m = Fq::zero().into_repr();
|
||||||
|
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because it isn't on the curve")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
let mut x = Fq2::one();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut x3b = x;
|
||||||
|
x3b.square();
|
||||||
|
x3b.mul_assign(&x);
|
||||||
|
x3b.add_assign(&Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||||
|
}); // TODO: perhaps expose coeff_b through API?
|
||||||
|
|
||||||
|
if let Some(y) = x3b.sqrt() {
|
||||||
|
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||||
|
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||||
|
y.c1.into_repr().write_be(&mut o.as_mut()[96..]).unwrap();
|
||||||
|
y.c0.into_repr().write_be(&mut o.as_mut()[144..]).unwrap();
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"should have rejected the point because it isn't in the correct subgroup"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x.add_assign(&Fq2::one());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_g1_compressed_invalid_vectors() {
|
||||||
|
{
|
||||||
|
let z = G1Affine::zero().into_compressed();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[0] &= 0b0111_1111;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because we expected a compressed point");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[0] |= 0b0010_0000;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..G1Compressed::size() {
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[i] |= 0b0000_0001;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let o = G1Affine::one().into_compressed();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
o.as_mut()[0] &= 0b0111_1111;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because we expected a compressed point");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let m = Fq::char();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
o.as_mut()[0] |= 0b1000_0000;
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||||
|
assert_eq!(coordinate, "x coordinate");
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
let mut x = Fq::one();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut x3b = x;
|
||||||
|
x3b.square();
|
||||||
|
x3b.mul_assign(&x);
|
||||||
|
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||||
|
|
||||||
|
if let Some(_) = x3b.sqrt() {
|
||||||
|
x.add_assign(&Fq::one());
|
||||||
|
} else {
|
||||||
|
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
o.as_mut()[0] |= 0b1000_0000;
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because it isn't on the curve")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
let mut x = Fq::one();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut x3b = x;
|
||||||
|
x3b.square();
|
||||||
|
x3b.mul_assign(&x);
|
||||||
|
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||||
|
|
||||||
|
if let Some(_) = x3b.sqrt() {
|
||||||
|
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||||
|
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
o.as_mut()[0] |= 0b1000_0000;
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"should have rejected the point because it isn't in the correct subgroup"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x.add_assign(&Fq::one());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_g2_compressed_invalid_vectors() {
|
||||||
|
{
|
||||||
|
let z = G2Affine::zero().into_compressed();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[0] &= 0b0111_1111;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because we expected a compressed point");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[0] |= 0b0010_0000;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..G2Compressed::size() {
|
||||||
|
let mut z = z;
|
||||||
|
z.as_mut()[i] |= 0b0000_0001;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let o = G2Affine::one().into_compressed();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
o.as_mut()[0] &= 0b0111_1111;
|
||||||
|
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||||
|
// :)
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because we expected a compressed point");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let m = Fq::char();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
o.as_mut()[0] |= 0b1000_0000;
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||||
|
assert_eq!(coordinate, "x coordinate (c1)");
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||||
|
o.as_mut()[0] |= 0b1000_0000;
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||||
|
assert_eq!(coordinate, "x coordinate (c0)");
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
let mut x = Fq2 {
|
||||||
|
c0: Fq::one(),
|
||||||
|
c1: Fq::one(),
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut x3b = x;
|
||||||
|
x3b.square();
|
||||||
|
x3b.mul_assign(&x);
|
||||||
|
x3b.add_assign(&Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||||
|
}); // TODO: perhaps expose coeff_b through API?
|
||||||
|
|
||||||
|
if let Some(_) = x3b.sqrt() {
|
||||||
|
x.add_assign(&Fq2::one());
|
||||||
|
} else {
|
||||||
|
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||||
|
o.as_mut()[0] |= 0b1000_0000;
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
panic!("should have rejected the point because it isn't on the curve")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut o = o;
|
||||||
|
let mut x = Fq2 {
|
||||||
|
c0: Fq::one(),
|
||||||
|
c1: Fq::one(),
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut x3b = x;
|
||||||
|
x3b.square();
|
||||||
|
x3b.mul_assign(&x);
|
||||||
|
x3b.add_assign(&Fq2 {
|
||||||
|
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||||
|
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||||
|
}); // TODO: perhaps expose coeff_b through API?
|
||||||
|
|
||||||
|
if let Some(_) = x3b.sqrt() {
|
||||||
|
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||||
|
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||||
|
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||||
|
o.as_mut()[0] |= 0b1000_0000;
|
||||||
|
|
||||||
|
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"should have rejected the point because it isn't in the correct subgroup"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x.add_assign(&Fq2::one());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
pairing/src/lib.rs
Normal file
113
pairing/src/lib.rs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
//! A library for working with pairing-friendly curves.
|
||||||
|
|
||||||
|
// `clippy` is a code linting tool for improving code quality by catching
|
||||||
|
// common mistakes or strange code patterns. If the `cargo-clippy` feature
|
||||||
|
// is provided, all compiler warnings are prohibited.
|
||||||
|
#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
|
||||||
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))]
|
||||||
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
|
||||||
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))]
|
||||||
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
|
||||||
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||||
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))]
|
||||||
|
// Catch documentation errors caused by code changes.
|
||||||
|
#![deny(intra_doc_link_resolution_failure)]
|
||||||
|
// Force public structures to implement Debug
|
||||||
|
#![deny(missing_debug_implementations)]
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests;
|
||||||
|
|
||||||
|
pub mod bls12_381;
|
||||||
|
|
||||||
|
use ff::{Field, PrimeField, ScalarEngine, SqrtField};
|
||||||
|
use group::{CurveAffine, CurveProjective};
|
||||||
|
|
||||||
|
/// An "engine" is a collection of types (fields, elliptic curve groups, etc.)
|
||||||
|
/// with well-defined relationships. In particular, the G1/G2 curve groups are
|
||||||
|
/// of prime order `r`, and are equipped with a bilinear pairing function.
|
||||||
|
pub trait Engine: ScalarEngine {
|
||||||
|
/// The projective representation of an element in G1.
|
||||||
|
type G1: CurveProjective<
|
||||||
|
Engine = Self,
|
||||||
|
Base = Self::Fq,
|
||||||
|
Scalar = Self::Fr,
|
||||||
|
Affine = Self::G1Affine,
|
||||||
|
> + From<Self::G1Affine>;
|
||||||
|
|
||||||
|
/// The affine representation of an element in G1.
|
||||||
|
type G1Affine: PairingCurveAffine<
|
||||||
|
Engine = Self,
|
||||||
|
Base = Self::Fq,
|
||||||
|
Scalar = Self::Fr,
|
||||||
|
Projective = Self::G1,
|
||||||
|
Pair = Self::G2Affine,
|
||||||
|
PairingResult = Self::Fqk,
|
||||||
|
> + From<Self::G1>;
|
||||||
|
|
||||||
|
/// The projective representation of an element in G2.
|
||||||
|
type G2: CurveProjective<
|
||||||
|
Engine = Self,
|
||||||
|
Base = Self::Fqe,
|
||||||
|
Scalar = Self::Fr,
|
||||||
|
Affine = Self::G2Affine,
|
||||||
|
> + From<Self::G2Affine>;
|
||||||
|
|
||||||
|
/// The affine representation of an element in G2.
|
||||||
|
type G2Affine: PairingCurveAffine<
|
||||||
|
Engine = Self,
|
||||||
|
Base = Self::Fqe,
|
||||||
|
Scalar = Self::Fr,
|
||||||
|
Projective = Self::G2,
|
||||||
|
Pair = Self::G1Affine,
|
||||||
|
PairingResult = Self::Fqk,
|
||||||
|
> + From<Self::G2>;
|
||||||
|
|
||||||
|
/// The base field that hosts G1.
|
||||||
|
type Fq: PrimeField + SqrtField;
|
||||||
|
|
||||||
|
/// The extension field that hosts G2.
|
||||||
|
type Fqe: SqrtField;
|
||||||
|
|
||||||
|
/// The extension field that hosts the target group of the pairing.
|
||||||
|
type Fqk: Field;
|
||||||
|
|
||||||
|
/// Perform a miller loop with some number of (G1, G2) pairs.
|
||||||
|
fn miller_loop<'a, I>(i: I) -> Self::Fqk
|
||||||
|
where
|
||||||
|
I: IntoIterator<
|
||||||
|
Item = &'a (
|
||||||
|
&'a <Self::G1Affine as PairingCurveAffine>::Prepared,
|
||||||
|
&'a <Self::G2Affine as PairingCurveAffine>::Prepared,
|
||||||
|
),
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// Perform final exponentiation of the result of a miller loop.
|
||||||
|
fn final_exponentiation(_: &Self::Fqk) -> Option<Self::Fqk>;
|
||||||
|
|
||||||
|
/// Performs a complete pairing operation `(p, q)`.
|
||||||
|
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
|
||||||
|
where
|
||||||
|
G1: Into<Self::G1Affine>,
|
||||||
|
G2: Into<Self::G2Affine>,
|
||||||
|
{
|
||||||
|
Self::final_exponentiation(&Self::miller_loop(
|
||||||
|
[(&(p.into().prepare()), &(q.into().prepare()))].iter(),
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Affine representation of an elliptic curve point that can be used
|
||||||
|
/// to perform pairings.
|
||||||
|
pub trait PairingCurveAffine: CurveAffine {
|
||||||
|
type Prepared: Clone + Send + Sync + 'static;
|
||||||
|
type Pair: PairingCurveAffine<Pair = Self>;
|
||||||
|
type PairingResult: Field;
|
||||||
|
|
||||||
|
/// Prepares this element for pairing purposes.
|
||||||
|
fn prepare(&self) -> Self::Prepared;
|
||||||
|
|
||||||
|
/// Perform a pairing
|
||||||
|
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult;
|
||||||
|
}
|
||||||
137
pairing/src/tests/engine.rs
Normal file
137
pairing/src/tests/engine.rs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
use group::{CurveAffine, CurveProjective};
|
||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use crate::{Engine, Field, PairingCurveAffine, PrimeField};
|
||||||
|
|
||||||
|
pub fn engine_tests<E: Engine>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..10 {
|
||||||
|
let a = E::G1::random(&mut rng).into_affine();
|
||||||
|
let b = E::G2::random(&mut rng).into_affine();
|
||||||
|
|
||||||
|
assert!(a.pairing_with(&b) == b.pairing_with(&a));
|
||||||
|
assert!(a.pairing_with(&b) == E::pairing(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let z1 = E::G1Affine::zero().prepare();
|
||||||
|
let z2 = E::G2Affine::zero().prepare();
|
||||||
|
|
||||||
|
let a = E::G1::random(&mut rng).into_affine().prepare();
|
||||||
|
let b = E::G2::random(&mut rng).into_affine().prepare();
|
||||||
|
let c = E::G1::random(&mut rng).into_affine().prepare();
|
||||||
|
let d = E::G2::random(&mut rng).into_affine().prepare();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
E::Fqk::one(),
|
||||||
|
E::final_exponentiation(&E::miller_loop(&[(&z1, &b)])).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
E::Fqk::one(),
|
||||||
|
E::final_exponentiation(&E::miller_loop(&[(&a, &z2)])).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
E::final_exponentiation(&E::miller_loop(&[(&z1, &b), (&c, &d)])).unwrap(),
|
||||||
|
E::final_exponentiation(&E::miller_loop(&[(&a, &z2), (&c, &d)])).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&z1, &d)])).unwrap(),
|
||||||
|
E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &z2)])).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
random_bilinearity_tests::<E>();
|
||||||
|
random_miller_loop_tests::<E>();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_miller_loop_tests<E: Engine>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Exercise the miller loop for a reduced pairing
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let a = E::G1::random(&mut rng);
|
||||||
|
let b = E::G2::random(&mut rng);
|
||||||
|
|
||||||
|
let p2 = E::pairing(a, b);
|
||||||
|
|
||||||
|
let a = a.into_affine().prepare();
|
||||||
|
let b = b.into_affine().prepare();
|
||||||
|
|
||||||
|
let p1 = E::final_exponentiation(&E::miller_loop(&[(&a, &b)])).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exercise a double miller loop
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let a = E::G1::random(&mut rng);
|
||||||
|
let b = E::G2::random(&mut rng);
|
||||||
|
let c = E::G1::random(&mut rng);
|
||||||
|
let d = E::G2::random(&mut rng);
|
||||||
|
|
||||||
|
let ab = E::pairing(a, b);
|
||||||
|
let cd = E::pairing(c, d);
|
||||||
|
|
||||||
|
let mut abcd = ab;
|
||||||
|
abcd.mul_assign(&cd);
|
||||||
|
|
||||||
|
let a = a.into_affine().prepare();
|
||||||
|
let b = b.into_affine().prepare();
|
||||||
|
let c = c.into_affine().prepare();
|
||||||
|
let d = d.into_affine().prepare();
|
||||||
|
|
||||||
|
let abcd_with_double_loop =
|
||||||
|
E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &d)])).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(abcd, abcd_with_double_loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_bilinearity_tests<E: Engine>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let a = E::G1::random(&mut rng);
|
||||||
|
let b = E::G2::random(&mut rng);
|
||||||
|
|
||||||
|
let c = E::Fr::random(&mut rng);
|
||||||
|
let d = E::Fr::random(&mut rng);
|
||||||
|
|
||||||
|
let mut ac = a;
|
||||||
|
ac.mul_assign(c);
|
||||||
|
|
||||||
|
let mut ad = a;
|
||||||
|
ad.mul_assign(d);
|
||||||
|
|
||||||
|
let mut bc = b;
|
||||||
|
bc.mul_assign(c);
|
||||||
|
|
||||||
|
let mut bd = b;
|
||||||
|
bd.mul_assign(d);
|
||||||
|
|
||||||
|
let acbd = E::pairing(ac, bd);
|
||||||
|
let adbc = E::pairing(ad, bc);
|
||||||
|
|
||||||
|
let mut cd = c;
|
||||||
|
cd.mul_assign(&d);
|
||||||
|
|
||||||
|
let abcd = E::pairing(a, b).pow(cd.into_repr());
|
||||||
|
|
||||||
|
assert_eq!(acbd, adbc);
|
||||||
|
assert_eq!(acbd, abcd);
|
||||||
|
}
|
||||||
|
}
|
||||||
279
pairing/src/tests/field.rs
Normal file
279
pairing/src/tests/field.rs
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
use ff::{Field, LegendreSymbol, PrimeField, SqrtField};
|
||||||
|
use rand_core::{RngCore, SeedableRng};
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
for i in 0..(maxpower + 1) {
|
||||||
|
let mut a = F::random(&mut rng);
|
||||||
|
let mut b = a;
|
||||||
|
|
||||||
|
for _ in 0..i {
|
||||||
|
a = a.pow(&characteristic);
|
||||||
|
}
|
||||||
|
b.frobenius_map(i);
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random_sqrt_tests<F: SqrtField>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..10000 {
|
||||||
|
let a = F::random(&mut rng);
|
||||||
|
let mut b = a;
|
||||||
|
b.square();
|
||||||
|
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
|
||||||
|
|
||||||
|
let b = b.sqrt().unwrap();
|
||||||
|
let mut negb = b;
|
||||||
|
negb.negate();
|
||||||
|
|
||||||
|
assert!(a == b || a == negb);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut c = F::one();
|
||||||
|
for _ in 0..10000 {
|
||||||
|
let mut b = c;
|
||||||
|
b.square();
|
||||||
|
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
|
||||||
|
|
||||||
|
b = b.sqrt().unwrap();
|
||||||
|
|
||||||
|
if b != c {
|
||||||
|
b.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(b, c);
|
||||||
|
|
||||||
|
c.add_assign(&F::one());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random_field_tests<F: Field>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
random_multiplication_tests::<F, _>(&mut rng);
|
||||||
|
random_addition_tests::<F, _>(&mut rng);
|
||||||
|
random_subtraction_tests::<F, _>(&mut rng);
|
||||||
|
random_negation_tests::<F, _>(&mut rng);
|
||||||
|
random_doubling_tests::<F, _>(&mut rng);
|
||||||
|
random_squaring_tests::<F, _>(&mut rng);
|
||||||
|
random_inversion_tests::<F, _>(&mut rng);
|
||||||
|
random_expansion_tests::<F, _>(&mut rng);
|
||||||
|
|
||||||
|
assert!(F::zero().is_zero());
|
||||||
|
{
|
||||||
|
let mut z = F::zero();
|
||||||
|
z.negate();
|
||||||
|
assert!(z.is_zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(F::zero().inverse().is_none());
|
||||||
|
|
||||||
|
// Multiplication by zero
|
||||||
|
{
|
||||||
|
let mut a = F::random(&mut rng);
|
||||||
|
a.mul_assign(&F::zero());
|
||||||
|
assert!(a.is_zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addition by zero
|
||||||
|
{
|
||||||
|
let mut a = F::random(&mut rng);
|
||||||
|
let copy = a;
|
||||||
|
a.add_assign(&F::zero());
|
||||||
|
assert_eq!(a, copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_str_tests<F: PrimeField>() {
|
||||||
|
{
|
||||||
|
let a = "84395729384759238745923745892374598234705297301958723458712394587103249587213984572934750213947582345792304758273458972349582734958273495872304598234";
|
||||||
|
let b = "38495729084572938457298347502349857029384609283450692834058293405982304598230458230495820394850293845098234059823049582309485203948502938452093482039";
|
||||||
|
let c = "3248875134290623212325429203829831876024364170316860259933542844758450336418538569901990710701240661702808867062612075657861768196242274635305077449545396068598317421057721935408562373834079015873933065667961469731886739181625866970316226171512545167081793907058686908697431878454091011239990119126";
|
||||||
|
|
||||||
|
let mut a = F::from_str(a).unwrap();
|
||||||
|
let b = F::from_str(b).unwrap();
|
||||||
|
let c = F::from_str(c).unwrap();
|
||||||
|
|
||||||
|
a.mul_assign(&b);
|
||||||
|
|
||||||
|
assert_eq!(a, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let n = rng.next_u64();
|
||||||
|
|
||||||
|
let a = F::from_str(&format!("{}", n)).unwrap();
|
||||||
|
let b = F::from_repr(n.into()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(F::from_str("").is_none());
|
||||||
|
assert!(F::from_str("0").unwrap().is_zero());
|
||||||
|
assert!(F::from_str("00").is_none());
|
||||||
|
assert!(F::from_str("00000000000").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_multiplication_tests<F: Field, R: RngCore>(rng: &mut R) {
|
||||||
|
for _ in 0..10000 {
|
||||||
|
let a = F::random(rng);
|
||||||
|
let b = F::random(rng);
|
||||||
|
let c = F::random(rng);
|
||||||
|
|
||||||
|
let mut t0 = a; // (a * b) * c
|
||||||
|
t0.mul_assign(&b);
|
||||||
|
t0.mul_assign(&c);
|
||||||
|
|
||||||
|
let mut t1 = a; // (a * c) * b
|
||||||
|
t1.mul_assign(&c);
|
||||||
|
t1.mul_assign(&b);
|
||||||
|
|
||||||
|
let mut t2 = b; // (b * c) * a
|
||||||
|
t2.mul_assign(&c);
|
||||||
|
t2.mul_assign(&a);
|
||||||
|
|
||||||
|
assert_eq!(t0, t1);
|
||||||
|
assert_eq!(t1, t2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_addition_tests<F: Field, R: RngCore>(rng: &mut R) {
|
||||||
|
for _ in 0..10000 {
|
||||||
|
let a = F::random(rng);
|
||||||
|
let b = F::random(rng);
|
||||||
|
let c = F::random(rng);
|
||||||
|
|
||||||
|
let mut t0 = a; // (a + b) + c
|
||||||
|
t0.add_assign(&b);
|
||||||
|
t0.add_assign(&c);
|
||||||
|
|
||||||
|
let mut t1 = a; // (a + c) + b
|
||||||
|
t1.add_assign(&c);
|
||||||
|
t1.add_assign(&b);
|
||||||
|
|
||||||
|
let mut t2 = b; // (b + c) + a
|
||||||
|
t2.add_assign(&c);
|
||||||
|
t2.add_assign(&a);
|
||||||
|
|
||||||
|
assert_eq!(t0, t1);
|
||||||
|
assert_eq!(t1, t2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_subtraction_tests<F: Field, R: RngCore>(rng: &mut R) {
|
||||||
|
for _ in 0..10000 {
|
||||||
|
let b = F::random(rng);
|
||||||
|
let a = F::random(rng);
|
||||||
|
|
||||||
|
let mut t0 = a; // (a - b)
|
||||||
|
t0.sub_assign(&b);
|
||||||
|
|
||||||
|
let mut t1 = b; // (b - a)
|
||||||
|
t1.sub_assign(&a);
|
||||||
|
|
||||||
|
let mut t2 = t0; // (a - b) + (b - a) = 0
|
||||||
|
t2.add_assign(&t1);
|
||||||
|
|
||||||
|
assert!(t2.is_zero());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_negation_tests<F: Field, R: RngCore>(rng: &mut R) {
|
||||||
|
for _ in 0..10000 {
|
||||||
|
let a = F::random(rng);
|
||||||
|
let mut b = a;
|
||||||
|
b.negate();
|
||||||
|
b.add_assign(&a);
|
||||||
|
|
||||||
|
assert!(b.is_zero());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_doubling_tests<F: Field, R: RngCore>(rng: &mut R) {
|
||||||
|
for _ in 0..10000 {
|
||||||
|
let mut a = F::random(rng);
|
||||||
|
let mut b = a;
|
||||||
|
a.add_assign(&b);
|
||||||
|
b.double();
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_squaring_tests<F: Field, R: RngCore>(rng: &mut R) {
|
||||||
|
for _ in 0..10000 {
|
||||||
|
let mut a = F::random(rng);
|
||||||
|
let mut b = a;
|
||||||
|
a.mul_assign(&b);
|
||||||
|
b.square();
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_inversion_tests<F: Field, R: RngCore>(rng: &mut R) {
|
||||||
|
assert!(F::zero().inverse().is_none());
|
||||||
|
|
||||||
|
for _ in 0..10000 {
|
||||||
|
let mut a = F::random(rng);
|
||||||
|
let b = a.inverse().unwrap(); // probablistically nonzero
|
||||||
|
a.mul_assign(&b);
|
||||||
|
|
||||||
|
assert_eq!(a, F::one());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_expansion_tests<F: Field, R: RngCore>(rng: &mut R) {
|
||||||
|
for _ in 0..10000 {
|
||||||
|
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
|
||||||
|
|
||||||
|
let a = F::random(rng);
|
||||||
|
let b = F::random(rng);
|
||||||
|
let c = F::random(rng);
|
||||||
|
let d = F::random(rng);
|
||||||
|
|
||||||
|
let mut t0 = a;
|
||||||
|
t0.add_assign(&b);
|
||||||
|
let mut t1 = c;
|
||||||
|
t1.add_assign(&d);
|
||||||
|
t0.mul_assign(&t1);
|
||||||
|
|
||||||
|
let mut t2 = a;
|
||||||
|
t2.mul_assign(&c);
|
||||||
|
let mut t3 = b;
|
||||||
|
t3.mul_assign(&c);
|
||||||
|
let mut t4 = a;
|
||||||
|
t4.mul_assign(&d);
|
||||||
|
let mut t5 = b;
|
||||||
|
t5.mul_assign(&d);
|
||||||
|
|
||||||
|
t2.add_assign(&t3);
|
||||||
|
t2.add_assign(&t4);
|
||||||
|
t2.add_assign(&t5);
|
||||||
|
|
||||||
|
assert_eq!(t0, t2);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
pairing/src/tests/mod.rs
Normal file
3
pairing/src/tests/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod engine;
|
||||||
|
pub mod field;
|
||||||
|
pub mod repr;
|
||||||
108
pairing/src/tests/repr.rs
Normal file
108
pairing/src/tests/repr.rs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
pub fn random_repr_tests<P: PrimeField>() {
|
||||||
|
random_encoding_tests::<P>();
|
||||||
|
random_shl_tests::<P>();
|
||||||
|
random_shr_tests::<P>();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_encoding_tests<P: PrimeField>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let r = P::random(&mut rng).into_repr();
|
||||||
|
|
||||||
|
// Big endian
|
||||||
|
{
|
||||||
|
let mut rdecoded = <P as PrimeField>::Repr::default();
|
||||||
|
|
||||||
|
let mut v: Vec<u8> = vec![];
|
||||||
|
r.write_be(&mut v).unwrap();
|
||||||
|
rdecoded.read_be(&v[0..]).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(r, rdecoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Little endian
|
||||||
|
{
|
||||||
|
let mut rdecoded = <P as PrimeField>::Repr::default();
|
||||||
|
|
||||||
|
let mut v: Vec<u8> = vec![];
|
||||||
|
r.write_le(&mut v).unwrap();
|
||||||
|
rdecoded.read_le(&v[0..]).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(r, rdecoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut rdecoded_le = <P as PrimeField>::Repr::default();
|
||||||
|
let mut rdecoded_be_flip = <P as PrimeField>::Repr::default();
|
||||||
|
|
||||||
|
let mut v: Vec<u8> = vec![];
|
||||||
|
r.write_le(&mut v).unwrap();
|
||||||
|
|
||||||
|
// This reads in little-endian, so we are done.
|
||||||
|
rdecoded_le.read_le(&v[..]).unwrap();
|
||||||
|
|
||||||
|
// This reads in big-endian, so we perform a swap of the
|
||||||
|
// bytes beforehand.
|
||||||
|
let v: Vec<u8> = v.into_iter().rev().collect();
|
||||||
|
rdecoded_be_flip.read_be(&v[..]).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(rdecoded_le, rdecoded_be_flip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_shl_tests<P: PrimeField>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
let r = P::random(&mut rng).into_repr();
|
||||||
|
|
||||||
|
for shift in 0..(r.num_bits() + 1) {
|
||||||
|
let mut r1 = r;
|
||||||
|
let mut r2 = r;
|
||||||
|
|
||||||
|
for _ in 0..shift {
|
||||||
|
r1.mul2();
|
||||||
|
}
|
||||||
|
|
||||||
|
r2.shl(shift);
|
||||||
|
|
||||||
|
assert_eq!(r1, r2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_shr_tests<P: PrimeField>() {
|
||||||
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
let r = P::random(&mut rng).into_repr();
|
||||||
|
|
||||||
|
for shift in 0..(r.num_bits() + 1) {
|
||||||
|
let mut r1 = r;
|
||||||
|
let mut r2 = r;
|
||||||
|
|
||||||
|
for _ in 0..shift {
|
||||||
|
r1.div2();
|
||||||
|
}
|
||||||
|
|
||||||
|
r2.shr(shift);
|
||||||
|
|
||||||
|
assert_eq!(r1, r2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1653
src/rustzcash.rs
1653
src/rustzcash.rs
File diff suppressed because it is too large
Load Diff
2
zcash_client_backend/.gitignore
vendored
Normal file
2
zcash_client_backend/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Protobufs
|
||||||
|
src/proto/
|
||||||
31
zcash_client_backend/Cargo.toml
Normal file
31
zcash_client_backend/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
name = "zcash_client_backend"
|
||||||
|
description = "APIs for creating shielded Zcash light clients"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = [
|
||||||
|
"Jack Grigg <jack@z.cash>",
|
||||||
|
]
|
||||||
|
homepage = "https://github.com/zcash/librustzcash"
|
||||||
|
repository = "https://github.com/zcash/librustzcash"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bech32 = "0.7"
|
||||||
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
|
hex = "0.3"
|
||||||
|
pairing = { version = "0.15.0", path = "../pairing" }
|
||||||
|
protobuf = "2"
|
||||||
|
subtle = "2"
|
||||||
|
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
protobuf-codegen-pure = "2"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand_core = "0.5"
|
||||||
|
rand_xorshift = "0.2"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
202
zcash_client_backend/LICENSE-APACHE
Normal file
202
zcash_client_backend/LICENSE-APACHE
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
21
zcash_client_backend/LICENSE-MIT
Normal file
21
zcash_client_backend/LICENSE-MIT
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017-2019 Electric Coin Company
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user