-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathOneMessageVerifier.sol
More file actions
953 lines (897 loc) · 48.5 KB
/
OneMessageVerifier.sol
File metadata and controls
953 lines (897 loc) · 48.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Groth16 verifier template.
/// @author Remco Bloemen
/// @notice Supports verifying Groth16 proofs. Proofs can be in uncompressed
/// (256 bytes) and compressed (128 bytes) format. A view function is provided
/// to compress proofs.
/// @notice See <https://2π.com/23/bn254-compression> for further explanation.
contract Verifier {
/// Some of the provided public input values are larger than the field modulus.
/// @dev Public input elements are not automatically reduced, as this is can be
/// a dangerous source of bugs.
error PublicInputNotInField();
/// The proof is invalid.
/// @dev This can mean that provided Groth16 proof points are not on their
/// curves, that pairing equation fails, or that the proof is not for the
/// provided public input.
error ProofInvalid();
/// The commitment is invalid
/// @dev This can mean that provided commitment points and/or proof of knowledge are not on their
/// curves, that pairing equation fails, or that the commitment and/or proof of knowledge is not for the
/// commitment key.
error CommitmentInvalid();
// Addresses of precompiles
uint256 constant PRECOMPILE_MODEXP = 0x05;
uint256 constant PRECOMPILE_ADD = 0x06;
uint256 constant PRECOMPILE_MUL = 0x07;
uint256 constant PRECOMPILE_VERIFY = 0x08;
// Base field Fp order P and scalar field Fr order R.
// For BN254 these are computed as follows:
// t = 4965661367192848881
// P = 36⋅t⁴ + 36⋅t³ + 24⋅t² + 6⋅t + 1
// R = 36⋅t⁴ + 36⋅t³ + 18⋅t² + 6⋅t + 1
uint256 constant P = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47;
uint256 constant R = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001;
// Extension field Fp2 = Fp[i] / (i² + 1)
// Note: This is the complex extension field of Fp with i² = -1.
// Values in Fp2 are represented as a pair of Fp elements (a₀, a₁) as a₀ + a₁⋅i.
// Note: The order of Fp2 elements is *opposite* that of the pairing contract, which
// expects Fp2 elements in order (a₁, a₀). This is also the order in which
// Fp2 elements are encoded in the public interface as this became convention.
// Constants in Fp
uint256 constant FRACTION_1_2_FP = 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea4;
uint256 constant FRACTION_27_82_FP = 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5;
uint256 constant FRACTION_3_82_FP = 0x2fcd3ac2a640a154eb23960892a85a68f031ca0c8344b23a577dcf1052b9e775;
// Exponents for inversions and square roots mod P
uint256 constant EXP_INVERSE_FP = 0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD45; // P - 2
uint256 constant EXP_SQRT_FP = 0xC19139CB84C680A6E14116DA060561765E05AA45A1C72A34F082305B61F3F52; // (P + 1) / 4;
// Groth16 alpha point in G1
uint256 constant ALPHA_X = 18641427686736713020993062226583607447301043608555966629092020111869245242565;
uint256 constant ALPHA_Y = 12432446291979862642469973588804293726383533142987786036790758886024477926424;
// Groth16 beta point in G2 in powers of i
uint256 constant BETA_NEG_X_0 = 9615694438524662631442752584096807013815617154953998260458922742045457152967;
uint256 constant BETA_NEG_X_1 = 11097895868754245911447533373694890232515767480833867501962441831901902095913;
uint256 constant BETA_NEG_Y_0 = 10253013218163874517258527761263983628737955515521973444654795116789931224493;
uint256 constant BETA_NEG_Y_1 = 18569211750884641425158983523563693820955458724692108476926429095650117298008;
// Groth16 gamma point in G2 in powers of i
uint256 constant GAMMA_NEG_X_0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 constant GAMMA_NEG_X_1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 constant GAMMA_NEG_Y_0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653;
uint256 constant GAMMA_NEG_Y_1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052;
// Groth16 delta point in G2 in powers of i
uint256 constant DELTA_NEG_X_0 = 3675049697043254769543473913458103528930212466287075803585411658200384746514;
uint256 constant DELTA_NEG_X_1 = 18908093541023970128043908051548670559368031614127192357014108089852553994808;
uint256 constant DELTA_NEG_Y_0 = 4533655959879331474973736959526638759847494216216899401001588225212639657777;
uint256 constant DELTA_NEG_Y_1 = 894186127501553009224526794645111966911990831855332983349524069859425431026;
// Pedersen G point in G2 in powers of i
uint256 constant PEDERSEN_G_X_0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 constant PEDERSEN_G_X_1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 constant PEDERSEN_G_Y_0 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
uint256 constant PEDERSEN_G_Y_1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
// Pedersen GSigmaNeg point in G2 in powers of i
uint256 constant PEDERSEN_GSIGMANEG_X_0 = 11620024760246792754342534399353419605608338549093041081335802242267327999790;
uint256 constant PEDERSEN_GSIGMANEG_X_1 = 8286351730278980386491033739336362823590413796029395043941770103507457628936;
uint256 constant PEDERSEN_GSIGMANEG_Y_0 = 6603517043256280517997220719730660434808714112235857700187153110121006111372;
uint256 constant PEDERSEN_GSIGMANEG_Y_1 = 20261823566090045701856088426168078114520438158256797801760138080818037871266;
// Constant and public input points
uint256 constant CONSTANT_X = 12884939265279250898729116391752599059448439957073503543886246412221662894741;
uint256 constant CONSTANT_Y = 3697560626522563724982332697217448903694950065590252525791719734483166408192;
uint256 constant PUB_0_X = 10203777678156007235581817993069257005737747000880622889078199794474712756176;
uint256 constant PUB_0_Y = 7840926828586263413771701088674659261733745733383431643737720151708160426858;
uint256 constant PUB_1_X = 17240324731637471014278856407550441392586309567760394155367099613342333600717;
uint256 constant PUB_1_Y = 708122064289922192991137015524953784746636726381173338665247426929302939987;
uint256 constant PUB_2_X = 20970938810528989084268035404525263806965750837998288385535699802237566760974;
uint256 constant PUB_2_Y = 5882544101299842561493295781812695223685789527128720235713960652951573608564;
uint256 constant PUB_3_X = 10360347810145642192121041871212043995027101052145011120958841107648359596445;
uint256 constant PUB_3_Y = 13439120493379842208342365483252508807420214426250726621491095215651390026847;
uint256 constant PUB_4_X = 6737815358287166911506993927075473974122563017694671826361516963995307936875;
uint256 constant PUB_4_Y = 10792623158436825090972973352762950509125332940362219424998353381922238736828;
uint256 constant PUB_5_X = 1344556628294272993418606327255535013712957214180128361513951292128852506272;
uint256 constant PUB_5_Y = 9471101073927201575661766814364677719144778068778220045410754890465523200750;
uint256 constant PUB_6_X = 4261016725826275820485109841201881435394764242176274632948515997910431679049;
uint256 constant PUB_6_Y = 12520079742278343614709742492999071245425093486621873433448348516809894716862;
uint256 constant PUB_7_X = 12966061960639936295638472819701875803263478752769328816368264873939012547100;
uint256 constant PUB_7_Y = 8647270452715349170262039744104912969556176751247059148594366260361305809558;
uint256 constant PUB_8_X = 7820323703057301784559318204609249583166699374649952207553332628592306366185;
uint256 constant PUB_8_Y = 20270419322663473934294331246823158775494610933889690084057331652759968281662;
uint256 constant PUB_9_X = 17100222409510428233143733952177321055526611514357405960575452409096148472028;
uint256 constant PUB_9_Y = 20031510449665710830838595291320869409370615878066303183962946425185396703403;
uint256 constant PUB_10_X = 21837842007365765506646890044439489887673569630598046917376494109662919221339;
uint256 constant PUB_10_Y = 4654829628732662191413805917122949156290572207927340943189974572131280217762;
uint256 constant PUB_11_X = 7617292678002354731305497722183628316764365985967550964623611054431494999055;
uint256 constant PUB_11_Y = 3491326943639535551045901959032930294073371072863660391551580586648377389049;
uint256 constant PUB_12_X = 4665884448850399922274674033412099495689190806931147010775401156554375587179;
uint256 constant PUB_12_Y = 2982542740590380002991318542964936028489069958882189807962400685884658661748;
uint256 constant PUB_13_X = 20932901940092760396567243287055208217470405980088631226748838809047686885073;
uint256 constant PUB_13_Y = 3560821518619824042053593793803931545672196311820542957664203711967321119678;
uint256 constant PUB_14_X = 16241500179864397914257740175322625565506623699770328753299043146565546428432;
uint256 constant PUB_14_Y = 16567252598226728073368900123579640613834888564929243528189371750540551135891;
uint256 constant PUB_15_X = 14141508057011162840394745489827635030121127779968116263653799512100617444576;
uint256 constant PUB_15_Y = 4299850795157272783169165174017730777370383280851893000336168655973164719697;
uint256 constant PUB_16_X = 3860906907605491664465499455938390064078405577745329796367764015149594165866;
uint256 constant PUB_16_Y = 11198226610413905825041441814621587198110397180135860380072334685211116425543;
uint256 constant PUB_17_X = 13903541273112843522582364725249668539965699138324140062754613507562197117628;
uint256 constant PUB_17_Y = 16709594746099540628688661092007709268421980378842104840878950072943662526970;
uint256 constant PUB_18_X = 6027519278131120994131103632661544013553958640019843384719080047831340785588;
uint256 constant PUB_18_Y = 2162186697545564443909287539735912139911193603009429273708512024429227638108;
uint256 constant PUB_19_X = 3259777088926629593736002351863659770968086215913829623350375441453509484348;
uint256 constant PUB_19_Y = 2927137769987935597500561304340209220874106062519182648380835827802549604703;
uint256 constant PUB_20_X = 4671253772183870027968303826119604143021775701571814174429060627215738166903;
uint256 constant PUB_20_Y = 21067711985106923114724336789349906259809173059595241029668824358149348143792;
uint256 constant PUB_21_X = 16777353538891931571896141107797072964140212847995784965273973890597395738419;
uint256 constant PUB_21_Y = 5652506422203376877146225936312910138065617312071400761266990259622200416776;
uint256 constant PUB_22_X = 14905174358742232765585672238378131786926490842187072057282787417907165033249;
uint256 constant PUB_22_Y = 6973964333124193333096941155335955658326658608260903217391241597224166138702;
uint256 constant PUB_23_X = 5802070034340669935742111802553437133787086150822555078564576585021408044036;
uint256 constant PUB_23_Y = 21309255901782510042740045731592913870935664796309803530781473440927260970845;
uint256 constant PUB_24_X = 14412834078912692168683191117338768304651592880827254922646087421928820171490;
uint256 constant PUB_24_Y = 7960114646672594400145630583000778217928116882559166191352331107307381340249;
uint256 constant PUB_25_X = 15045724546032393462940381346362058005738642152411321635314532404791892714481;
uint256 constant PUB_25_Y = 21659294784952063407607380360902111271280305717624676733789092915467151942451;
uint256 constant PUB_26_X = 12436421126036541861721606525417518128789116738244724456002611369053226146645;
uint256 constant PUB_26_Y = 6283544451152388188420971261242014113516424420185358111755712727513711592152;
uint256 constant PUB_27_X = 12181890594447117569769848406541612562272012181820237009088113315497820616037;
uint256 constant PUB_27_Y = 7106679148523770355552067178810013501425483954213062969965150940281138363459;
uint256 constant PUB_28_X = 8372007442632605248642786333595534993391719999457397001831955088527503775806;
uint256 constant PUB_28_Y = 7882637551830419295947337153254756384323729506925119100591798481800074028190;
uint256 constant PUB_29_X = 7541315067969939722982590729142295781311837292093441902895928284311219905535;
uint256 constant PUB_29_Y = 5941389075144182125233183021390604125151179469420938568945874004725806080086;
uint256 constant PUB_30_X = 12466738961528266873801006372345961452506018869174801907101893331199247848070;
uint256 constant PUB_30_Y = 17976132142740629012503498024290883296158090774139166827034284711076769579091;
uint256 constant PUB_31_X = 8391725565258334072109936168835313182327641204161431962545444459611844386645;
uint256 constant PUB_31_Y = 4627749894530634027548233305815629701508596085374319766954039138128946073438;
uint256 constant PUB_32_X = 3787701415788562156979119456591517543357873440718278151292740694334895584522;
uint256 constant PUB_32_Y = 9869503558160196005610269525370243690164662444062458381337807595352483992184;
/// Negation in Fp.
/// @notice Returns a number x such that a + x = 0 in Fp.
/// @notice The input does not need to be reduced.
/// @param a the base
/// @return x the result
function negate(uint256 a) internal pure returns (uint256 x) {
unchecked {
x = (P - (a % P)) % P; // Modulo is cheaper than branching
}
}
/// Exponentiation in Fp.
/// @notice Returns a number x such that a ^ e = x in Fp.
/// @notice The input does not need to be reduced.
/// @param a the base
/// @param e the exponent
/// @return x the result
function exp(uint256 a, uint256 e) internal view returns (uint256 x) {
bool success;
assembly ("memory-safe") {
let f := mload(0x40)
mstore(f, 0x20)
mstore(add(f, 0x20), 0x20)
mstore(add(f, 0x40), 0x20)
mstore(add(f, 0x60), a)
mstore(add(f, 0x80), e)
mstore(add(f, 0xa0), P)
success := staticcall(gas(), PRECOMPILE_MODEXP, f, 0xc0, f, 0x20)
x := mload(f)
}
if (!success) {
// Exponentiation failed.
// Should not happen.
revert ProofInvalid();
}
}
/// Invertsion in Fp.
/// @notice Returns a number x such that a * x = 1 in Fp.
/// @notice The input does not need to be reduced.
/// @notice Reverts with ProofInvalid() if the inverse does not exist
/// @param a the input
/// @return x the solution
function invert_Fp(uint256 a) internal view returns (uint256 x) {
x = exp(a, EXP_INVERSE_FP);
if (mulmod(a, x, P) != 1) {
// Inverse does not exist.
// Can only happen during G2 point decompression.
revert ProofInvalid();
}
}
/// Square root in Fp.
/// @notice Returns a number x such that x * x = a in Fp.
/// @notice Will revert with InvalidProof() if the input is not a square
/// or not reduced.
/// @param a the square
/// @return x the solution
function sqrt_Fp(uint256 a) internal view returns (uint256 x) {
x = exp(a, EXP_SQRT_FP);
if (mulmod(x, x, P) != a) {
// Square root does not exist or a is not reduced.
// Happens when G1 point is not on curve.
revert ProofInvalid();
}
}
/// Square test in Fp.
/// @notice Returns whether a number x exists such that x * x = a in Fp.
/// @notice Will revert with InvalidProof() if the input is not a square
/// or not reduced.
/// @param a the square
/// @return x the solution
function isSquare_Fp(uint256 a) internal view returns (bool) {
uint256 x = exp(a, EXP_SQRT_FP);
return mulmod(x, x, P) == a;
}
/// Square root in Fp2.
/// @notice Fp2 is the complex extension Fp[i]/(i^2 + 1). The input is
/// a0 + a1 ⋅ i and the result is x0 + x1 ⋅ i.
/// @notice Will revert with InvalidProof() if
/// * the input is not a square,
/// * the hint is incorrect, or
/// * the input coefficients are not reduced.
/// @param a0 The real part of the input.
/// @param a1 The imaginary part of the input.
/// @param hint A hint which of two possible signs to pick in the equation.
/// @return x0 The real part of the square root.
/// @return x1 The imaginary part of the square root.
function sqrt_Fp2(uint256 a0, uint256 a1, bool hint) internal view returns (uint256 x0, uint256 x1) {
// If this square root reverts there is no solution in Fp2.
uint256 d = sqrt_Fp(addmod(mulmod(a0, a0, P), mulmod(a1, a1, P), P));
if (hint) {
d = negate(d);
}
// If this square root reverts there is no solution in Fp2.
x0 = sqrt_Fp(mulmod(addmod(a0, d, P), FRACTION_1_2_FP, P));
x1 = mulmod(a1, invert_Fp(mulmod(x0, 2, P)), P);
// Check result to make sure we found a root.
// Note: this also fails if a0 or a1 is not reduced.
if (a0 != addmod(mulmod(x0, x0, P), negate(mulmod(x1, x1, P)), P)
|| a1 != mulmod(2, mulmod(x0, x1, P), P)) {
revert ProofInvalid();
}
}
/// Compress a G1 point.
/// @notice Reverts with InvalidProof if the coordinates are not reduced
/// or if the point is not on the curve.
/// @notice The point at infinity is encoded as (0,0) and compressed to 0.
/// @param x The X coordinate in Fp.
/// @param y The Y coordinate in Fp.
/// @return c The compresed point (x with one signal bit).
function compress_g1(uint256 x, uint256 y) internal view returns (uint256 c) {
if (x >= P || y >= P) {
// G1 point not in field.
revert ProofInvalid();
}
if (x == 0 && y == 0) {
// Point at infinity
return 0;
}
// Note: sqrt_Fp reverts if there is no solution, i.e. the x coordinate is invalid.
uint256 y_pos = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P));
if (y == y_pos) {
return (x << 1) | 0;
} else if (y == negate(y_pos)) {
return (x << 1) | 1;
} else {
// G1 point not on curve.
revert ProofInvalid();
}
}
/// Decompress a G1 point.
/// @notice Reverts with InvalidProof if the input does not represent a valid point.
/// @notice The point at infinity is encoded as (0,0) and compressed to 0.
/// @param c The compresed point (x with one signal bit).
/// @return x The X coordinate in Fp.
/// @return y The Y coordinate in Fp.
function decompress_g1(uint256 c) internal view returns (uint256 x, uint256 y) {
// Note that X = 0 is not on the curve since 0³ + 3 = 3 is not a square.
// so we can use it to represent the point at infinity.
if (c == 0) {
// Point at infinity as encoded in EIP196 and EIP197.
return (0, 0);
}
bool negate_point = c & 1 == 1;
x = c >> 1;
if (x >= P) {
// G1 x coordinate not in field.
revert ProofInvalid();
}
// Note: (x³ + 3) is irreducible in Fp, so it can not be zero and therefore
// y can not be zero.
// Note: sqrt_Fp reverts if there is no solution, i.e. the point is not on the curve.
y = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P));
if (negate_point) {
y = negate(y);
}
}
/// Compress a G2 point.
/// @notice Reverts with InvalidProof if the coefficients are not reduced
/// or if the point is not on the curve.
/// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1)
/// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i).
/// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0).
/// @param x0 The real part of the X coordinate.
/// @param x1 The imaginary poart of the X coordinate.
/// @param y0 The real part of the Y coordinate.
/// @param y1 The imaginary part of the Y coordinate.
/// @return c0 The first half of the compresed point (x0 with two signal bits).
/// @return c1 The second half of the compressed point (x1 unmodified).
function compress_g2(uint256 x0, uint256 x1, uint256 y0, uint256 y1)
internal view returns (uint256 c0, uint256 c1) {
if (x0 >= P || x1 >= P || y0 >= P || y1 >= P) {
// G2 point not in field.
revert ProofInvalid();
}
if ((x0 | x1 | y0 | y1) == 0) {
// Point at infinity
return (0, 0);
}
// Compute y^2
// Note: shadowing variables and scoping to avoid stack-to-deep.
uint256 y0_pos;
uint256 y1_pos;
{
uint256 n3ab = mulmod(mulmod(x0, x1, P), P-3, P);
uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P);
uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P);
y0_pos = addmod(FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P);
y1_pos = negate(addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P));
}
// Determine hint bit
// If this sqrt fails the x coordinate is not on the curve.
bool hint;
{
uint256 d = sqrt_Fp(addmod(mulmod(y0_pos, y0_pos, P), mulmod(y1_pos, y1_pos, P), P));
hint = !isSquare_Fp(mulmod(addmod(y0_pos, d, P), FRACTION_1_2_FP, P));
}
// Recover y
(y0_pos, y1_pos) = sqrt_Fp2(y0_pos, y1_pos, hint);
if (y0 == y0_pos && y1 == y1_pos) {
c0 = (x0 << 2) | (hint ? 2 : 0) | 0;
c1 = x1;
} else if (y0 == negate(y0_pos) && y1 == negate(y1_pos)) {
c0 = (x0 << 2) | (hint ? 2 : 0) | 1;
c1 = x1;
} else {
// G1 point not on curve.
revert ProofInvalid();
}
}
/// Decompress a G2 point.
/// @notice Reverts with InvalidProof if the input does not represent a valid point.
/// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1)
/// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i).
/// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0).
/// @param c0 The first half of the compresed point (x0 with two signal bits).
/// @param c1 The second half of the compressed point (x1 unmodified).
/// @return x0 The real part of the X coordinate.
/// @return x1 The imaginary poart of the X coordinate.
/// @return y0 The real part of the Y coordinate.
/// @return y1 The imaginary part of the Y coordinate.
function decompress_g2(uint256 c0, uint256 c1)
internal view returns (uint256 x0, uint256 x1, uint256 y0, uint256 y1) {
// Note that X = (0, 0) is not on the curve since 0³ + 3/(9 + i) is not a square.
// so we can use it to represent the point at infinity.
if (c0 == 0 && c1 == 0) {
// Point at infinity as encoded in EIP197.
return (0, 0, 0, 0);
}
bool negate_point = c0 & 1 == 1;
bool hint = c0 & 2 == 2;
x0 = c0 >> 2;
x1 = c1;
if (x0 >= P || x1 >= P) {
// G2 x0 or x1 coefficient not in field.
revert ProofInvalid();
}
uint256 n3ab = mulmod(mulmod(x0, x1, P), P-3, P);
uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P);
uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P);
y0 = addmod(FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P);
y1 = negate(addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P));
// Note: sqrt_Fp2 reverts if there is no solution, i.e. the point is not on the curve.
// Note: (X³ + 3/(9 + i)) is irreducible in Fp2, so y can not be zero.
// But y0 or y1 may still independently be zero.
(y0, y1) = sqrt_Fp2(y0, y1, hint);
if (negate_point) {
y0 = negate(y0);
y1 = negate(y1);
}
}
/// Compute the public input linear combination.
/// @notice Reverts with PublicInputNotInField if the input is not in the field.
/// @notice Computes the multi-scalar-multiplication of the public input
/// elements and the verification key including the constant term.
/// @param input The public inputs. These are elements of the scalar field Fr.
/// @param publicCommitments public inputs generated from pedersen commitments.
/// @param commitments The Pedersen commitments from the proof.
/// @return x The X coordinate of the resulting G1 point.
/// @return y The Y coordinate of the resulting G1 point.
function publicInputMSM(
uint256[32] calldata input,
uint256[1] memory publicCommitments,
uint256[2] memory commitments
)
internal view returns (uint256 x, uint256 y) {
// Note: The ECMUL precompile does not reject unreduced values, so we check this.
// Note: Unrolling this loop does not cost much extra in code-size, the bulk of the
// code-size is in the PUB_ constants.
// ECMUL has input (x, y, scalar) and output (x', y').
// ECADD has input (x1, y1, x2, y2) and output (x', y').
// We reduce commitments(if any) with constants as the first point argument to ECADD.
// We call them such that ecmul output is already in the second point
// argument to ECADD so we can have a tight loop.
bool success = true;
assembly ("memory-safe") {
let f := mload(0x40)
let g := add(f, 0x40)
let s
mstore(f, CONSTANT_X)
mstore(add(f, 0x20), CONSTANT_Y)
mstore(g, mload(commitments))
mstore(add(g, 0x20), mload(add(commitments, 0x20)))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_0_X)
mstore(add(g, 0x20), PUB_0_Y)
s := calldataload(input)
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_1_X)
mstore(add(g, 0x20), PUB_1_Y)
s := calldataload(add(input, 32))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_2_X)
mstore(add(g, 0x20), PUB_2_Y)
s := calldataload(add(input, 64))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_3_X)
mstore(add(g, 0x20), PUB_3_Y)
s := calldataload(add(input, 96))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_4_X)
mstore(add(g, 0x20), PUB_4_Y)
s := calldataload(add(input, 128))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_5_X)
mstore(add(g, 0x20), PUB_5_Y)
s := calldataload(add(input, 160))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_6_X)
mstore(add(g, 0x20), PUB_6_Y)
s := calldataload(add(input, 192))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_7_X)
mstore(add(g, 0x20), PUB_7_Y)
s := calldataload(add(input, 224))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_8_X)
mstore(add(g, 0x20), PUB_8_Y)
s := calldataload(add(input, 256))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_9_X)
mstore(add(g, 0x20), PUB_9_Y)
s := calldataload(add(input, 288))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_10_X)
mstore(add(g, 0x20), PUB_10_Y)
s := calldataload(add(input, 320))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_11_X)
mstore(add(g, 0x20), PUB_11_Y)
s := calldataload(add(input, 352))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_12_X)
mstore(add(g, 0x20), PUB_12_Y)
s := calldataload(add(input, 384))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_13_X)
mstore(add(g, 0x20), PUB_13_Y)
s := calldataload(add(input, 416))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_14_X)
mstore(add(g, 0x20), PUB_14_Y)
s := calldataload(add(input, 448))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_15_X)
mstore(add(g, 0x20), PUB_15_Y)
s := calldataload(add(input, 480))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_16_X)
mstore(add(g, 0x20), PUB_16_Y)
s := calldataload(add(input, 512))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_17_X)
mstore(add(g, 0x20), PUB_17_Y)
s := calldataload(add(input, 544))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_18_X)
mstore(add(g, 0x20), PUB_18_Y)
s := calldataload(add(input, 576))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_19_X)
mstore(add(g, 0x20), PUB_19_Y)
s := calldataload(add(input, 608))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_20_X)
mstore(add(g, 0x20), PUB_20_Y)
s := calldataload(add(input, 640))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_21_X)
mstore(add(g, 0x20), PUB_21_Y)
s := calldataload(add(input, 672))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_22_X)
mstore(add(g, 0x20), PUB_22_Y)
s := calldataload(add(input, 704))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_23_X)
mstore(add(g, 0x20), PUB_23_Y)
s := calldataload(add(input, 736))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_24_X)
mstore(add(g, 0x20), PUB_24_Y)
s := calldataload(add(input, 768))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_25_X)
mstore(add(g, 0x20), PUB_25_Y)
s := calldataload(add(input, 800))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_26_X)
mstore(add(g, 0x20), PUB_26_Y)
s := calldataload(add(input, 832))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_27_X)
mstore(add(g, 0x20), PUB_27_Y)
s := calldataload(add(input, 864))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_28_X)
mstore(add(g, 0x20), PUB_28_Y)
s := calldataload(add(input, 896))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_29_X)
mstore(add(g, 0x20), PUB_29_Y)
s := calldataload(add(input, 928))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_30_X)
mstore(add(g, 0x20), PUB_30_Y)
s := calldataload(add(input, 960))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_31_X)
mstore(add(g, 0x20), PUB_31_Y)
s := calldataload(add(input, 992))
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
mstore(g, PUB_32_X)
mstore(add(g, 0x20), PUB_32_Y)
s := mload(publicCommitments)
mstore(add(g, 0x40), s)
success := and(success, lt(s, R))
success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
x := mload(f)
y := mload(add(f, 0x20))
}
if (!success) {
// Either Public input not in field, or verification key invalid.
// We assume the contract is correctly generated, so the verification key is valid.
revert PublicInputNotInField();
}
}
/// Compress a proof.
/// @notice Will revert with InvalidProof if the curve points are invalid,
/// but does not verify the proof itself.
/// @param proof The uncompressed Groth16 proof. Elements are in the same order as for
/// verifyProof. I.e. Groth16 points (A, B, C) encoded as in EIP-197.
/// @param commitments Pedersen commitments from the proof.
/// @param commitmentPok proof of knowledge for the Pedersen commitments.
/// @return compressed The compressed proof. Elements are in the same order as for
/// verifyCompressedProof. I.e. points (A, B, C) in compressed format.
/// @return compressedCommitments compressed Pedersen commitments from the proof.
/// @return compressedCommitmentPok compressed proof of knowledge for the Pedersen commitments.
function compressProof(
uint256[8] calldata proof,
uint256[2] calldata commitments,
uint256[2] calldata commitmentPok
)
public view returns (
uint256[4] memory compressed,
uint256[1] memory compressedCommitments,
uint256 compressedCommitmentPok
) {
compressed[0] = compress_g1(proof[0], proof[1]);
(compressed[2], compressed[1]) = compress_g2(proof[3], proof[2], proof[5], proof[4]);
compressed[3] = compress_g1(proof[6], proof[7]);
compressedCommitments[0] = compress_g1(commitments[0], commitments[1]);
compressedCommitmentPok = compress_g1(commitmentPok[0], commitmentPok[1]);
}
/// Verify a Groth16 proof with compressed points.
/// @notice Reverts with InvalidProof if the proof is invalid or
/// with PublicInputNotInField the public input is not reduced.
/// @notice There is no return value. If the function does not revert, the
/// proof was successfully verified.
/// @param compressedProof the points (A, B, C) in compressed format
/// matching the output of compressProof.
/// @param compressedCommitments compressed Pedersen commitments from the proof.
/// @param compressedCommitmentPok compressed proof of knowledge for the Pedersen commitments.
/// @param input the public input field elements in the scalar field Fr.
/// Elements must be reduced.
function verifyCompressedProof(
uint256[4] calldata compressedProof,
uint256[1] calldata compressedCommitments,
uint256 compressedCommitmentPok,
uint256[32] calldata input
) public view {
uint256[1] memory publicCommitments;
uint256[2] memory commitments;
uint256[24] memory pairings;
{
(commitments[0], commitments[1]) = decompress_g1(compressedCommitments[0]);
(uint256 Px, uint256 Py) = decompress_g1(compressedCommitmentPok);
uint256[] memory publicAndCommitmentCommitted;
publicCommitments[0] = uint256(
sha256(
abi.encodePacked(
commitments[0],
commitments[1],
publicAndCommitmentCommitted
)
)
) % R;
// Commitments
pairings[ 0] = commitments[0];
pairings[ 1] = commitments[1];
pairings[ 2] = PEDERSEN_GSIGMANEG_X_1;
pairings[ 3] = PEDERSEN_GSIGMANEG_X_0;
pairings[ 4] = PEDERSEN_GSIGMANEG_Y_1;
pairings[ 5] = PEDERSEN_GSIGMANEG_Y_0;
pairings[ 6] = Px;
pairings[ 7] = Py;
pairings[ 8] = PEDERSEN_G_X_1;
pairings[ 9] = PEDERSEN_G_X_0;
pairings[10] = PEDERSEN_G_Y_1;
pairings[11] = PEDERSEN_G_Y_0;
// Verify pedersen commitments
bool success;
assembly ("memory-safe") {
let f := mload(0x40)
success := staticcall(gas(), PRECOMPILE_VERIFY, pairings, 0x180, f, 0x20)
success := and(success, mload(f))
}
if (!success) {
revert CommitmentInvalid();
}
}
{
(uint256 Ax, uint256 Ay) = decompress_g1(compressedProof[0]);
(uint256 Bx0, uint256 Bx1, uint256 By0, uint256 By1) = decompress_g2(compressedProof[2], compressedProof[1]);
(uint256 Cx, uint256 Cy) = decompress_g1(compressedProof[3]);
(uint256 Lx, uint256 Ly) = publicInputMSM(
input,
publicCommitments,
commitments
);
// Verify the pairing
// Note: The precompile expects the F2 coefficients in big-endian order.
// Note: The pairing precompile rejects unreduced values, so we won't check that here.
// e(A, B)
pairings[ 0] = Ax;
pairings[ 1] = Ay;
pairings[ 2] = Bx1;
pairings[ 3] = Bx0;
pairings[ 4] = By1;
pairings[ 5] = By0;
// e(C, -δ)
pairings[ 6] = Cx;
pairings[ 7] = Cy;
pairings[ 8] = DELTA_NEG_X_1;
pairings[ 9] = DELTA_NEG_X_0;
pairings[10] = DELTA_NEG_Y_1;
pairings[11] = DELTA_NEG_Y_0;
// e(α, -β)
pairings[12] = ALPHA_X;
pairings[13] = ALPHA_Y;
pairings[14] = BETA_NEG_X_1;
pairings[15] = BETA_NEG_X_0;
pairings[16] = BETA_NEG_Y_1;
pairings[17] = BETA_NEG_Y_0;
// e(L_pub, -γ)
pairings[18] = Lx;
pairings[19] = Ly;
pairings[20] = GAMMA_NEG_X_1;
pairings[21] = GAMMA_NEG_X_0;
pairings[22] = GAMMA_NEG_Y_1;
pairings[23] = GAMMA_NEG_Y_0;
// Check pairing equation.
bool success;
uint256[1] memory output;
assembly ("memory-safe") {
success := staticcall(gas(), PRECOMPILE_VERIFY, pairings, 0x300, output, 0x20)
}
if (!success || output[0] != 1) {
// Either proof or verification key invalid.
// We assume the contract is correctly generated, so the verification key is valid.
revert ProofInvalid();
}
}
}
/// Verify an uncompressed Groth16 proof.
/// @notice Reverts with InvalidProof if the proof is invalid or
/// with PublicInputNotInField the public input is not reduced.
/// @notice There is no return value. If the function does not revert, the
/// proof was successfully verified.
/// @param proof the points (A, B, C) in EIP-197 format matching the output
/// of compressProof.
/// @param commitments the Pedersen commitments from the proof.
/// @param commitmentPok the proof of knowledge for the Pedersen commitments.
/// @param input the public input field elements in the scalar field Fr.
/// Elements must be reduced.
function verifyProof(
uint256[8] calldata proof,
uint256[2] calldata commitments,
uint256[2] calldata commitmentPok,
uint256[32] calldata input
) public view {
// HashToField
uint256[1] memory publicCommitments;
uint256[] memory publicAndCommitmentCommitted;
publicCommitments[0] = uint256(
sha256(
abi.encodePacked(
commitments[0],
commitments[1],
publicAndCommitmentCommitted
)
)
) % R;
// Verify pedersen commitments
bool success;
assembly ("memory-safe") {
let f := mload(0x40)
calldatacopy(f, commitments, 0x40) // Copy Commitments
mstore(add(f, 0x40), PEDERSEN_GSIGMANEG_X_1)
mstore(add(f, 0x60), PEDERSEN_GSIGMANEG_X_0)
mstore(add(f, 0x80), PEDERSEN_GSIGMANEG_Y_1)
mstore(add(f, 0xa0), PEDERSEN_GSIGMANEG_Y_0)
calldatacopy(add(f, 0xc0), commitmentPok, 0x40)
mstore(add(f, 0x100), PEDERSEN_G_X_1)
mstore(add(f, 0x120), PEDERSEN_G_X_0)
mstore(add(f, 0x140), PEDERSEN_G_Y_1)
mstore(add(f, 0x160), PEDERSEN_G_Y_0)
success := staticcall(gas(), PRECOMPILE_VERIFY, f, 0x180, f, 0x20)
success := and(success, mload(f))
}
if (!success) {
revert CommitmentInvalid();
}
(uint256 x, uint256 y) = publicInputMSM(
input,
publicCommitments,
commitments
);
// Note: The precompile expects the F2 coefficients in big-endian order.
// Note: The pairing precompile rejects unreduced values, so we won't check that here.
assembly ("memory-safe") {
let f := mload(0x40) // Free memory pointer.
// Copy points (A, B, C) to memory. They are already in correct encoding.
// This is pairing e(A, B) and G1 of e(C, -δ).
calldatacopy(f, proof, 0x100)
// Complete e(C, -δ) and write e(α, -β), e(L_pub, -γ) to memory.
// OPT: This could be better done using a single codecopy, but
// Solidity (unlike standalone Yul) doesn't provide a way to
// to do this.
mstore(add(f, 0x100), DELTA_NEG_X_1)
mstore(add(f, 0x120), DELTA_NEG_X_0)
mstore(add(f, 0x140), DELTA_NEG_Y_1)
mstore(add(f, 0x160), DELTA_NEG_Y_0)
mstore(add(f, 0x180), ALPHA_X)
mstore(add(f, 0x1a0), ALPHA_Y)
mstore(add(f, 0x1c0), BETA_NEG_X_1)
mstore(add(f, 0x1e0), BETA_NEG_X_0)
mstore(add(f, 0x200), BETA_NEG_Y_1)
mstore(add(f, 0x220), BETA_NEG_Y_0)
mstore(add(f, 0x240), x)
mstore(add(f, 0x260), y)
mstore(add(f, 0x280), GAMMA_NEG_X_1)
mstore(add(f, 0x2a0), GAMMA_NEG_X_0)
mstore(add(f, 0x2c0), GAMMA_NEG_Y_1)
mstore(add(f, 0x2e0), GAMMA_NEG_Y_0)
// Check pairing equation.
success := staticcall(gas(), PRECOMPILE_VERIFY, f, 0x300, f, 0x20)
// Also check returned value (both are either 1 or 0).
success := and(success, mload(f))
}
if (!success) {
// Either proof or verification key invalid.
// We assume the contract is correctly generated, so the verification key is valid.
revert ProofInvalid();
}
}
}