@crisp-e3/contracts 0.0.1-test

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1886 @@
1
+ // SPDX-License-Identifier: LGPL-3.0-only
2
+ //
3
+ // This file is provided WITHOUT ANY WARRANTY;
4
+ // without even the implied warranty of MERCHANTABILITY
5
+ // or FITNESS FOR A PARTICULAR PURPOSE.
6
+ pragma solidity >=0.8.21;
7
+
8
+ uint256 constant N = 1048576;
9
+ uint256 constant LOG_N = 20;
10
+ uint256 constant NUMBER_OF_PUBLIC_INPUTS = 0;
11
+ library HonkVerificationKey {
12
+ function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) {
13
+ Honk.VerificationKey memory vk = Honk.VerificationKey({
14
+ circuitSize: uint256(1048576),
15
+ logCircuitSize: uint256(20),
16
+ publicInputsSize: uint256(0),
17
+ ql: Honk.G1Point({
18
+ x: uint256(0x10d36906c36e560297d7bc49c7661fbf50a476bea06e780c40d53a45c459599e),
19
+ y: uint256(0x1ed002ce45e75474d75d25ef1764280c6f1974b704ae77315baf31726978c175)
20
+ }),
21
+ qr: Honk.G1Point({
22
+ x: uint256(0x248d6c5782f08a14f794dae5832e8d4a5b42afebf0b248532d725e5d633e7215),
23
+ y: uint256(0x266b795b6cf4aa5fff2eb8c3e5a9b9780d6fd5d7170fc2b87f0e6aec8eaa9551)
24
+ }),
25
+ qo: Honk.G1Point({
26
+ x: uint256(0x2137d78eee77dfd53fc4bf2adeacef7b15a94cc050a06fb8f52e5a28e938a78e),
27
+ y: uint256(0x1b0888693ee93d8804044d8d5e8d950ed53fbb451da45863f6f768145c7d24b1)
28
+ }),
29
+ q4: Honk.G1Point({
30
+ x: uint256(0x00dceb0527695295e8bc90df562acf14d9bdb55e6250ca81034c7f56b9345985),
31
+ y: uint256(0x07963d90a20b5fbd22fcb35424b408b2ed41ab59fb2014027d9ec235a5f5905f)
32
+ }),
33
+ qm: Honk.G1Point({
34
+ x: uint256(0x11a037d3a9259c512f1039f1049fc7541cbbf315ddb90fe2869e6281c12023ef),
35
+ y: uint256(0x2d9966faba8461ee70d9db68e1d817fb21b15fb1e09e7476839dc6d07135cc23)
36
+ }),
37
+ qc: Honk.G1Point({
38
+ x: uint256(0x1219c094d86699487f09191b809a6be165c9b4808bbcadef831592295a1d7da6),
39
+ y: uint256(0x20b99b2dcadc856a88cc731f349aa47893a0569c4e747b4af0d7aae0dc9bdcda)
40
+ }),
41
+ qArith: Honk.G1Point({
42
+ x: uint256(0x147b6a393db982d0f0c7764012b6835c95bc0fa7926fda3f8bf684105282d388),
43
+ y: uint256(0x25b693e3f55d510c41dde8bd6b0d61b5c39db3888b55aeada5d7622f34254edf)
44
+ }),
45
+ qDeltaRange: Honk.G1Point({
46
+ x: uint256(0x2c9e01c750140eb161d24b9ee0fc8984acb60bf4e395b8e6ebbaa91c1be174e9),
47
+ y: uint256(0x1bc18c861d47b1f9a988e2e3d3d7a0a6252dc986d614fcca0e4f8ac7457c0892)
48
+ }),
49
+ qElliptic: Honk.G1Point({
50
+ x: uint256(0x088a610ed8e4090aefcac9ab7277b21fdfb04ce437eb458bdb3696bff53c1cd0),
51
+ y: uint256(0x2c5353cedecc1e5f4d84b3ae7f3711e36deebc3071201975bf0fecc655545f5e)
52
+ }),
53
+ qAux: Honk.G1Point({
54
+ x: uint256(0x1baad7c6c4a0f76d721636e31a5b5c28c1062d21f2148339266343c8c1d38996),
55
+ y: uint256(0x1eb16da6aed4aa1a55302d002d233fd4dbb885c96431a494683dc97a9275f43b)
56
+ }),
57
+ qLookup: Honk.G1Point({
58
+ x: uint256(0x1d64341216e323f076ac53aa06192392677f44b67b6947dd6a0a1490fb32a083),
59
+ y: uint256(0x28d02cea9cc379ace2ae8779011e247ddc4213ef69895a8e634f425844107141)
60
+ }),
61
+ qPoseidon2External: Honk.G1Point({
62
+ x: uint256(0x1e0266510d883b6379b58686bd63642eae499ea68a706cc9e70c14c9c0e8536a),
63
+ y: uint256(0x11491f77ce7999d3618f0bac66ef7c4f1ed122f862d1c1465e71ce1262c4d7bd)
64
+ }),
65
+ qPoseidon2Internal: Honk.G1Point({
66
+ x: uint256(0x22b036a9ca9be4e71743a125c08648843a55f2cf070dafbbda117ca79e071ad5),
67
+ y: uint256(0x12fd44e4853d17621023115fbb658d1150aa85fae5a020f07eddd2215812b2f8)
68
+ }),
69
+ s1: Honk.G1Point({
70
+ x: uint256(0x1a18a989b31c09eea3da5ae3faa7680ef410474de274210f9c39ebfe6a798c7d),
71
+ y: uint256(0x2108883b1e5364123ffd7c32161f9228987ee6aa31ba3c579765f6096579b8e0)
72
+ }),
73
+ s2: Honk.G1Point({
74
+ x: uint256(0x26ddc6264203943a7c31aed2afb7d53fe32a7a38249897fc2a8771c65c414096),
75
+ y: uint256(0x0249864a3e742b55386cf6ab4051915fa677a11885e4b0eb04d20d38e9632afe)
76
+ }),
77
+ s3: Honk.G1Point({
78
+ x: uint256(0x250a1f5dd0a1e0ec7ab855db5863a25a567877ef1bd1c19e5bc1ccdb733b2956),
79
+ y: uint256(0x2cc285a8d6a6e91d142e2a941e23c0a7aee361316975b582e0370e46c4939d9f)
80
+ }),
81
+ s4: Honk.G1Point({
82
+ x: uint256(0x0fc8be2a322d1c45d22b210211992629be0450555321e4a55c2cf2d8f5498c6c),
83
+ y: uint256(0x1091f3056debdf6a98ab5e4c6df48dc4fded590c78788baf2b394ff6005df6f6)
84
+ }),
85
+ t1: Honk.G1Point({
86
+ x: uint256(0x1bf7da4add7c858eb94b75f2e78fbd89c84f5fa43824a0d5534173872ee099c2),
87
+ y: uint256(0x1b35fa2a35673699ee1cb260d9e6c4be79b26d488c26dc2531194e43c8f747ea)
88
+ }),
89
+ t2: Honk.G1Point({
90
+ x: uint256(0x16bf79791869cec464180d5322eeaaef18fed6dc10c3e64e314c04d85c3faece),
91
+ y: uint256(0x2e2ec6341669b5b975e25e465af5d9e40533d5ac173554df19daed27f66c36ff)
92
+ }),
93
+ t3: Honk.G1Point({
94
+ x: uint256(0x150253026f1b985165783c2f4ee1df612c826dda543d06d34711b965730ab69e),
95
+ y: uint256(0x0c4062ebcca21d81273b9c58d64447e4ee4d55effa8cbc8fdbd6a76bc3092264)
96
+ }),
97
+ t4: Honk.G1Point({
98
+ x: uint256(0x159f2541ce446c6d59ea3f06be91ec9f47c9c82f3e4fd10696511efaff4121fa),
99
+ y: uint256(0x15f873b33ec9467e1f0c4fb3a0b59a6fcd6f3480515f1ff5506c48f0c521f00f)
100
+ }),
101
+ id1: Honk.G1Point({
102
+ x: uint256(0x0c02225e1d329e09a738ff6a3d1f2eefee2d9c2446f748430a4c9e3db3af493e),
103
+ y: uint256(0x22970a47d992efe75e1a9ae8c48617327596f0b9d1536898b516e9dc0d92e351)
104
+ }),
105
+ id2: Honk.G1Point({
106
+ x: uint256(0x025b15baf18a3565112553243b581ab7c1ef2bb7b6cecb6fd3dbcb494f131c1b),
107
+ y: uint256(0x0784d79e6aa29c9c710670f3e62df4c3bf99a73a85bb255a009d5566da762426)
108
+ }),
109
+ id3: Honk.G1Point({
110
+ x: uint256(0x2f0d8870dfa4fe4f0bcf1e8a8b69f750b15e8315a76dea5965fe82d369e00ffd),
111
+ y: uint256(0x0b8a634aa17cb7e29434a052392f7c40e013e37f2dfdcce584ecaa23a3508c89)
112
+ }),
113
+ id4: Honk.G1Point({
114
+ x: uint256(0x126143c034d5218ac854d254492806967ae0d23ec5684d341d730cd4dda79f08),
115
+ y: uint256(0x1c00c21435080bb1e5b99a42eba611eda1328b0e38e92fcd955d81e96b9b2917)
116
+ }),
117
+ lagrangeFirst: Honk.G1Point({
118
+ x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001),
119
+ y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002)
120
+ }),
121
+ lagrangeLast: Honk.G1Point({
122
+ x: uint256(0x1120d97a81a9c90c251f46b1ec3998bc67e7978323aebf46551a536bf4d0f167),
123
+ y: uint256(0x26d1e132ba53edea7d5e8aa5b21067176a3cf0ba74257f595e17bf0db56a98de)
124
+ })
125
+ });
126
+ return vk;
127
+ }
128
+ }
129
+
130
+ pragma solidity ^0.8.27;
131
+
132
+ type Fr is uint256;
133
+
134
+ using { add as + } for Fr global;
135
+ using { sub as - } for Fr global;
136
+ using { mul as * } for Fr global;
137
+ using { exp as ^ } for Fr global;
138
+ using { notEqual as != } for Fr global;
139
+ using { equal as == } for Fr global;
140
+
141
+ uint256 constant MODULUS =
142
+ 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order
143
+
144
+ Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1);
145
+
146
+ // Instantiation
147
+ library FrLib
148
+ {
149
+ function from(uint256 value) internal pure returns(Fr)
150
+ {
151
+ return Fr.wrap(value % MODULUS);
152
+ }
153
+
154
+ function fromBytes32(bytes32 value) internal pure returns(Fr)
155
+ {
156
+ return Fr.wrap(uint256(value) % MODULUS);
157
+ }
158
+
159
+ function toBytes32(Fr value) internal pure returns(bytes32)
160
+ {
161
+ return bytes32(Fr.unwrap(value));
162
+ }
163
+
164
+ function invert(Fr value) internal view returns(Fr)
165
+ {
166
+ uint256 v = Fr.unwrap(value);
167
+ uint256 result;
168
+
169
+ // Call the modexp precompile to invert in the field
170
+ assembly
171
+ {
172
+ let free := mload(0x40)
173
+ mstore(free, 0x20)
174
+ mstore(add(free, 0x20), 0x20)
175
+ mstore(add(free, 0x40), 0x20)
176
+ mstore(add(free, 0x60), v)
177
+ mstore(add(free, 0x80), sub(MODULUS, 2))
178
+ mstore(add(free, 0xa0), MODULUS)
179
+ let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)
180
+ if iszero(success) {
181
+ revert(0, 0)
182
+ }
183
+ result := mload(0x00)
184
+ }
185
+
186
+ return Fr.wrap(result);
187
+ }
188
+
189
+ function pow(Fr base, uint256 v) internal view returns(Fr)
190
+ {
191
+ uint256 b = Fr.unwrap(base);
192
+ uint256 result;
193
+
194
+ // Call the modexp precompile to invert in the field
195
+ assembly
196
+ {
197
+ let free := mload(0x40)
198
+ mstore(free, 0x20)
199
+ mstore(add(free, 0x20), 0x20)
200
+ mstore(add(free, 0x40), 0x20)
201
+ mstore(add(free, 0x60), b)
202
+ mstore(add(free, 0x80), v)
203
+ mstore(add(free, 0xa0), MODULUS)
204
+ let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)
205
+ if iszero(success) {
206
+ revert(0, 0)
207
+ }
208
+ result := mload(0x00)
209
+ }
210
+
211
+ return Fr.wrap(result);
212
+ }
213
+
214
+ function div(Fr numerator, Fr denominator) internal view returns(Fr)
215
+ {
216
+ return numerator * invert(denominator);
217
+ }
218
+
219
+ function sqr(Fr value) internal pure returns (Fr) {
220
+ return value * value;
221
+ }
222
+
223
+ function unwrap(Fr value) internal pure returns (uint256) {
224
+ return Fr.unwrap(value);
225
+ }
226
+
227
+ function neg(Fr value) internal pure returns (Fr) {
228
+ return Fr.wrap(MODULUS - Fr.unwrap(value));
229
+ }
230
+ }
231
+
232
+ // Free functions
233
+ function add(Fr a, Fr b) pure returns(Fr)
234
+ {
235
+ return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS));
236
+ }
237
+
238
+ function mul(Fr a, Fr b) pure returns(Fr)
239
+ {
240
+ return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS));
241
+ }
242
+
243
+ function sub(Fr a, Fr b) pure returns(Fr)
244
+ {
245
+ return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS));
246
+ }
247
+
248
+ function exp(Fr base, Fr exponent) pure returns(Fr)
249
+ {
250
+ if (Fr.unwrap(exponent) == 0) return Fr.wrap(1);
251
+
252
+ for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) {
253
+ base = base * base;
254
+ }
255
+ return base;
256
+ }
257
+
258
+ function notEqual(Fr a, Fr b) pure returns(bool)
259
+ {
260
+ return Fr.unwrap(a) != Fr.unwrap(b);
261
+ }
262
+
263
+ function equal(Fr a, Fr b) pure returns(bool)
264
+ {
265
+ return Fr.unwrap(a) == Fr.unwrap(b);
266
+ }
267
+
268
+ uint256 constant CONST_PROOF_SIZE_LOG_N = 28;
269
+
270
+ uint256 constant NUMBER_OF_SUBRELATIONS = 26;
271
+ uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8;
272
+ uint256 constant NUMBER_OF_ENTITIES = 40;
273
+ uint256 constant NUMBER_UNSHIFTED = 35;
274
+ uint256 constant NUMBER_TO_BE_SHIFTED = 5;
275
+
276
+ // Alphas are used as relation separators so there should be NUMBER_OF_SUBRELATIONS - 1
277
+ uint256 constant NUMBER_OF_ALPHAS = 25;
278
+
279
+ // Prime field order
280
+ uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q
281
+ uint256 constant P = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order, F_r
282
+
283
+ // ENUM FOR WIRES
284
+ enum WIRE {
285
+ Q_M,
286
+ Q_C,
287
+ Q_L,
288
+ Q_R,
289
+ Q_O,
290
+ Q_4,
291
+ Q_LOOKUP,
292
+ Q_ARITH,
293
+ Q_RANGE,
294
+ Q_ELLIPTIC,
295
+ Q_AUX,
296
+ Q_POSEIDON2_EXTERNAL,
297
+ Q_POSEIDON2_INTERNAL,
298
+ SIGMA_1,
299
+ SIGMA_2,
300
+ SIGMA_3,
301
+ SIGMA_4,
302
+ ID_1,
303
+ ID_2,
304
+ ID_3,
305
+ ID_4,
306
+ TABLE_1,
307
+ TABLE_2,
308
+ TABLE_3,
309
+ TABLE_4,
310
+ LAGRANGE_FIRST,
311
+ LAGRANGE_LAST,
312
+ W_L,
313
+ W_R,
314
+ W_O,
315
+ W_4,
316
+ Z_PERM,
317
+ LOOKUP_INVERSES,
318
+ LOOKUP_READ_COUNTS,
319
+ LOOKUP_READ_TAGS,
320
+ W_L_SHIFT,
321
+ W_R_SHIFT,
322
+ W_O_SHIFT,
323
+ W_4_SHIFT,
324
+ Z_PERM_SHIFT
325
+ }
326
+
327
+ library Honk {
328
+ struct G1Point {
329
+ uint256 x;
330
+ uint256 y;
331
+ }
332
+
333
+ struct G1ProofPoint {
334
+ uint256 x_0;
335
+ uint256 x_1;
336
+ uint256 y_0;
337
+ uint256 y_1;
338
+ }
339
+
340
+ struct VerificationKey {
341
+ // Misc Params
342
+ uint256 circuitSize;
343
+ uint256 logCircuitSize;
344
+ uint256 publicInputsSize;
345
+ // Selectors
346
+ G1Point qm;
347
+ G1Point qc;
348
+ G1Point ql;
349
+ G1Point qr;
350
+ G1Point qo;
351
+ G1Point q4;
352
+ G1Point qLookup; // Lookup
353
+ G1Point qArith; // Arithmetic widget
354
+ G1Point qDeltaRange; // Delta Range sort
355
+ G1Point qAux; // Auxillary
356
+ G1Point qElliptic; // Auxillary
357
+ G1Point qPoseidon2External;
358
+ G1Point qPoseidon2Internal;
359
+ // Copy cnstraints
360
+ G1Point s1;
361
+ G1Point s2;
362
+ G1Point s3;
363
+ G1Point s4;
364
+ // Copy identity
365
+ G1Point id1;
366
+ G1Point id2;
367
+ G1Point id3;
368
+ G1Point id4;
369
+ // Precomputed lookup table
370
+ G1Point t1;
371
+ G1Point t2;
372
+ G1Point t3;
373
+ G1Point t4;
374
+ // Fixed first and last
375
+ G1Point lagrangeFirst;
376
+ G1Point lagrangeLast;
377
+ }
378
+
379
+ struct RelationParameters {
380
+ // challenges
381
+ Fr eta;
382
+ Fr etaTwo;
383
+ Fr etaThree;
384
+ Fr beta;
385
+ Fr gamma;
386
+ // derived
387
+ Fr publicInputsDelta;
388
+ }
389
+
390
+
391
+ struct Proof {
392
+ // Free wires
393
+ Honk.G1ProofPoint w1;
394
+ Honk.G1ProofPoint w2;
395
+ Honk.G1ProofPoint w3;
396
+ Honk.G1ProofPoint w4;
397
+ // Lookup helpers - Permutations
398
+ Honk.G1ProofPoint zPerm;
399
+ // Lookup helpers - logup
400
+ Honk.G1ProofPoint lookupReadCounts;
401
+ Honk.G1ProofPoint lookupReadTags;
402
+ Honk.G1ProofPoint lookupInverses;
403
+ // Sumcheck
404
+ Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates;
405
+ Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations;
406
+ // Shplemini
407
+ Honk.G1ProofPoint[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms;
408
+ Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations;
409
+ Honk.G1ProofPoint shplonkQ;
410
+ Honk.G1ProofPoint kzgQuotient;
411
+ }
412
+ }
413
+
414
+ // Transcript library to generate fiat shamir challenges
415
+ struct Transcript {
416
+ // Oink
417
+ Honk.RelationParameters relationParameters;
418
+ Fr[NUMBER_OF_ALPHAS] alphas;
419
+ Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges;
420
+ // Sumcheck
421
+ Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges;
422
+ // Gemini
423
+ Fr rho;
424
+ Fr geminiR;
425
+ // Shplonk
426
+ Fr shplonkNu;
427
+ Fr shplonkZ;
428
+ }
429
+
430
+ library TranscriptLib {
431
+ function generateTranscript(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 circuitSize, uint256 publicInputsSize, uint256 pubInputsOffset)
432
+ internal
433
+ pure
434
+ returns (Transcript memory t)
435
+ {
436
+ Fr previousChallenge;
437
+ (t.relationParameters, previousChallenge) =
438
+ generateRelationParametersChallenges(proof, publicInputs, circuitSize, publicInputsSize, pubInputsOffset, previousChallenge);
439
+
440
+ (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof);
441
+
442
+ (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge);
443
+
444
+ (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge);
445
+
446
+ (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge);
447
+
448
+ (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge);
449
+
450
+ (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge);
451
+
452
+ (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge);
453
+
454
+ return t;
455
+ }
456
+
457
+ function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) {
458
+ uint256 challengeU256 = uint256(Fr.unwrap(challenge));
459
+ uint256 lo = challengeU256 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
460
+ uint256 hi = challengeU256 >> 128;
461
+ first = FrLib.fromBytes32(bytes32(lo));
462
+ second = FrLib.fromBytes32(bytes32(hi));
463
+ }
464
+
465
+ function generateRelationParametersChallenges(
466
+ Honk.Proof memory proof,
467
+ bytes32[] calldata publicInputs,
468
+ uint256 circuitSize,
469
+ uint256 publicInputsSize,
470
+ uint256 pubInputsOffset,
471
+ Fr previousChallenge
472
+ ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) {
473
+ (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) =
474
+ generateEtaChallenge(proof, publicInputs, circuitSize, publicInputsSize, pubInputsOffset);
475
+
476
+ (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof);
477
+
478
+ }
479
+
480
+ function generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 circuitSize, uint256 publicInputsSize, uint256 pubInputsOffset)
481
+ internal
482
+ pure
483
+ returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge)
484
+ {
485
+ bytes32[] memory round0 = new bytes32[](3 + publicInputsSize + 12);
486
+ round0[0] = bytes32(circuitSize);
487
+ round0[1] = bytes32(publicInputsSize);
488
+ round0[2] = bytes32(pubInputsOffset);
489
+ for (uint256 i = 0; i < publicInputsSize; i++) {
490
+ round0[3 + i] = bytes32(publicInputs[i]);
491
+ }
492
+
493
+ // Create the first challenge
494
+ // Note: w4 is added to the challenge later on
495
+ round0[3 + publicInputsSize] = bytes32(proof.w1.x_0);
496
+ round0[3 + publicInputsSize + 1] = bytes32(proof.w1.x_1);
497
+ round0[3 + publicInputsSize + 2] = bytes32(proof.w1.y_0);
498
+ round0[3 + publicInputsSize + 3] = bytes32(proof.w1.y_1);
499
+ round0[3 + publicInputsSize + 4] = bytes32(proof.w2.x_0);
500
+ round0[3 + publicInputsSize + 5] = bytes32(proof.w2.x_1);
501
+ round0[3 + publicInputsSize + 6] = bytes32(proof.w2.y_0);
502
+ round0[3 + publicInputsSize + 7] = bytes32(proof.w2.y_1);
503
+ round0[3 + publicInputsSize + 8] = bytes32(proof.w3.x_0);
504
+ round0[3 + publicInputsSize + 9] = bytes32(proof.w3.x_1);
505
+ round0[3 + publicInputsSize + 10] = bytes32(proof.w3.y_0);
506
+ round0[3 + publicInputsSize + 11] = bytes32(proof.w3.y_1);
507
+
508
+ previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0)));
509
+ (eta, etaTwo) = splitChallenge(previousChallenge);
510
+ previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))));
511
+ Fr unused;
512
+ (etaThree, unused) = splitChallenge(previousChallenge);
513
+ }
514
+
515
+ function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof)
516
+ internal
517
+ pure
518
+ returns (Fr beta, Fr gamma, Fr nextPreviousChallenge)
519
+ {
520
+ bytes32[13] memory round1;
521
+ round1[0] = FrLib.toBytes32(previousChallenge);
522
+ round1[1] = bytes32(proof.lookupReadCounts.x_0);
523
+ round1[2] = bytes32(proof.lookupReadCounts.x_1);
524
+ round1[3] = bytes32(proof.lookupReadCounts.y_0);
525
+ round1[4] = bytes32(proof.lookupReadCounts.y_1);
526
+ round1[5] = bytes32(proof.lookupReadTags.x_0);
527
+ round1[6] = bytes32(proof.lookupReadTags.x_1);
528
+ round1[7] = bytes32(proof.lookupReadTags.y_0);
529
+ round1[8] = bytes32(proof.lookupReadTags.y_1);
530
+ round1[9] = bytes32(proof.w4.x_0);
531
+ round1[10] = bytes32(proof.w4.x_1);
532
+ round1[11] = bytes32(proof.w4.y_0);
533
+ round1[12] = bytes32(proof.w4.y_1);
534
+
535
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1)));
536
+ (beta, gamma) = splitChallenge(nextPreviousChallenge);
537
+ }
538
+
539
+ // Alpha challenges non-linearise the gate contributions
540
+ function generateAlphaChallenges(Fr previousChallenge, Honk.Proof memory proof)
541
+ internal
542
+ pure
543
+ returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge)
544
+ {
545
+ // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup
546
+ uint256[9] memory alpha0;
547
+ alpha0[0] = Fr.unwrap(previousChallenge);
548
+ alpha0[1] = proof.lookupInverses.x_0;
549
+ alpha0[2] = proof.lookupInverses.x_1;
550
+ alpha0[3] = proof.lookupInverses.y_0;
551
+ alpha0[4] = proof.lookupInverses.y_1;
552
+ alpha0[5] = proof.zPerm.x_0;
553
+ alpha0[6] = proof.zPerm.x_1;
554
+ alpha0[7] = proof.zPerm.y_0;
555
+ alpha0[8] = proof.zPerm.y_1;
556
+
557
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0)));
558
+ (alphas[0], alphas[1]) = splitChallenge(nextPreviousChallenge);
559
+
560
+ for (uint256 i = 1; i < NUMBER_OF_ALPHAS / 2; i++) {
561
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(nextPreviousChallenge))));
562
+ (alphas[2 * i], alphas[2 * i + 1]) = splitChallenge(nextPreviousChallenge);
563
+ }
564
+ if (((NUMBER_OF_ALPHAS & 1) == 1) && (NUMBER_OF_ALPHAS > 2)) {
565
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(nextPreviousChallenge))));
566
+ Fr unused;
567
+ (alphas[NUMBER_OF_ALPHAS - 1], unused) = splitChallenge(nextPreviousChallenge);
568
+ }
569
+ }
570
+
571
+ function generateGateChallenges(Fr previousChallenge)
572
+ internal
573
+ pure
574
+ returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge)
575
+ {
576
+ for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) {
577
+ previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))));
578
+ Fr unused;
579
+ (gateChallenges[i], unused) = splitChallenge(previousChallenge);
580
+ }
581
+ nextPreviousChallenge = previousChallenge;
582
+ }
583
+
584
+ function generateSumcheckChallenges(Honk.Proof memory proof, Fr prevChallenge)
585
+ internal
586
+ pure
587
+ returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge)
588
+ {
589
+ for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) {
590
+ Fr[BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal;
591
+ univariateChal[0] = prevChallenge;
592
+
593
+ for (uint256 j = 0; j < BATCHED_RELATION_PARTIAL_LENGTH; j++) {
594
+ univariateChal[j + 1] = proof.sumcheckUnivariates[i][j];
595
+ }
596
+ prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal)));
597
+ Fr unused;
598
+ (sumcheckChallenges[i], unused) = splitChallenge(prevChallenge);
599
+ }
600
+ nextPreviousChallenge = prevChallenge;
601
+ }
602
+
603
+ function generateRhoChallenge(Honk.Proof memory proof, Fr prevChallenge)
604
+ internal
605
+ pure
606
+ returns (Fr rho, Fr nextPreviousChallenge)
607
+ {
608
+ Fr[NUMBER_OF_ENTITIES + 1] memory rhoChallengeElements;
609
+ rhoChallengeElements[0] = prevChallenge;
610
+
611
+ for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) {
612
+ rhoChallengeElements[i + 1] = proof.sumcheckEvaluations[i];
613
+ }
614
+
615
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements)));
616
+ Fr unused;
617
+ (rho, unused) = splitChallenge(nextPreviousChallenge);
618
+ }
619
+
620
+ function generateGeminiRChallenge(Honk.Proof memory proof, Fr prevChallenge)
621
+ internal
622
+ pure
623
+ returns (Fr geminiR, Fr nextPreviousChallenge)
624
+ {
625
+ uint256[(CONST_PROOF_SIZE_LOG_N - 1) * 4 + 1] memory gR;
626
+ gR[0] = Fr.unwrap(prevChallenge);
627
+
628
+ for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; i++) {
629
+ gR[1 + i * 4] = proof.geminiFoldComms[i].x_0;
630
+ gR[2 + i * 4] = proof.geminiFoldComms[i].x_1;
631
+ gR[3 + i * 4] = proof.geminiFoldComms[i].y_0;
632
+ gR[4 + i * 4] = proof.geminiFoldComms[i].y_1;
633
+ }
634
+
635
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR)));
636
+ Fr unused;
637
+ (geminiR, unused) = splitChallenge(nextPreviousChallenge);
638
+ }
639
+
640
+ function generateShplonkNuChallenge(Honk.Proof memory proof, Fr prevChallenge)
641
+ internal
642
+ pure
643
+ returns (Fr shplonkNu, Fr nextPreviousChallenge)
644
+ {
645
+ uint256[(CONST_PROOF_SIZE_LOG_N) + 1] memory shplonkNuChallengeElements;
646
+ shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge);
647
+
648
+ for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) {
649
+ shplonkNuChallengeElements[i + 1] = Fr.unwrap(proof.geminiAEvaluations[i]);
650
+ }
651
+
652
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements)));
653
+ Fr unused;
654
+ (shplonkNu, unused) = splitChallenge(nextPreviousChallenge);
655
+ }
656
+
657
+ function generateShplonkZChallenge(Honk.Proof memory proof, Fr prevChallenge)
658
+ internal
659
+ pure
660
+ returns (Fr shplonkZ, Fr nextPreviousChallenge)
661
+ {
662
+ uint256[5] memory shplonkZChallengeElements;
663
+ shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge);
664
+
665
+ shplonkZChallengeElements[1] = proof.shplonkQ.x_0;
666
+ shplonkZChallengeElements[2] = proof.shplonkQ.x_1;
667
+ shplonkZChallengeElements[3] = proof.shplonkQ.y_0;
668
+ shplonkZChallengeElements[4] = proof.shplonkQ.y_1;
669
+
670
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements)));
671
+ Fr unused;
672
+ (shplonkZ, unused) = splitChallenge(nextPreviousChallenge);
673
+ }
674
+
675
+ function loadProof(bytes calldata proof) internal pure returns (Honk.Proof memory p) {
676
+ // Commitments
677
+ p.w1 = bytesToG1ProofPoint(proof[0x0:0x80]);
678
+
679
+ p.w2 = bytesToG1ProofPoint(proof[0x80:0x100]);
680
+ p.w3 = bytesToG1ProofPoint(proof[0x100:0x180]);
681
+
682
+ // Lookup / Permutation Helper Commitments
683
+ p.lookupReadCounts = bytesToG1ProofPoint(proof[0x180:0x200]);
684
+ p.lookupReadTags = bytesToG1ProofPoint(proof[0x200:0x280]);
685
+ p.w4 = bytesToG1ProofPoint(proof[0x280:0x300]);
686
+ p.lookupInverses = bytesToG1ProofPoint(proof[0x300:0x380]);
687
+ p.zPerm = bytesToG1ProofPoint(proof[0x380:0x400]);
688
+ uint256 boundary = 0x400;
689
+
690
+ // Sumcheck univariates
691
+ for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) {
692
+ for (uint256 j = 0; j < BATCHED_RELATION_PARTIAL_LENGTH; j++) {
693
+ p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + 0x20]);
694
+ boundary += 0x20;
695
+ }
696
+ }
697
+ // Sumcheck evaluations
698
+ for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) {
699
+ p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + 0x20]);
700
+ boundary += 0x20;
701
+ }
702
+
703
+ // Gemini
704
+ // Read gemini fold univariates
705
+ for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; i++) {
706
+ p.geminiFoldComms[i] = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]);
707
+ boundary += 0x80;
708
+ }
709
+
710
+ // Read gemini a evaluations
711
+ for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) {
712
+ p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + 0x20]);
713
+ boundary += 0x20;
714
+ }
715
+
716
+ // Shplonk
717
+ p.shplonkQ = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]);
718
+ boundary = boundary + 0x80;
719
+ // KZG
720
+ p.kzgQuotient = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]);
721
+ }
722
+ }
723
+
724
+
725
+ // Fr utility
726
+
727
+ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) {
728
+ require(proofSection.length == 0x20, "invalid bytes scalar");
729
+ scalar = FrLib.fromBytes32(bytes32(proofSection));
730
+ }
731
+
732
+ // EC Point utilities
733
+ function convertProofPoint(Honk.G1ProofPoint memory input) pure returns (Honk.G1Point memory) {
734
+ return Honk.G1Point({x: input.x_0 | (input.x_1 << 136), y: input.y_0 | (input.y_1 << 136)});
735
+ }
736
+
737
+ function bytesToG1ProofPoint(bytes calldata proofSection) pure returns (Honk.G1ProofPoint memory point) {
738
+ require(proofSection.length == 0x80, "invalid bytes point");
739
+ point = Honk.G1ProofPoint({
740
+ x_0: uint256(bytes32(proofSection[0x00:0x20])),
741
+ x_1: uint256(bytes32(proofSection[0x20:0x40])),
742
+ y_0: uint256(bytes32(proofSection[0x40:0x60])),
743
+ y_1: uint256(bytes32(proofSection[0x60:0x80]))
744
+ });
745
+ }
746
+
747
+ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) {
748
+ point.y = (Q - point.y) % Q;
749
+ return point;
750
+ }
751
+
752
+ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool) {
753
+ bytes memory input = abi.encodePacked(
754
+ rhs.x,
755
+ rhs.y,
756
+ // Fixed G1 point
757
+ uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2),
758
+ uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed),
759
+ uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b),
760
+ uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa),
761
+ lhs.x,
762
+ lhs.y,
763
+ // G1 point from VK
764
+ uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1),
765
+ uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0),
766
+ uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4),
767
+ uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55)
768
+ );
769
+
770
+ (bool success, bytes memory result) = address(0x08).staticcall(input);
771
+ bool decodedResult = abi.decode(result, (bool));
772
+ return success && decodedResult;
773
+ }
774
+
775
+
776
+ library RelationsLib {
777
+ Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17)
778
+
779
+ function accumulateRelationEvaluations(
780
+ Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations,
781
+ Honk.RelationParameters memory rp,
782
+ Fr[NUMBER_OF_ALPHAS] memory alphas,
783
+ Fr powPartialEval
784
+ ) internal pure returns (Fr accumulator) {
785
+ Fr[NUMBER_OF_SUBRELATIONS] memory evaluations;
786
+
787
+ // Accumulate all relations in Ultra Honk - each with varying number of subrelations
788
+ accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval);
789
+ accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval);
790
+ accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval);
791
+ accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval);
792
+ accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval);
793
+ accumulateAuxillaryRelation(purportedEvaluations, rp, evaluations, powPartialEval);
794
+ accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval);
795
+ accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval);
796
+ // batch the subrelations with the alpha challenges to obtain the full honk relation
797
+ accumulator = scaleAndBatchSubrelations(evaluations, alphas);
798
+ }
799
+
800
+ /**
801
+ * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids
802
+ * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code
803
+ * editors, and thus is noisy.
804
+ */
805
+ function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) {
806
+ return p[uint256(_wire)];
807
+ }
808
+
809
+ uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;
810
+ /**
811
+ * Ultra Arithmetic Relation
812
+ *
813
+ */
814
+ function accumulateArithmeticRelation(
815
+ Fr[NUMBER_OF_ENTITIES] memory p,
816
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
817
+ Fr domainSep
818
+ ) internal pure {
819
+ // Relation 0
820
+ Fr q_arith = wire(p, WIRE.Q_ARITH);
821
+ {
822
+ Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P);
823
+
824
+ Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half;
825
+ accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R))
826
+ + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C);
827
+ accum = accum + (q_arith - Fr.wrap(1)) * wire(p, WIRE.W_4_SHIFT);
828
+ accum = accum * q_arith;
829
+ accum = accum * domainSep;
830
+ evals[0] = accum;
831
+ }
832
+
833
+ // Relation 1
834
+ {
835
+ Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M);
836
+ accum = accum * (q_arith - Fr.wrap(2));
837
+ accum = accum * (q_arith - Fr.wrap(1));
838
+ accum = accum * q_arith;
839
+ accum = accum * domainSep;
840
+ evals[1] = accum;
841
+ }
842
+ }
843
+
844
+ function accumulatePermutationRelation(
845
+ Fr[NUMBER_OF_ENTITIES] memory p,
846
+ Honk.RelationParameters memory rp,
847
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
848
+ Fr domainSep
849
+ ) internal pure {
850
+ Fr grand_product_numerator;
851
+ Fr grand_product_denominator;
852
+
853
+ {
854
+ Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma;
855
+ num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma);
856
+ num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma);
857
+ num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma);
858
+
859
+ grand_product_numerator = num;
860
+ }
861
+ {
862
+ Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma;
863
+ den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma);
864
+ den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma);
865
+ den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma);
866
+
867
+ grand_product_denominator = den;
868
+ }
869
+
870
+ // Contribution 2
871
+ {
872
+ Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator;
873
+
874
+ acc = acc
875
+ - (
876
+ (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta))
877
+ * grand_product_denominator
878
+ );
879
+ acc = acc * domainSep;
880
+ evals[2] = acc;
881
+ }
882
+
883
+ // Contribution 3
884
+ {
885
+ Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep;
886
+ evals[3] = acc;
887
+ }
888
+ }
889
+
890
+ function accumulateLogDerivativeLookupRelation(
891
+ Fr[NUMBER_OF_ENTITIES] memory p,
892
+ Honk.RelationParameters memory rp,
893
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
894
+ Fr domainSep
895
+ ) internal pure {
896
+ Fr write_term;
897
+ Fr read_term;
898
+
899
+ // Calculate the write term (the table accumulation)
900
+ {
901
+ write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta)
902
+ + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree);
903
+ }
904
+
905
+ // Calculate the write term
906
+ {
907
+ Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT));
908
+ Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT);
909
+ Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT);
910
+
911
+ read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo)
912
+ + (wire(p, WIRE.Q_O) * rp.etaThree);
913
+ }
914
+
915
+ Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term;
916
+ Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term;
917
+
918
+ Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP)
919
+ - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP));
920
+
921
+ // Inverse calculated correctly relation
922
+ Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor;
923
+ accumulatorNone = accumulatorNone * domainSep;
924
+
925
+ // Inverse
926
+ Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse;
927
+
928
+ evals[4] = accumulatorNone;
929
+ evals[5] = accumulatorOne;
930
+ }
931
+
932
+ function accumulateDeltaRangeRelation(
933
+ Fr[NUMBER_OF_ENTITIES] memory p,
934
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
935
+ Fr domainSep
936
+ ) internal pure {
937
+ Fr minus_one = Fr.wrap(0) - Fr.wrap(1);
938
+ Fr minus_two = Fr.wrap(0) - Fr.wrap(2);
939
+ Fr minus_three = Fr.wrap(0) - Fr.wrap(3);
940
+
941
+ // Compute wire differences
942
+ Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L);
943
+ Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R);
944
+ Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O);
945
+ Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4);
946
+
947
+ // Contribution 6
948
+ {
949
+ Fr acc = delta_1;
950
+ acc = acc * (delta_1 + minus_one);
951
+ acc = acc * (delta_1 + minus_two);
952
+ acc = acc * (delta_1 + minus_three);
953
+ acc = acc * wire(p, WIRE.Q_RANGE);
954
+ acc = acc * domainSep;
955
+ evals[6] = acc;
956
+ }
957
+
958
+ // Contribution 7
959
+ {
960
+ Fr acc = delta_2;
961
+ acc = acc * (delta_2 + minus_one);
962
+ acc = acc * (delta_2 + minus_two);
963
+ acc = acc * (delta_2 + minus_three);
964
+ acc = acc * wire(p, WIRE.Q_RANGE);
965
+ acc = acc * domainSep;
966
+ evals[7] = acc;
967
+ }
968
+
969
+ // Contribution 8
970
+ {
971
+ Fr acc = delta_3;
972
+ acc = acc * (delta_3 + minus_one);
973
+ acc = acc * (delta_3 + minus_two);
974
+ acc = acc * (delta_3 + minus_three);
975
+ acc = acc * wire(p, WIRE.Q_RANGE);
976
+ acc = acc * domainSep;
977
+ evals[8] = acc;
978
+ }
979
+
980
+ // Contribution 9
981
+ {
982
+ Fr acc = delta_4;
983
+ acc = acc * (delta_4 + minus_one);
984
+ acc = acc * (delta_4 + minus_two);
985
+ acc = acc * (delta_4 + minus_three);
986
+ acc = acc * wire(p, WIRE.Q_RANGE);
987
+ acc = acc * domainSep;
988
+ evals[9] = acc;
989
+ }
990
+ }
991
+
992
+ struct EllipticParams {
993
+ // Points
994
+ Fr x_1;
995
+ Fr y_1;
996
+ Fr x_2;
997
+ Fr y_2;
998
+ Fr y_3;
999
+ Fr x_3;
1000
+ // push accumulators into memory
1001
+ Fr x_double_identity;
1002
+ }
1003
+
1004
+ function accumulateEllipticRelation(
1005
+ Fr[NUMBER_OF_ENTITIES] memory p,
1006
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1007
+ Fr domainSep
1008
+ ) internal pure {
1009
+ EllipticParams memory ep;
1010
+ ep.x_1 = wire(p, WIRE.W_R);
1011
+ ep.y_1 = wire(p, WIRE.W_O);
1012
+
1013
+ ep.x_2 = wire(p, WIRE.W_L_SHIFT);
1014
+ ep.y_2 = wire(p, WIRE.W_4_SHIFT);
1015
+ ep.y_3 = wire(p, WIRE.W_O_SHIFT);
1016
+ ep.x_3 = wire(p, WIRE.W_R_SHIFT);
1017
+
1018
+ Fr q_sign = wire(p, WIRE.Q_L);
1019
+ Fr q_is_double = wire(p, WIRE.Q_M);
1020
+
1021
+ // Contribution 10 point addition, x-coordinate check
1022
+ // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
1023
+ Fr x_diff = (ep.x_2 - ep.x_1);
1024
+ Fr y1_sqr = (ep.y_1 * ep.y_1);
1025
+ {
1026
+ // Move to top
1027
+ Fr partialEval = domainSep;
1028
+
1029
+ Fr y2_sqr = (ep.y_2 * ep.y_2);
1030
+ Fr y1y2 = ep.y_1 * ep.y_2 * q_sign;
1031
+ Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1);
1032
+ x_add_identity = x_add_identity * x_diff * x_diff;
1033
+ x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2;
1034
+
1035
+ evals[10] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double);
1036
+ }
1037
+
1038
+ // Contribution 11 point addition, x-coordinate check
1039
+ // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0
1040
+ {
1041
+ Fr y1_plus_y3 = ep.y_1 + ep.y_3;
1042
+ Fr y_diff = ep.y_2 * q_sign - ep.y_1;
1043
+ Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff;
1044
+ evals[11] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double);
1045
+ }
1046
+
1047
+ // Contribution 10 point doubling, x-coordinate check
1048
+ // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0
1049
+ // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1
1050
+ {
1051
+ Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1;
1052
+ Fr y1_sqr_mul_4 = y1_sqr + y1_sqr;
1053
+ y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4;
1054
+ Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9);
1055
+
1056
+ // NOTE: pushed into memory (stack >:'( )
1057
+ ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9;
1058
+
1059
+ Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double;
1060
+ evals[10] = evals[10] + acc;
1061
+ }
1062
+
1063
+ // Contribution 11 point doubling, y-coordinate check
1064
+ // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0
1065
+ {
1066
+ Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1;
1067
+ Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3);
1068
+ evals[11] = evals[11] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double;
1069
+ }
1070
+ }
1071
+
1072
+ // Constants for the auxiliary relation
1073
+ Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68);
1074
+ Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14);
1075
+
1076
+ // Parameters used within the Auxiliary Relation
1077
+ // A struct is used to work around stack too deep. This relation has alot of variables
1078
+ struct AuxParams {
1079
+ Fr limb_subproduct;
1080
+ Fr non_native_field_gate_1;
1081
+ Fr non_native_field_gate_2;
1082
+ Fr non_native_field_gate_3;
1083
+ Fr limb_accumulator_1;
1084
+ Fr limb_accumulator_2;
1085
+ Fr memory_record_check;
1086
+ Fr partial_record_check;
1087
+ Fr next_gate_access_type;
1088
+ Fr record_delta;
1089
+ Fr index_delta;
1090
+ Fr adjacent_values_match_if_adjacent_indices_match;
1091
+ Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation;
1092
+ Fr access_check;
1093
+ Fr next_gate_access_type_is_boolean;
1094
+ Fr ROM_consistency_check_identity;
1095
+ Fr RAM_consistency_check_identity;
1096
+ Fr timestamp_delta;
1097
+ Fr RAM_timestamp_check_identity;
1098
+ Fr memory_identity;
1099
+ Fr index_is_monotonically_increasing;
1100
+ Fr auxiliary_identity;
1101
+ }
1102
+
1103
+ function accumulateAuxillaryRelation(
1104
+ Fr[NUMBER_OF_ENTITIES] memory p,
1105
+ Honk.RelationParameters memory rp,
1106
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1107
+ Fr domainSep
1108
+ ) internal pure {
1109
+ AuxParams memory ap;
1110
+
1111
+ /**
1112
+ * Contribution 12
1113
+ * Non native field arithmetic gate 2
1114
+ * deg 4
1115
+ *
1116
+ * _ _
1117
+ * / _ _ _ 14 \
1118
+ * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 |
1119
+ * \_ _/
1120
+ *
1121
+ *
1122
+ */
1123
+ ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R);
1124
+ ap.non_native_field_gate_2 =
1125
+ (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT));
1126
+ ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE;
1127
+ ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT);
1128
+ ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct;
1129
+ ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4);
1130
+
1131
+ ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE;
1132
+ ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT));
1133
+ ap.non_native_field_gate_1 = ap.limb_subproduct;
1134
+ ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4));
1135
+ ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O);
1136
+
1137
+ ap.non_native_field_gate_3 = ap.limb_subproduct;
1138
+ ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4);
1139
+ ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT));
1140
+ ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M);
1141
+
1142
+ Fr non_native_field_identity =
1143
+ ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3;
1144
+ non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R);
1145
+
1146
+ // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm
1147
+ // deg 2
1148
+ ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT;
1149
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT);
1150
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;
1151
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O);
1152
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;
1153
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R);
1154
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;
1155
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L);
1156
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4);
1157
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4);
1158
+
1159
+ // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm
1160
+ // deg 2
1161
+ ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT;
1162
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT);
1163
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;
1164
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT);
1165
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;
1166
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4);
1167
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;
1168
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O);
1169
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT);
1170
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M);
1171
+
1172
+ Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2;
1173
+ limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3
1174
+
1175
+ /**
1176
+ * MEMORY
1177
+ *
1178
+ * A RAM memory record contains a tuple of the following fields:
1179
+ * * i: `index` of memory cell being accessed
1180
+ * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM)
1181
+ * * v: `value` of memory cell being accessed
1182
+ * * a: `access` type of record. read: 0 = read, 1 = write
1183
+ * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three
1184
+ *
1185
+ * A ROM memory record contains a tuple of the following fields:
1186
+ * * i: `index` of memory cell being accessed
1187
+ * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index)
1188
+ * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index)
1189
+ * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three
1190
+ *
1191
+ * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires +
1192
+ * selectors, depending on whether the gate is a RAM read/write or a ROM read
1193
+ *
1194
+ * | gate type | i | v2/t | v | a | r |
1195
+ * | --------- | -- | ----- | -- | -- | -- |
1196
+ * | ROM | w1 | w2 | w3 | -- | w4 |
1197
+ * | RAM | w1 | w2 | w3 | qc | w4 |
1198
+ *
1199
+ * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on
1200
+ * `w2` to fix its value)
1201
+ *
1202
+ *
1203
+ */
1204
+
1205
+ /**
1206
+ * Memory Record Check
1207
+ * Partial degree: 1
1208
+ * Total degree: 4
1209
+ *
1210
+ * A ROM/ROM access gate can be evaluated with the identity:
1211
+ *
1212
+ * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0
1213
+ *
1214
+ * For ROM gates, qc = 0
1215
+ */
1216
+ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree;
1217
+ ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo);
1218
+ ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta);
1219
+ ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C);
1220
+ ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4
1221
+ ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4);
1222
+
1223
+ /**
1224
+ * Contribution 13 & 14
1225
+ * ROM Consistency Check
1226
+ * Partial degree: 1
1227
+ * Total degree: 4
1228
+ *
1229
+ * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of
1230
+ * records that are sorted.
1231
+ *
1232
+ * We apply the following checks for the sorted records:
1233
+ *
1234
+ * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4
1235
+ * 2. index values for adjacent records are monotonically increasing
1236
+ * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1}
1237
+ *
1238
+ */
1239
+ ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L);
1240
+ ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4);
1241
+
1242
+ ap.index_is_monotonically_increasing = ap.index_delta * ap.index_delta - ap.index_delta; // deg 2
1243
+
1244
+ ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + Fr.wrap(1)) * ap.record_delta; // deg 2
1245
+
1246
+ evals[13] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R))
1247
+ * (wire(p, WIRE.Q_AUX) * domainSep); // deg 5
1248
+ evals[14] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R))
1249
+ * (wire(p, WIRE.Q_AUX) * domainSep); // deg 5
1250
+
1251
+ ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7
1252
+
1253
+ /**
1254
+ * Contributions 15,16,17
1255
+ * RAM Consistency Check
1256
+ *
1257
+ * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check`
1258
+ * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`.
1259
+ * This is validated by requiring `access` to be boolean
1260
+ *
1261
+ * For two adjacent entries in the sorted list if _both_
1262
+ * A) index values match
1263
+ * B) adjacent access value is 0 (i.e. next gate is a READ)
1264
+ * then
1265
+ * C) both values must match.
1266
+ * The gate boolean check is
1267
+ * (A && B) => C === !(A && B) || C === !A || !B || C
1268
+ *
1269
+ * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized
1270
+ * with a WRITE operation.
1271
+ */
1272
+ Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4
1273
+ ap.access_check = access_type * access_type - access_type; // check value is 0 or 1; deg 2 or 8
1274
+
1275
+ ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree;
1276
+ ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo);
1277
+ ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta);
1278
+ ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type;
1279
+
1280
+ Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O);
1281
+ ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = (
1282
+ ap.index_delta * MINUS_ONE + Fr.wrap(1)
1283
+ ) * value_delta * (ap.next_gate_access_type * MINUS_ONE + Fr.wrap(1)); // deg 3 or 6
1284
+
1285
+ // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the
1286
+ // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't
1287
+ // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access
1288
+ // type is correct, to cover this edge case
1289
+ // deg 2 or 4
1290
+ ap.next_gate_access_type_is_boolean =
1291
+ ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type;
1292
+
1293
+ // Putting it all together...
1294
+ evals[15] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation
1295
+ * (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 5 or 8
1296
+ evals[16] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4
1297
+ evals[17] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 6
1298
+
1299
+ ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_ARITH)); // deg 3 or 9
1300
+
1301
+ /**
1302
+ * RAM Timestamp Consistency Check
1303
+ *
1304
+ * | w1 | w2 | w3 | w4 |
1305
+ * | index | timestamp | timestamp_check | -- |
1306
+ *
1307
+ * Let delta_index = index_{i + 1} - index_{i}
1308
+ *
1309
+ * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i
1310
+ * Else timestamp_check = 0
1311
+ */
1312
+ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R);
1313
+ ap.RAM_timestamp_check_identity =
1314
+ (ap.index_delta * MINUS_ONE + Fr.wrap(1)) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3
1315
+
1316
+ /**
1317
+ * Complete Contribution 12
1318
+ * The complete RAM/ROM memory identity
1319
+ * Partial degree:
1320
+ */
1321
+ ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6
1322
+ ap.memory_identity =
1323
+ ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4
1324
+ ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6
1325
+ ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9
1326
+
1327
+ // (deg 3 or 9) + (deg 4) + (deg 3)
1328
+ ap.auxiliary_identity = ap.memory_identity + non_native_field_identity + limb_accumulator_identity;
1329
+ ap.auxiliary_identity = ap.auxiliary_identity * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 10
1330
+ evals[12] = ap.auxiliary_identity;
1331
+ }
1332
+
1333
+ struct PoseidonExternalParams {
1334
+ Fr s1;
1335
+ Fr s2;
1336
+ Fr s3;
1337
+ Fr s4;
1338
+ Fr u1;
1339
+ Fr u2;
1340
+ Fr u3;
1341
+ Fr u4;
1342
+ Fr t0;
1343
+ Fr t1;
1344
+ Fr t2;
1345
+ Fr t3;
1346
+ Fr v1;
1347
+ Fr v2;
1348
+ Fr v3;
1349
+ Fr v4;
1350
+ Fr q_pos_by_scaling;
1351
+ }
1352
+
1353
+ function accumulatePoseidonExternalRelation(
1354
+ Fr[NUMBER_OF_ENTITIES] memory p,
1355
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1356
+ Fr domainSep
1357
+ ) internal pure {
1358
+ PoseidonExternalParams memory ep;
1359
+
1360
+ ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L);
1361
+ ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R);
1362
+ ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O);
1363
+ ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4);
1364
+
1365
+ ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1;
1366
+ ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2;
1367
+ ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3;
1368
+ ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4;
1369
+ // matrix mul v = M_E * u with 14 additions
1370
+ ep.t0 = ep.u1 + ep.u2; // u_1 + u_2
1371
+ ep.t1 = ep.u3 + ep.u4; // u_3 + u_4
1372
+ ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2
1373
+ // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4
1374
+ ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4
1375
+ // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4
1376
+ ep.v4 = ep.t1 + ep.t1;
1377
+ ep.v4 = ep.v4 + ep.v4 + ep.t3;
1378
+ // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4
1379
+ ep.v2 = ep.t0 + ep.t0;
1380
+ ep.v2 = ep.v2 + ep.v2 + ep.t2;
1381
+ // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4
1382
+ ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4
1383
+ ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4
1384
+
1385
+ ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep;
1386
+ evals[18] = evals[18] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT));
1387
+
1388
+ evals[19] = evals[19] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT));
1389
+
1390
+ evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT));
1391
+
1392
+ evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT));
1393
+ }
1394
+
1395
+ struct PoseidonInternalParams {
1396
+ Fr u1;
1397
+ Fr u2;
1398
+ Fr u3;
1399
+ Fr u4;
1400
+ Fr u_sum;
1401
+ Fr v1;
1402
+ Fr v2;
1403
+ Fr v3;
1404
+ Fr v4;
1405
+ Fr s1;
1406
+ Fr q_pos_by_scaling;
1407
+ }
1408
+
1409
+ function accumulatePoseidonInternalRelation(
1410
+ Fr[NUMBER_OF_ENTITIES] memory p,
1411
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1412
+ Fr domainSep
1413
+ ) internal pure {
1414
+ PoseidonInternalParams memory ip;
1415
+
1416
+ Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [
1417
+ FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7),
1418
+ FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b),
1419
+ FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15),
1420
+ FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b)
1421
+ ];
1422
+
1423
+ // add round constants
1424
+ ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L);
1425
+
1426
+ // apply s-box round
1427
+ ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1;
1428
+ ip.u2 = wire(p, WIRE.W_R);
1429
+ ip.u3 = wire(p, WIRE.W_O);
1430
+ ip.u4 = wire(p, WIRE.W_4);
1431
+
1432
+ // matrix mul with v = M_I * u 4 muls and 7 additions
1433
+ ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4;
1434
+
1435
+ ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep;
1436
+
1437
+ ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum;
1438
+ evals[22] = evals[22] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT));
1439
+
1440
+ ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum;
1441
+ evals[23] = evals[23] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT));
1442
+
1443
+ ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum;
1444
+ evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT));
1445
+
1446
+ ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum;
1447
+ evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT));
1448
+ }
1449
+
1450
+ function scaleAndBatchSubrelations(
1451
+ Fr[NUMBER_OF_SUBRELATIONS] memory evaluations,
1452
+ Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges
1453
+ ) internal pure returns (Fr accumulator) {
1454
+ accumulator = accumulator + evaluations[0];
1455
+
1456
+ for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) {
1457
+ accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1];
1458
+ }
1459
+ }
1460
+ }
1461
+
1462
+ struct ShpleminiIntermediates {
1463
+ Fr unshiftedScalar;
1464
+ Fr shiftedScalar;
1465
+ // Scalar to be multiplied by [1]₁
1466
+ Fr constantTermAccumulator;
1467
+ // Accumulator for powers of rho
1468
+ Fr batchingChallenge;
1469
+ // Linear combination of multilinear (sumcheck) evaluations and powers of rho
1470
+ Fr batchedEvaluation;
1471
+ // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated
1472
+ Fr posInvertedDenominator;
1473
+ // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated
1474
+ Fr negInvertedDenominator;
1475
+ // v^{2i} * 1/(z - r^{2^i})
1476
+ Fr scalingFactorPos;
1477
+ // v^{2i+1} * 1/(z + r^{2^i})
1478
+ Fr scalingFactorNeg;
1479
+ // // Fold_i(r^{2^i}) reconstructed by Verifier
1480
+ // Fr[CONST_PROOF_SIZE_LOG_N] foldPosEvaluations;
1481
+ }
1482
+
1483
+ library CommitmentSchemeLib {
1484
+ using FrLib for Fr;
1485
+
1486
+ function computeSquares(Fr r) internal pure returns (Fr[CONST_PROOF_SIZE_LOG_N] memory squares) {
1487
+ squares[0] = r;
1488
+ for (uint256 i = 1; i < CONST_PROOF_SIZE_LOG_N; ++i) {
1489
+ squares[i] = squares[i - 1].sqr();
1490
+ }
1491
+ }
1492
+
1493
+ // Compute the evaluations A_l(r^{2^l}) for l = 0, ..., m-1
1494
+ function computeFoldPosEvaluations(
1495
+ Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges,
1496
+ Fr batchedEvalAccumulator,
1497
+ Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations,
1498
+ Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvalChallengePowers,
1499
+ uint256 logSize
1500
+ ) internal view returns (Fr[CONST_PROOF_SIZE_LOG_N] memory foldPosEvaluations) {
1501
+ for (uint256 i = CONST_PROOF_SIZE_LOG_N; i > 0; --i) {
1502
+ Fr challengePower = geminiEvalChallengePowers[i - 1];
1503
+ Fr u = sumcheckUChallenges[i - 1];
1504
+
1505
+ Fr batchedEvalRoundAcc = (
1506
+ (challengePower * batchedEvalAccumulator * Fr.wrap(2))
1507
+ - geminiEvaluations[i - 1] * (challengePower * (Fr.wrap(1) - u) - u)
1508
+ );
1509
+ // Divide by the denominator
1510
+ batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (Fr.wrap(1) - u) + u).invert();
1511
+
1512
+ if (i <= logSize) {
1513
+ batchedEvalAccumulator = batchedEvalRoundAcc;
1514
+ foldPosEvaluations[i - 1] = batchedEvalRoundAcc;
1515
+ }
1516
+ }
1517
+
1518
+ }
1519
+ }
1520
+
1521
+ interface IVerifier {
1522
+ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool);
1523
+ }
1524
+
1525
+
1526
+ abstract contract BaseHonkVerifier is IVerifier {
1527
+ using FrLib for Fr;
1528
+
1529
+ uint256 immutable n;
1530
+ uint256 immutable logN;
1531
+ uint256 immutable numPublicInputs;
1532
+
1533
+ constructor(uint256 _n, uint256 _logN, uint256 _numPublicInputs) {
1534
+ n = _n;
1535
+ logN = _logN;
1536
+ numPublicInputs = _numPublicInputs;
1537
+ }
1538
+
1539
+ error ProofLengthWrong();
1540
+ error PublicInputsLengthWrong();
1541
+ error SumcheckFailed();
1542
+ error ShpleminiFailed();
1543
+
1544
+ // Number of field elements in a ultra honk zero knowledge proof
1545
+ uint256 constant PROOF_SIZE = 440;
1546
+
1547
+ function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory);
1548
+
1549
+ function verify(bytes calldata proof, bytes32[] calldata publicInputs) public view override returns (bool) {
1550
+ // Check the received proof is the expected size where each field element is 32 bytes
1551
+ if (proof.length != PROOF_SIZE * 32) {
1552
+ revert ProofLengthWrong();
1553
+ }
1554
+
1555
+ Honk.VerificationKey memory vk = loadVerificationKey();
1556
+ Honk.Proof memory p = TranscriptLib.loadProof(proof);
1557
+
1558
+ if (publicInputs.length != vk.publicInputsSize) {
1559
+ revert PublicInputsLengthWrong();
1560
+ }
1561
+
1562
+ // Generate the fiat shamir challenges for the whole protocol
1563
+ // TODO(https://github.com/AztecProtocol/barretenberg/issues/1281): Add pubInputsOffset to VK or remove entirely.
1564
+ Transcript memory t = TranscriptLib.generateTranscript(p, publicInputs, vk.circuitSize, vk.publicInputsSize, /*pubInputsOffset=*/1);
1565
+
1566
+ // Derive public input delta
1567
+ // TODO(https://github.com/AztecProtocol/barretenberg/issues/1281): Add pubInputsOffset to VK or remove entirely.
1568
+ t.relationParameters.publicInputsDelta = computePublicInputDelta(
1569
+ publicInputs, t.relationParameters.beta, t.relationParameters.gamma, /*pubInputsOffset=*/1
1570
+ );
1571
+
1572
+ // Sumcheck
1573
+ bool sumcheckVerified = verifySumcheck(p, t);
1574
+ if (!sumcheckVerified) revert SumcheckFailed();
1575
+
1576
+ bool shpleminiVerified = verifyShplemini(p, vk, t);
1577
+ if (!shpleminiVerified) revert ShpleminiFailed();
1578
+
1579
+ return sumcheckVerified && shpleminiVerified; // Boolean condition not required - nice for vanity :)
1580
+ }
1581
+
1582
+ function computePublicInputDelta(bytes32[] memory publicInputs, Fr beta, Fr gamma, uint256 offset)
1583
+ internal
1584
+ view
1585
+ returns (Fr publicInputDelta)
1586
+ {
1587
+ Fr numerator = Fr.wrap(1);
1588
+ Fr denominator = Fr.wrap(1);
1589
+
1590
+ Fr numeratorAcc = gamma + (beta * FrLib.from(n + offset));
1591
+ Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1));
1592
+
1593
+ {
1594
+ for (uint256 i = 0; i < numPublicInputs; i++) {
1595
+ Fr pubInput = FrLib.fromBytes32(publicInputs[i]);
1596
+
1597
+ numerator = numerator * (numeratorAcc + pubInput);
1598
+ denominator = denominator * (denominatorAcc + pubInput);
1599
+
1600
+ numeratorAcc = numeratorAcc + beta;
1601
+ denominatorAcc = denominatorAcc - beta;
1602
+ }
1603
+ }
1604
+
1605
+ // Fr delta = numerator / denominator; // TOOO: batch invert later?
1606
+ publicInputDelta = FrLib.div(numerator, denominator);
1607
+ }
1608
+
1609
+ function verifySumcheck(Honk.Proof memory proof, Transcript memory tp) internal view returns (bool verified) {
1610
+ Fr roundTarget;
1611
+ Fr powPartialEvaluation = Fr.wrap(1);
1612
+
1613
+ // We perform sumcheck reductions over log n rounds ( the multivariate degree )
1614
+ for (uint256 round; round < logN; ++round) {
1615
+ Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round];
1616
+ bool valid = checkSum(roundUnivariate, roundTarget);
1617
+ if (!valid) revert SumcheckFailed();
1618
+
1619
+ Fr roundChallenge = tp.sumCheckUChallenges[round];
1620
+
1621
+ // Update the round target for the next rounf
1622
+ roundTarget = computeNextTargetSum(roundUnivariate, roundChallenge);
1623
+ powPartialEvaluation = partiallyEvaluatePOW(tp.gateChallenges[round], powPartialEvaluation, roundChallenge);
1624
+ }
1625
+
1626
+ // Last round
1627
+ Fr grandHonkRelationSum =
1628
+ RelationsLib.accumulateRelationEvaluations(proof.sumcheckEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation);
1629
+ verified = (grandHonkRelationSum == roundTarget);
1630
+ }
1631
+
1632
+ function checkSum(Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate, Fr roundTarget)
1633
+ internal
1634
+ pure
1635
+ returns (bool checked)
1636
+ {
1637
+ Fr totalSum = roundUnivariate[0] + roundUnivariate[1];
1638
+ checked = totalSum == roundTarget;
1639
+ }
1640
+
1641
+ // Return the new target sum for the next sumcheck round
1642
+ function computeNextTargetSum(Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge)
1643
+ internal
1644
+ view
1645
+ returns (Fr targetSum)
1646
+ {
1647
+ // TODO: inline
1648
+ Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [
1649
+ Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51),
1650
+ Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0),
1651
+ Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff11),
1652
+ Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000090),
1653
+ Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff71),
1654
+ Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000000f0),
1655
+ Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31),
1656
+ Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000013b0)
1657
+ ];
1658
+
1659
+ // To compute the next target sum, we evaluate the given univariate at a point u (challenge).
1660
+
1661
+ // Performing Barycentric evaluations
1662
+ // Compute B(x)
1663
+ Fr numeratorValue = Fr.wrap(1);
1664
+ for (uint256 i = 0; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) {
1665
+ numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i));
1666
+ }
1667
+
1668
+ // Calculate domain size N of inverses
1669
+ Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses;
1670
+ for (uint256 i = 0; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) {
1671
+ Fr inv = BARYCENTRIC_LAGRANGE_DENOMINATORS[i];
1672
+ inv = inv * (roundChallenge - Fr.wrap(i));
1673
+ inv = FrLib.invert(inv);
1674
+ denominatorInverses[i] = inv;
1675
+ }
1676
+
1677
+ for (uint256 i = 0; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) {
1678
+ Fr term = roundUnivariates[i];
1679
+ term = term * denominatorInverses[i];
1680
+ targetSum = targetSum + term;
1681
+ }
1682
+
1683
+ // Scale the sum by the value of B(x)
1684
+ targetSum = targetSum * numeratorValue;
1685
+ }
1686
+
1687
+ // Univariate evaluation of the monomial ((1-X_l) + X_l.B_l) at the challenge point X_l=u_l
1688
+ function partiallyEvaluatePOW(Fr gateChallenge, Fr currentEvaluation, Fr roundChallenge)
1689
+ internal
1690
+ pure
1691
+ returns (Fr newEvaluation)
1692
+ {
1693
+ Fr univariateEval = Fr.wrap(1) + (roundChallenge * (gateChallenge - Fr.wrap(1)));
1694
+ newEvaluation = currentEvaluation * univariateEval;
1695
+ }
1696
+
1697
+ function verifyShplemini(Honk.Proof memory proof, Honk.VerificationKey memory vk, Transcript memory tp)
1698
+ internal
1699
+ view
1700
+ returns (bool verified)
1701
+ {
1702
+ ShpleminiIntermediates memory mem; // stack
1703
+
1704
+ // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size
1705
+ Fr[CONST_PROOF_SIZE_LOG_N] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR);
1706
+
1707
+ // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings
1708
+ Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory scalars;
1709
+ Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory commitments;
1710
+
1711
+ mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert();
1712
+ mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert();
1713
+
1714
+ mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator);
1715
+ mem.shiftedScalar =
1716
+ tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator));
1717
+
1718
+ scalars[0] = Fr.wrap(1);
1719
+ commitments[0] = convertProofPoint(proof.shplonkQ);
1720
+
1721
+ mem.batchingChallenge = Fr.wrap(1);
1722
+ mem.batchedEvaluation = Fr.wrap(0);
1723
+
1724
+ for (uint256 i = 1; i <= NUMBER_UNSHIFTED; ++i) {
1725
+ scalars[i] = mem.unshiftedScalar.neg() * mem.batchingChallenge;
1726
+ mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - 1] * mem.batchingChallenge);
1727
+ mem.batchingChallenge = mem.batchingChallenge * tp.rho;
1728
+ }
1729
+ // g commitments are accumulated at r
1730
+ for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) {
1731
+ scalars[i] = mem.shiftedScalar.neg() * mem.batchingChallenge;
1732
+ mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - 1] * mem.batchingChallenge);
1733
+ mem.batchingChallenge = mem.batchingChallenge * tp.rho;
1734
+ }
1735
+
1736
+ commitments[1] = vk.qm;
1737
+ commitments[2] = vk.qc;
1738
+ commitments[3] = vk.ql;
1739
+ commitments[4] = vk.qr;
1740
+ commitments[5] = vk.qo;
1741
+ commitments[6] = vk.q4;
1742
+ commitments[7] = vk.qLookup;
1743
+ commitments[8] = vk.qArith;
1744
+ commitments[9] = vk.qDeltaRange;
1745
+ commitments[10] = vk.qElliptic;
1746
+ commitments[11] = vk.qAux;
1747
+ commitments[12] = vk.qPoseidon2External;
1748
+ commitments[13] = vk.qPoseidon2Internal;
1749
+ commitments[14] = vk.s1;
1750
+ commitments[15] = vk.s2;
1751
+ commitments[16] = vk.s3;
1752
+ commitments[17] = vk.s4;
1753
+ commitments[18] = vk.id1;
1754
+ commitments[19] = vk.id2;
1755
+ commitments[20] = vk.id3;
1756
+ commitments[21] = vk.id4;
1757
+ commitments[22] = vk.t1;
1758
+ commitments[23] = vk.t2;
1759
+ commitments[24] = vk.t3;
1760
+ commitments[25] = vk.t4;
1761
+ commitments[26] = vk.lagrangeFirst;
1762
+ commitments[27] = vk.lagrangeLast;
1763
+
1764
+ // Accumulate proof points
1765
+ commitments[28] = convertProofPoint(proof.w1);
1766
+ commitments[29] = convertProofPoint(proof.w2);
1767
+ commitments[30] = convertProofPoint(proof.w3);
1768
+ commitments[31] = convertProofPoint(proof.w4);
1769
+ commitments[32] = convertProofPoint(proof.zPerm);
1770
+ commitments[33] = convertProofPoint(proof.lookupInverses);
1771
+ commitments[34] = convertProofPoint(proof.lookupReadCounts);
1772
+ commitments[35] = convertProofPoint(proof.lookupReadTags);
1773
+
1774
+ // to be Shifted
1775
+ commitments[36] = convertProofPoint(proof.w1);
1776
+ commitments[37] = convertProofPoint(proof.w2);
1777
+ commitments[38] = convertProofPoint(proof.w3);
1778
+ commitments[39] = convertProofPoint(proof.w4);
1779
+ commitments[40] = convertProofPoint(proof.zPerm);
1780
+
1781
+ // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator:
1782
+ // Compute the evaluations A_l(r^{2^l}) for l = 0, ..., logN - 1
1783
+ Fr[CONST_PROOF_SIZE_LOG_N] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations(
1784
+ tp.sumCheckUChallenges,
1785
+ mem.batchedEvaluation,
1786
+ proof.geminiAEvaluations,
1787
+ powers_of_evaluation_challenge,
1788
+ logN
1789
+ );
1790
+
1791
+ // Compute the Shplonk constant term contributions from A₀(±r)
1792
+ mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator;
1793
+ mem.constantTermAccumulator =
1794
+ mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator);
1795
+ mem.batchingChallenge = tp.shplonkNu.sqr();
1796
+
1797
+ // Compute Shplonk constant term contributions from Aₗ(±r^{2ˡ}) for l = 1, ..., m-1;
1798
+ // Compute scalar multipliers for each fold commitment
1799
+ for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; ++i) {
1800
+ bool dummy_round = i >= (logN - 1);
1801
+
1802
+ if (!dummy_round) {
1803
+ // Update inverted denominators
1804
+ mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert();
1805
+ mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert();
1806
+
1807
+ // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ]
1808
+ mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator;
1809
+ mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator;
1810
+ // [Aₗ] is multiplied by -v^{2l}/(z-r^{2^l}) - v^{2l+1} /(z+ r^{2^l})
1811
+ scalars[NUMBER_OF_ENTITIES + 1 + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg();
1812
+
1813
+ // Accumulate the const term contribution given by
1814
+ // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l})
1815
+ Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1];
1816
+ accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1];
1817
+ mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution;
1818
+ // Update the running power of v
1819
+ mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu;
1820
+ }
1821
+
1822
+ commitments[NUMBER_OF_ENTITIES + 1 + i] = convertProofPoint(proof.geminiFoldComms[i]);
1823
+ }
1824
+
1825
+ // Finalise the batch opening claim
1826
+ commitments[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = Honk.G1Point({x: 1, y: 2});
1827
+ scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = mem.constantTermAccumulator;
1828
+
1829
+ Honk.G1Point memory quotient_commitment = convertProofPoint(proof.kzgQuotient);
1830
+
1831
+ commitments[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = quotient_commitment;
1832
+ scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = tp.shplonkZ; // evaluation challenge
1833
+
1834
+ Honk.G1Point memory P_0 = batchMul(commitments, scalars);
1835
+ Honk.G1Point memory P_1 = negateInplace(quotient_commitment);
1836
+
1837
+ return pairing(P_0, P_1);
1838
+ }
1839
+
1840
+ // This implementation is the same as above with different constants
1841
+ function batchMul(
1842
+ Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory base,
1843
+ Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory scalars
1844
+ ) internal view returns (Honk.G1Point memory result) {
1845
+ uint256 limit = NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2;
1846
+ assembly {
1847
+ let success := 0x01
1848
+ let free := mload(0x40)
1849
+
1850
+ // Write the original into the accumulator
1851
+ // Load into memory for ecMUL, leave offset for eccAdd result
1852
+ // base is an array of pointers, so we have to dereference them
1853
+ mstore(add(free, 0x40), mload(mload(base)))
1854
+ mstore(add(free, 0x60), mload(add(0x20, mload(base))))
1855
+ // Add scalar
1856
+ mstore(add(free, 0x80), mload(scalars))
1857
+ success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, free, 0x40))
1858
+
1859
+ let count := 0x01
1860
+ for {} lt(count, limit) { count := add(count, 1) } {
1861
+ // Get loop offsets
1862
+ let base_base := add(base, mul(count, 0x20))
1863
+ let scalar_base := add(scalars, mul(count, 0x20))
1864
+
1865
+ mstore(add(free, 0x40), mload(mload(base_base)))
1866
+ mstore(add(free, 0x60), mload(add(0x20, mload(base_base))))
1867
+ // Add scalar
1868
+ mstore(add(free, 0x80), mload(scalar_base))
1869
+
1870
+ success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40))
1871
+ // accumulator = accumulator + accumulator_2
1872
+ success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40))
1873
+ }
1874
+
1875
+ // Return the result - i hate this
1876
+ mstore(result, mload(free))
1877
+ mstore(add(result, 0x20), mload(add(free, 0x20)))
1878
+ }
1879
+ }
1880
+ }
1881
+
1882
+ contract HonkVerifier is BaseHonkVerifier(N, LOG_N, NUMBER_OF_PUBLIC_INPUTS) {
1883
+ function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) {
1884
+ return HonkVerificationKey.loadVerificationKey();
1885
+ }
1886
+ }