Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
honk_optimized_contract.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Planned, auditors: [], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
8#include <filesystem>
9#include <fstream>
10#include <iostream>
11#include <sstream>
12#include <vector>
13
14// Complete implementation of generate_offsets.py converted to C++
15inline std::string generate_memory_offsets(int log_n)
16{
17 const int BATCHED_RELATION_PARTIAL_LENGTH = 8;
18 const int NUMBER_OF_SUBRELATIONS = 28;
19 const int NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1;
20 const int START_POINTER = 0x1000;
21 const int SCRATCH_SPACE_POINTER = 0x100;
22 const int BARYCENTRIC_DOMAIN_SIZE = 8;
23
24 std::ostringstream out;
25
26 // Helper lambdas
27 auto print_header_centered = [&](const std::string& text) {
28 const std::string top = "/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/";
29 const std::string bottom = "/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/";
30 size_t width = static_cast<size_t>(top.length()) - 4; // exclude /* and */
31 std::string centered =
32 "/*" + std::string(static_cast<size_t>((width - text.length()) / 2), ' ') + text +
33 std::string(static_cast<size_t>(width - text.length() - (width - text.length()) / 2), ' ') + "*/";
34 out << "\n" << top << "\n" << centered << "\n" << bottom << "\n";
35 };
36
37 auto print_loc = [&](int pointer, const std::string& name) {
38 out << "uint256 internal constant " << name << " = " << std::showbase << std::hex << pointer << ";\n";
39 };
40
41 auto print_fr = print_loc;
42
43 auto print_g1 = [&](int pointer, const std::string& name) {
44 print_loc(pointer, name + "_X_LOC");
45 print_loc(pointer + 32, name + "_Y_LOC");
46 };
47
48 // Data arrays from Python script
49 const std::vector<std::string> vk_fr = { "VK_CIRCUIT_SIZE_LOC",
50 "VK_NUM_PUBLIC_INPUTS_LOC",
51 "VK_PUB_INPUTS_OFFSET_LOC" };
52
53 const std::vector<std::string> vk_g1 = { "Q_M",
54 "Q_C",
55 "Q_L",
56 "Q_R",
57 "Q_O",
58 "Q_4",
59 "Q_LOOKUP",
60 "Q_ARITH",
61 "Q_DELTA_RANGE",
62 "Q_ELLIPTIC",
63 "Q_MEMORY",
64 "Q_NNF",
65 "Q_POSEIDON_2_EXTERNAL",
66 "Q_POSEIDON_2_INTERNAL",
67 "SIGMA_1",
68 "SIGMA_2",
69 "SIGMA_3",
70 "SIGMA_4",
71 "ID_1",
72 "ID_2",
73 "ID_3",
74 "ID_4",
75 "TABLE_1",
76 "TABLE_2",
77 "TABLE_3",
78 "TABLE_4",
79 "LAGRANGE_FIRST",
80 "LAGRANGE_LAST" };
81
82 const std::vector<std::string> proof_fr = { "PROOF_CIRCUIT_SIZE",
83 "PROOF_NUM_PUBLIC_INPUTS",
84 "PROOF_PUB_INPUTS_OFFSET" };
85
86 const std::vector<std::string> pairing_points = { "PAIRING_POINT_0_X_0_LOC", "PAIRING_POINT_0_X_1_LOC",
87 "PAIRING_POINT_0_Y_0_LOC", "PAIRING_POINT_0_Y_1_LOC",
88 "PAIRING_POINT_1_X_0_LOC", "PAIRING_POINT_1_X_1_LOC",
89 "PAIRING_POINT_1_Y_0_LOC", "PAIRING_POINT_1_Y_1_LOC" };
90
91 const std::vector<std::string> proof_g1 = {
92 "W_L", "W_R", "W_O", "LOOKUP_READ_COUNTS", "LOOKUP_READ_TAGS", "W_4", "LOOKUP_INVERSES", "Z_PERM"
93 };
94
95 const std::vector<std::string> entities = { "QM",
96 "QC",
97 "QL",
98 "QR",
99 "QO",
100 "Q4",
101 "QLOOKUP",
102 "QARITH",
103 "QRANGE",
104 "QELLIPTIC",
105 "QMEMORY",
106 "QNNF",
107 "QPOSEIDON2_EXTERNAL",
108 "QPOSEIDON2_INTERNAL",
109 "SIGMA1",
110 "SIGMA2",
111 "SIGMA3",
112 "SIGMA4",
113 "ID1",
114 "ID2",
115 "ID3",
116 "ID4",
117 "TABLE1",
118 "TABLE2",
119 "TABLE3",
120 "TABLE4",
121 "LAGRANGE_FIRST",
122 "LAGRANGE_LAST",
123 "W1",
124 "W2",
125 "W3",
126 "W4",
127 "Z_PERM",
128 "LOOKUP_INVERSES",
129 "LOOKUP_READ_COUNTS",
130 "LOOKUP_READ_TAGS",
131 "W1_SHIFT",
132 "W2_SHIFT",
133 "W3_SHIFT",
134 "W4_SHIFT",
135 "Z_PERM_SHIFT" };
136
137 const std::vector<std::string> challenges = { "ETA",
138 "ETA_TWO",
139 "ETA_THREE",
140 "BETA",
141 "GAMMA",
142 "RHO",
143 "GEMINI_R",
144 "SHPLONK_NU",
145 "SHPLONK_Z",
146 "PUBLIC_INPUTS_DELTA_NUMERATOR",
147 "PUBLIC_INPUTS_DELTA_DENOMINATOR" };
148
149 const std::vector<std::string> subrelation_intermediates = { "AUX_NON_NATIVE_FIELD_IDENTITY",
150 "AUX_LIMB_ACCUMULATOR_IDENTITY",
151 "AUX_RAM_CONSISTENCY_CHECK_IDENTITY",
152 "AUX_ROM_CONSISTENCY_CHECK_IDENTITY",
153 "AUX_MEMORY_CHECK_IDENTITY" };
154
155 const std::vector<std::string> general_intermediates = { "FINAL_ROUND_TARGET_LOC", "POW_PARTIAL_EVALUATION_LOC" };
156
157 int pointer = START_POINTER;
158
159 // VK INDICIES
160 print_header_centered("VK INDICIES");
161 for (const auto& item : vk_fr) {
162 print_fr(pointer, item);
163 pointer += 32;
164 }
165 for (const auto& item : vk_g1) {
166 print_g1(pointer, item);
167 pointer += 64;
168 }
169
170 // PROOF INDICIES
171 print_header_centered("PROOF INDICIES");
172 for (const auto& item : pairing_points) {
173 print_fr(pointer, item);
174 pointer += 32;
175 }
176 for (const auto& item : proof_g1) {
177 print_g1(pointer, item);
178 pointer += 64;
179 }
180
181 // SUMCHECK UNIVARIATES
182 print_header_centered("PROOF INDICIES - SUMCHECK UNIVARIATES");
183 for (int size = 0; size < log_n; ++size) {
184 for (int relation_len = 0; relation_len < BATCHED_RELATION_PARTIAL_LENGTH; ++relation_len) {
185 std::string name =
186 "SUMCHECK_UNIVARIATE_" + std::to_string(size) + "_" + std::to_string(relation_len) + "_LOC";
187 print_fr(pointer, name);
188 pointer += 32;
189 }
190 }
191
192 // SUMCHECK EVALUATIONS
193 print_header_centered("PROOF INDICIES - SUMCHECK EVALUATIONS");
194 for (const auto& entity : entities) {
195 print_fr(pointer, entity + "_EVAL_LOC");
196 pointer += 32;
197 }
198
199 // SHPLEMINI - GEMINI FOLDING COMMS
200 print_header_centered("PROOF INDICIES - GEMINI FOLDING COMMS");
201 for (int size = 0; size < log_n - 1; ++size) {
202 print_g1(pointer, "GEMINI_FOLD_UNIVARIATE_" + std::to_string(size));
203 pointer += 64;
204 }
205
206 // GEMINI FOLDING EVALUATIONS
207 print_header_centered("PROOF INDICIES - GEMINI FOLDING EVALUATIONS");
208 for (int size = 0; size < log_n; ++size) {
209 print_fr(pointer, "GEMINI_A_EVAL_" + std::to_string(size));
210 pointer += 32;
211 }
212 print_g1(pointer, "SHPLONK_Q");
213 pointer += 64;
214 print_g1(pointer, "KZG_QUOTIENT");
215 pointer += 64;
216
217 print_header_centered("PROOF INDICIES - COMPLETE");
218
219 // CHALLENGES
220 print_header_centered("CHALLENGES");
221 for (const auto& chall : challenges) {
222 print_fr(pointer, chall + "_CHALLENGE");
223 pointer += 32;
224 }
225 for (int alpha = 0; alpha < NUMBER_OF_ALPHAS; ++alpha) {
226 print_fr(pointer, "ALPHA_CHALLENGE_" + std::to_string(alpha));
227 pointer += 32;
228 }
229 for (int gate = 0; gate < log_n; ++gate) {
230 print_fr(pointer, "GATE_CHALLENGE_" + std::to_string(gate));
231 pointer += 32;
232 }
233 for (int sum_u = 0; sum_u < log_n; ++sum_u) {
234 print_fr(pointer, "SUM_U_CHALLENGE_" + std::to_string(sum_u));
235 pointer += 32;
236 }
237 print_header_centered("CHALLENGES - COMPLETE");
238
239 // RUNTIME MEMORY
240 print_header_centered("SUMCHECK - RUNTIME MEMORY");
241 print_header_centered("SUMCHECK - RUNTIME MEMORY - BARYCENTRIC");
242
243 // Barycentric domain (uses scratch space)
244 int bary_pointer = SCRATCH_SPACE_POINTER;
245 for (int i = 0; i < BARYCENTRIC_DOMAIN_SIZE; ++i) {
246 print_fr(bary_pointer, "BARYCENTRIC_LAGRANGE_DENOMINATOR_" + std::to_string(i) + "_LOC");
247 bary_pointer += 32;
248 }
249 for (int i = 0; i < log_n; ++i) {
250 for (int j = 0; j < BARYCENTRIC_DOMAIN_SIZE; ++j) {
251 print_fr(bary_pointer,
252 "BARYCENTRIC_DENOMINATOR_INVERSES_" + std::to_string(i) + "_" + std::to_string(j) + "_LOC");
253 bary_pointer += 32;
254 }
255 }
256 print_header_centered("SUMCHECK - RUNTIME MEMORY - BARYCENTRIC COMPLETE");
257
258 // SUBRELATION EVALUATIONS
259 print_header_centered("SUMCHECK - RUNTIME MEMORY - SUBRELATION EVALUATIONS");
260 for (int i = 0; i < NUMBER_OF_SUBRELATIONS; ++i) {
261 print_fr(pointer, "SUBRELATION_EVAL_" + std::to_string(i) + "_LOC");
262 pointer += 32;
263 }
264 print_header_centered("SUMCHECK - RUNTIME MEMORY - SUBRELATION EVALUATIONS COMPLETE");
265
266 // SUBRELATION INTERMEDIATES
267 print_header_centered("SUMCHECK - RUNTIME MEMORY - SUBRELATION INTERMEDIATES");
268 for (const auto& item : general_intermediates) {
269 print_fr(pointer, item);
270 pointer += 32;
271 }
272 for (const auto& item : subrelation_intermediates) {
273 print_fr(pointer, item);
274 pointer += 32;
275 }
276 print_header_centered("SUMCHECK - RUNTIME MEMORY - COMPLETE");
277
278 // SHPLEMINI RUNTIME MEMORY
279 print_header_centered("SHPLEMINI - RUNTIME MEMORY");
280 print_header_centered("SHPLEMINI - POWERS OF EVALUATION CHALLENGE");
281 out << "/// {{ UNROLL_SECTION_START POWERS_OF_EVALUATION_CHALLENGE }}\n";
282 for (int i = 0; i < log_n; ++i) {
283 print_fr(pointer, "POWERS_OF_EVALUATION_CHALLENGE_" + std::to_string(i) + "_LOC");
284 pointer += 32;
285 }
286 out << "/// {{ UNROLL_SECTION_END POWERS_OF_EVALUATION_CHALLENGE }}\n";
287 print_header_centered("SHPLEMINI - POWERS OF EVALUATION CHALLENGE COMPLETE");
288
289 // BATCH SCALARS
290 print_header_centered("SHPLEMINI - RUNTIME MEMORY - BATCH SCALARS");
291 const int BATCH_SIZE = 69;
292 for (int i = 0; i < BATCH_SIZE; ++i) {
293 print_fr(pointer, "BATCH_SCALAR_" + std::to_string(i) + "_LOC");
294 pointer += 32;
295 }
296 print_header_centered("SHPLEMINI - RUNTIME MEMORY - BATCH SCALARS COMPLETE");
297
298 // INVERSIONS
299 print_header_centered("SHPLEMINI - RUNTIME MEMORY - INVERSIONS");
300
301 // Inverted gemini denominators
302 int inv_pointer = SCRATCH_SPACE_POINTER;
303 for (int i = 0; i < log_n + 1; ++i) {
304 print_fr(inv_pointer, "INVERTED_GEMINI_DENOMINATOR_" + std::to_string(i) + "_LOC");
305 inv_pointer += 32;
306 }
307
308 // Batched evaluation accumulator inversions
309 for (int i = 0; i < log_n; ++i) {
310 print_fr(inv_pointer, "BATCH_EVALUATION_ACCUMULATOR_INVERSION_" + std::to_string(i) + "_LOC");
311 inv_pointer += 32;
312 }
313
314 out << "\n";
315 print_fr(inv_pointer, "BATCHED_EVALUATION_LOC");
316 inv_pointer += 32;
317 print_fr(inv_pointer, "CONSTANT_TERM_ACCUMULATOR_LOC");
318 inv_pointer += 32;
319
320 out << "\n";
321 print_fr(inv_pointer, "POS_INVERTED_DENOMINATOR");
322 inv_pointer += 32;
323 print_fr(inv_pointer, "NEG_INVERTED_DENOMINATOR");
324 inv_pointer += 32;
325
326 out << "\n";
327 out << "// LOG_N challenge pow minus u\n";
328 for (int i = 0; i < log_n; ++i) {
329 print_fr(inv_pointer, "INVERTED_CHALLENEGE_POW_MINUS_U_" + std::to_string(i) + "_LOC");
330 inv_pointer += 32;
331 }
332
333 out << "\n";
334 out << "// LOG_N pos_inverted_off\n";
335 for (int i = 0; i < log_n; ++i) {
336 print_fr(inv_pointer, "POS_INVERTED_DENOM_" + std::to_string(i) + "_LOC");
337 inv_pointer += 32;
338 }
339
340 out << "\n";
341 out << "// LOG_N neg_inverted_off\n";
342 for (int i = 0; i < log_n; ++i) {
343 print_fr(inv_pointer, "NEG_INVERTED_DENOM_" + std::to_string(i) + "_LOC");
344 inv_pointer += 32;
345 }
346
347 out << "\n";
348 for (int i = 0; i < log_n; ++i) {
349 print_fr(inv_pointer, "FOLD_POS_EVALUATIONS_" + std::to_string(i) + "_LOC");
350 inv_pointer += 32;
351 }
352
353 print_header_centered("SHPLEMINI RUNTIME MEMORY - INVERSIONS - COMPLETE");
354 print_header_centered("SHPLEMINI RUNTIME MEMORY - COMPLETE");
355
356 out << "\n";
357 print_fr(pointer, "LATER_SCRATCH_SPACE");
358 pointer += 32;
359
360 // Temporary space
361 print_header_centered("Temporary space");
362 for (int i = 0; i < 3 * log_n; ++i) {
363 print_fr(pointer, "TEMP_" + std::to_string(i) + "_LOC");
364 pointer += 32;
365 }
366 print_header_centered("Temporary space - COMPLETE");
367
368 // Scratch space aliases
369 out << "\n";
370 out << "// Aliases for scratch space\n";
371 out << "// TODO: work out the stack scheduling for these\n";
372 print_fr(0x00, "CHALL_POW_LOC");
373 print_fr(0x20, "SUMCHECK_U_LOC");
374 print_fr(0x40, "GEMINI_A_LOC");
375 out << "\n";
376 print_fr(0x00, "SS_POS_INV_DENOM_LOC");
377 print_fr(0x20, "SS_NEG_INV_DENOM_LOC");
378 print_fr(0x40, "SS_GEMINI_EVALS_LOC");
379
380 // EC aliases
381 out << "\n\n";
382 out << "// Aliases\n";
383 out << "// Aliases for wire values (Elliptic curve gadget)\n";
384 print_header_centered("SUMCHECK - MEMORY ALIASES");
385
386 return out.str();
387}
388
389// Source code for the Ultrahonk Solidity verifier.
390// It's expected that the AcirComposer will inject a library which will load the verification key into memory.
391// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
392static const char HONK_CONTRACT_OPT_SOURCE[] = R"(
393// SPDX-License-Identifier: Apache-2.0
394// Copyright 2022 Aztec
395pragma solidity ^0.8.27;
396
397interface IVerifier {
398 function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool);
399}
400
401
402
403uint256 constant NUMBER_OF_SUBRELATIONS = 28;
404uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8;
405uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9;
406uint256 constant NUMBER_OF_ENTITIES = 41;
407uint256 constant NUMBER_UNSHIFTED = 36;
408uint256 constant NUMBER_TO_BE_SHIFTED = 5;
409uint256 constant PAIRING_POINTS_SIZE = 8;
410
411uint256 constant VK_HASH = {{ VK_HASH }};
412uint256 constant CIRCUIT_SIZE = {{ CIRCUIT_SIZE }};
413uint256 constant LOG_N = {{ LOG_CIRCUIT_SIZE }};
414uint256 constant NUMBER_PUBLIC_INPUTS = {{ NUM_PUBLIC_INPUTS }};
415uint256 constant REAL_NUMBER_PUBLIC_INPUTS = {{ REAL_NUM_PUBLIC_INPUTS }};
416uint256 constant PUBLIC_INPUTS_OFFSET = 1;
417// LOG_N * 8
418uint256 constant NUMBER_OF_BARYCENTRIC_INVERSES = {{ NUMBER_OF_BARYCENTRIC_INVERSES }};
419
420contract HonkVerifier is IVerifier {
421 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
422 /* SLAB ALLOCATION */
423 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
432 // {{ SECTION_START MEMORY_LAYOUT }}
433 // {{ SECTION_END MEMORY_LAYOUT }}
434
435 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
436 /* SUMCHECK - MEMORY ALIASES */
437 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
438 uint256 internal constant EC_X_1 = W2_EVAL_LOC;
439 uint256 internal constant EC_Y_1 = W3_EVAL_LOC;
440 uint256 internal constant EC_X_2 = W1_SHIFT_EVAL_LOC;
441 uint256 internal constant EC_Y_2 = W4_SHIFT_EVAL_LOC;
442 uint256 internal constant EC_Y_3 = W3_SHIFT_EVAL_LOC;
443 uint256 internal constant EC_X_3 = W2_SHIFT_EVAL_LOC;
444
445 // Aliases for selectors (Elliptic curve gadget)
446 uint256 internal constant EC_Q_SIGN = QL_EVAL_LOC;
447 uint256 internal constant EC_Q_IS_DOUBLE = QM_EVAL_LOC;
448
449 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
450 /* CONSTANTS */
451 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
452 uint256 internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = 17; // -(-17)
453
454 // Auxiliary relation constants
455 // In the Non Native Field Arithmetic Relation, large field elements are broken up into 4 LIMBs of 68 `LIMB_SIZE` bits each.
456 uint256 internal constant LIMB_SIZE = 0x100000000000000000; // 2<<68
457
458 // In the Delta Range Check Relation, there is a range checking relation that can validate 14-bit range checks with only 1
459 // extra relation in the execution trace.
460 // For large range checks, we decompose them into a collection of 14-bit range checks.
461 uint256 internal constant SUBLIMB_SHIFT = 0x4000; // 2<<14
462
463 // Poseidon2 internal constants
464 // https://github.com/HorizenLabs/poseidon2/blob/main/poseidon2_rust_params.sage - derivation code
465 uint256 internal constant POS_INTERNAL_MATRIX_D_0 =
466 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7;
467 uint256 internal constant POS_INTERNAL_MATRIX_D_1 =
468 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b;
469 uint256 internal constant POS_INTERNAL_MATRIX_D_2 =
470 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15;
471 uint256 internal constant POS_INTERNAL_MATRIX_D_3 =
472 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b;
473
474 // Constants inspecting proof components
475 uint256 internal constant NUMBER_OF_UNSHIFTED_ENTITIES = 36;
476 // Shifted columns are columes that are duplicates of existing columns but right-shifted by 1
477 uint256 internal constant NUMBER_OF_SHIFTED_ENTITIES = 5;
478 uint256 internal constant TOTAL_NUMBER_OF_ENTITIES = 41;
479
480 // Constants for performing batch multiplication
481 uint256 internal constant ACCUMULATOR = 0x00;
482 uint256 internal constant ACCUMULATOR_2 = 0x40;
483 uint256 internal constant G1_LOCATION = 0x60;
484 uint256 internal constant G1_Y_LOCATION = 0x80;
485 uint256 internal constant SCALAR_LOCATION = 0xa0;
486
487 uint256 internal constant LOWER_127_MASK = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
488
489 // Group order
490 uint256 internal constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order
491
492 // Field order constants
493 // -1/2 mod p
494 uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;
495 uint256 internal constant P = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
496 uint256 internal constant P_SUB_1 = 21888242871839275222246405745257275088548364400416034343698204186575808495616;
497 uint256 internal constant P_SUB_2 = 21888242871839275222246405745257275088548364400416034343698204186575808495615;
498 uint256 internal constant P_SUB_3 = 21888242871839275222246405745257275088548364400416034343698204186575808495614;
499 uint256 internal constant P_SUB_4 = 21888242871839275222246405745257275088548364400416034343698204186575808495613;
500 uint256 internal constant P_SUB_5 = 21888242871839275222246405745257275088548364400416034343698204186575808495612;
501 uint256 internal constant P_SUB_6 = 21888242871839275222246405745257275088548364400416034343698204186575808495611;
502 uint256 internal constant P_SUB_7 = 21888242871839275222246405745257275088548364400416034343698204186575808495610;
503
504 // Barycentric evaluation constants
505 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_0 =
506 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51;
507 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_1 =
508 0x00000000000000000000000000000000000000000000000000000000000002d0;
509 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_2 =
510 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff11;
511 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_3 =
512 0x0000000000000000000000000000000000000000000000000000000000000090;
513 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_4 =
514 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff71;
515 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_5 =
516 0x00000000000000000000000000000000000000000000000000000000000000f0;
517 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_6 =
518 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31;
519 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_7 =
520 0x00000000000000000000000000000000000000000000000000000000000013b0;
521
522 // Constants for computing public input delta
523 uint256 internal constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28;
524
525 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
526 /* ERRORS */
527 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
528 // The errors match Errors.sol
529
530 bytes4 internal constant VALUE_GE_LIMB_MAX_SELECTOR = 0xeb73e0bd;
531 bytes4 internal constant VALUE_GE_GROUP_ORDER_SELECTOR = 0x607be13e;
532 bytes4 internal constant VALUE_GE_FIELD_ORDER_SELECTOR = 0x20a33589;
533 bytes4 internal constant SUMCHECK_FAILED_SELECTOR = 0x9fc3a218;
534 bytes4 internal constant SHPLEMINI_FAILED_SELECTOR = 0xa5d82e8a;
535 bytes4 internal constant POINT_AT_INFINITY_SELECTOR = 0x4ddaa5e5;
536
537 bytes4 internal constant PROOF_LENGTH_WRONG_WITH_LOG_N_SELECTOR = 0x59895a53;
538 bytes4 internal constant PUBLIC_INPUTS_LENGTH_WRONG_SELECTOR = 0xfa066593;
539
540 bytes4 internal constant MODEXP_FAILED_SELECTOR = 0xf442f163;
541
542 constructor() {}
543
544 function verify(
545 bytes calldata,
546 /*proof*/
547 bytes32[] calldata /*public_inputs*/
548 )
549 public
550 view
551 override
552 returns (bool)
553 {
554 // Load the proof from calldata in one large chunk
555 assembly {
556 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
557 /* LOAD VERIFCATION KEY */
558 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
559 // Write the verification key into memory
560 //
561 // Although defined at the top of the file, it is used towards the end of the algorithm when batching in the commitment scheme.
562 function loadVk() {
563 mstore(Q_L_X_LOC, {{ Q_L_X_LOC }})
564 mstore(Q_L_Y_LOC, {{ Q_L_Y_LOC }})
565 mstore(Q_R_X_LOC, {{ Q_R_X_LOC }})
566 mstore(Q_R_Y_LOC, {{ Q_R_Y_LOC }})
567 mstore(Q_O_X_LOC, {{ Q_O_X_LOC }})
568 mstore(Q_O_Y_LOC, {{ Q_O_Y_LOC }})
569 mstore(Q_4_X_LOC, {{ Q_4_X_LOC }})
570 mstore(Q_4_Y_LOC, {{ Q_4_Y_LOC }})
571 mstore(Q_M_X_LOC, {{ Q_M_X_LOC }})
572 mstore(Q_M_Y_LOC, {{ Q_M_Y_LOC }})
573 mstore(Q_C_X_LOC, {{ Q_C_X_LOC }})
574 mstore(Q_C_Y_LOC, {{ Q_C_Y_LOC }})
575 mstore(Q_LOOKUP_X_LOC, {{ Q_LOOKUP_X_LOC }})
576 mstore(Q_LOOKUP_Y_LOC, {{ Q_LOOKUP_Y_LOC }})
577 mstore(Q_ARITH_X_LOC, {{ Q_ARITH_X_LOC }})
578 mstore(Q_ARITH_Y_LOC, {{ Q_ARITH_Y_LOC }})
579 mstore(Q_DELTA_RANGE_X_LOC, {{ Q_DELTA_RANGE_X_LOC }})
580 mstore(Q_DELTA_RANGE_Y_LOC, {{ Q_DELTA_RANGE_Y_LOC }})
581 mstore(Q_ELLIPTIC_X_LOC, {{ Q_ELLIPTIC_X_LOC }})
582 mstore(Q_ELLIPTIC_Y_LOC, {{ Q_ELLIPTIC_Y_LOC }})
583 mstore(Q_MEMORY_X_LOC, {{ Q_MEMORY_X_LOC }})
584 mstore(Q_MEMORY_Y_LOC, {{ Q_MEMORY_Y_LOC }})
585 mstore(Q_NNF_X_LOC, {{ Q_NNF_X_LOC }})
586 mstore(Q_NNF_Y_LOC, {{ Q_NNF_Y_LOC }})
587 mstore(Q_POSEIDON_2_EXTERNAL_X_LOC, {{ Q_POSEIDON_2_EXTERNAL_X_LOC }})
588 mstore(Q_POSEIDON_2_EXTERNAL_Y_LOC, {{ Q_POSEIDON_2_EXTERNAL_Y_LOC }})
589 mstore(Q_POSEIDON_2_INTERNAL_X_LOC, {{ Q_POSEIDON_2_INTERNAL_X_LOC }})
590 mstore(Q_POSEIDON_2_INTERNAL_Y_LOC, {{ Q_POSEIDON_2_INTERNAL_Y_LOC }})
591 mstore(SIGMA_1_X_LOC, {{ SIGMA_1_X_LOC }})
592 mstore(SIGMA_1_Y_LOC, {{ SIGMA_1_Y_LOC }})
593 mstore(SIGMA_2_X_LOC, {{ SIGMA_2_X_LOC }})
594 mstore(SIGMA_2_Y_LOC, {{ SIGMA_2_Y_LOC }})
595 mstore(SIGMA_3_X_LOC, {{ SIGMA_3_X_LOC }})
596 mstore(SIGMA_3_Y_LOC, {{ SIGMA_3_Y_LOC }})
597 mstore(SIGMA_4_X_LOC, {{ SIGMA_4_X_LOC }})
598 mstore(SIGMA_4_Y_LOC, {{ SIGMA_4_Y_LOC }})
599 mstore(TABLE_1_X_LOC, {{ TABLE_1_X_LOC }})
600 mstore(TABLE_1_Y_LOC, {{ TABLE_1_Y_LOC }})
601 mstore(TABLE_2_X_LOC, {{ TABLE_2_X_LOC }})
602 mstore(TABLE_2_Y_LOC, {{ TABLE_2_Y_LOC }})
603 mstore(TABLE_3_X_LOC, {{ TABLE_3_X_LOC }})
604 mstore(TABLE_3_Y_LOC, {{ TABLE_3_Y_LOC }})
605 mstore(TABLE_4_X_LOC, {{ TABLE_4_X_LOC }})
606 mstore(TABLE_4_Y_LOC, {{ TABLE_4_Y_LOC }})
607 mstore(ID_1_X_LOC, {{ ID_1_X_LOC }})
608 mstore(ID_1_Y_LOC, {{ ID_1_Y_LOC }})
609 mstore(ID_2_X_LOC, {{ ID_2_X_LOC }})
610 mstore(ID_2_Y_LOC, {{ ID_2_Y_LOC }})
611 mstore(ID_3_X_LOC, {{ ID_3_X_LOC }})
612 mstore(ID_3_Y_LOC, {{ ID_3_Y_LOC }})
613 mstore(ID_4_X_LOC, {{ ID_4_X_LOC }})
614 mstore(ID_4_Y_LOC, {{ ID_4_Y_LOC }})
615 mstore(LAGRANGE_FIRST_X_LOC, {{ LAGRANGE_FIRST_X_LOC }})
616 mstore(LAGRANGE_FIRST_Y_LOC, {{ LAGRANGE_FIRST_Y_LOC }})
617 mstore(LAGRANGE_LAST_X_LOC, {{ LAGRANGE_LAST_X_LOC }})
618 mstore(LAGRANGE_LAST_Y_LOC, {{ LAGRANGE_LAST_Y_LOC }})
619 }
620
621 // Prime field order - placing on the stack
622 let p := P
623
624 {
625 let proof_ptr := add(calldataload(0x04), 0x24)
626
627 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
628 /* VALIDATE INPUT LENGTHS */
629 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
630 // Validate proof byte length matches expected size for this circuit's LOG_N.
631 // Expected = (8*2 + LOG_N*BATCHED_RELATION_PARTIAL_LENGTH + NUMBER_OF_ENTITIES
632 // + (LOG_N-1)*2 + LOG_N + 2*2 + PAIRING_POINTS_SIZE) * 32
633 {
634 let expected_proof_size := mul(
635 add(
636 add(
637 add(16, mul(LOG_N, BATCHED_RELATION_PARTIAL_LENGTH)),
638 add(NUMBER_OF_ENTITIES, mul(sub(LOG_N, 1), 2))
639 ),
640 add(add(LOG_N, 4), PAIRING_POINTS_SIZE)
641 ),
642 32
643 )
644 let proof_length := calldataload(add(calldataload(0x04), 0x04))
645 if iszero(eq(proof_length, expected_proof_size)) {
646 mstore(0x00, PROOF_LENGTH_WRONG_WITH_LOG_N_SELECTOR)
647 mstore(0x04, LOG_N)
648 mstore(0x24, proof_length)
649 mstore(0x44, expected_proof_size)
650 revert(0x00, 0x64)
651 }
652 }
653 // Validate public inputs array length matches expected count.
654 {
655 let pi_count := calldataload(add(calldataload(0x24), 0x04))
656 if iszero(eq(pi_count, REAL_NUMBER_PUBLIC_INPUTS)) {
657 mstore(0x00, PUBLIC_INPUTS_LENGTH_WRONG_SELECTOR)
658 revert(0x00, 0x04)
659 }
660 }
661
662 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
663 /* GENERATE CHALLENGES */
664 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
665 /*
666 * Proof points (affine coordinates) in the proof are in the following format, where offset is
667 * the offset in the entire proof until the first bit of the x coordinate
668 * offset + 0x00: x
669 * offset + 0x20: y
670 */
671
672 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
673 /* GENERATE ETA CHALLENGE */
674 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
675 /* Eta challenge participants
676 * - circuit size
677 * - number of public inputs
678 * - public inputs offset
679 * - w1
680 * - w2
681 * - w3
682 *
683 * Where circuit size, number of public inputs and public inputs offset are all 32 byte values
684 * and w1,w2,w3 are all proof points values
685 */
686
687 mstore(0x00, VK_HASH)
688
689 let public_inputs_start := add(calldataload(0x24), 0x24)
690 let public_inputs_size := mul(REAL_NUMBER_PUBLIC_INPUTS, 0x20)
691
692 // Copy the public inputs into the eta buffer
693 calldatacopy(0x20, public_inputs_start, public_inputs_size)
694
695 // Copy Pairing points into eta buffer
696 let public_inputs_end := add(0x20, public_inputs_size)
697
698 calldatacopy(public_inputs_end, proof_ptr, 0x100)
699
700 // 0x20 * 8 = 0x100 (8 pairing point limbs)
701 // End of public inputs + pairing points
702 calldatacopy(add(0x120, public_inputs_size), add(proof_ptr, 0x100), 0x100)
703
704 // 0x1e0 = 1 * 32 bytes + 3 * 64 bytes for (w1,w2,w3) + 0x100 for pairing points
705 let eta_input_length := add(0x1e0, public_inputs_size)
706
707 // Get single eta challenge and compute powers (eta, eta², eta³)
708 let prev_challenge := mod(keccak256(0x00, eta_input_length), p)
709 mstore(0x00, prev_challenge)
710
711 let eta := and(prev_challenge, LOWER_127_MASK)
712 let eta_two := mulmod(eta, eta, p)
713 let eta_three := mulmod(eta_two, eta, p)
714
715 mstore(ETA_CHALLENGE, eta)
716 mstore(ETA_TWO_CHALLENGE, eta_two)
717 mstore(ETA_THREE_CHALLENGE, eta_three)
718
719 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
720 /* LOAD PROOF INTO MEMORY */
721 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
722 // As all of our proof points are written in contiguous parts of memory, we call use a single
723 // calldatacopy to place all of our proof into the correct memory regions
724 // We copy the entire proof into memory as we must hash each proof section for challenge
725 // evaluation
726 // The last item in the proof, and the first item in the proof (pairing point 0)
727 let proof_size := sub(ETA_CHALLENGE, PAIRING_POINT_0_X_0_LOC)
728
729 calldatacopy(PAIRING_POINT_0_X_0_LOC, proof_ptr, proof_size)
730
731 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
732 /* VALIDATE PROOF INPUTS */
733 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
734 // Validate all proof elements are within their expected ranges.
735 // Pairing limbs: lo < 2^136, hi < 2^120. G1 coordinates < Q. Fr elements < P.
736 {
737 let valid := true
738 let lo_limb_max := shl(136, 1)
739 let hi_limb_max := shl(120, 1)
740 let q_mod := Q
741
742 // 1. Pairing limbs: lo < 2^136, hi < 2^120 (4 pairs, stride 0x40)
743 let ptr := PAIRING_POINT_0_X_0_LOC
744 for {} lt(ptr, W_L_X_LOC) { ptr := add(ptr, 0x40) } {
745 valid := and(valid, lt(mload(ptr), lo_limb_max))
746 valid := and(valid, lt(mload(add(ptr, 0x20)), hi_limb_max))
747 }
748 if iszero(valid) {
749 mstore(0x00, VALUE_GE_LIMB_MAX_SELECTOR)
750 revert(0x00, 0x04)
751 }
752
753 // 2. G1 coordinates: each < Q
754 // - Witness commitments: W_L through Z_PERM (16 slots)
755 for { ptr := W_L_X_LOC } lt(ptr, SUMCHECK_UNIVARIATE_0_0_LOC) { ptr := add(ptr, 0x20) } {
756 valid := and(valid, lt(mload(ptr), q_mod))
757 }
758 // - Gemini fold commitments (28 slots)
759 for { ptr := GEMINI_FOLD_UNIVARIATE_0_X_LOC } lt(ptr, GEMINI_A_EVAL_0) { ptr := add(ptr, 0x20) } {
760 valid := and(valid, lt(mload(ptr), q_mod))
761 }
762 // - Shplonk Q + KZG quotient (4 slots)
763 for { ptr := SHPLONK_Q_X_LOC } lt(ptr, ETA_CHALLENGE) { ptr := add(ptr, 0x20) } {
764 valid := and(valid, lt(mload(ptr), q_mod))
765 }
766 if iszero(valid) {
767 mstore(0x00, VALUE_GE_GROUP_ORDER_SELECTOR)
768 revert(0x00, 0x04)
769 }
770
771 // 2b. G1 points: reject point at infinity (0,0).
772 // EVM precompiles silently treat (0,0) as the identity element,
773 // which could zero out commitments. On-curve validation (y² = x³ + 3)
774 // is handled by the ecAdd/ecMul precompiles per EIP-196.
775 // - Witness commitments (8 points, stride 0x40)
776 for { ptr := W_L_X_LOC } lt(ptr, SUMCHECK_UNIVARIATE_0_0_LOC) { ptr := add(ptr, 0x40) } {
777 valid := and(valid, iszero(iszero(or(mload(ptr), mload(add(ptr, 0x20))))))
778 }
779 // - Gemini fold commitments (14 points, stride 0x40)
780 for { ptr := GEMINI_FOLD_UNIVARIATE_0_X_LOC } lt(ptr, GEMINI_A_EVAL_0) { ptr := add(ptr, 0x40) } {
781 valid := and(valid, iszero(iszero(or(mload(ptr), mload(add(ptr, 0x20))))))
782 }
783 // - Shplonk Q + KZG quotient (2 points, stride 0x40)
784 for { ptr := SHPLONK_Q_X_LOC } lt(ptr, ETA_CHALLENGE) { ptr := add(ptr, 0x40) } {
785 valid := and(valid, iszero(iszero(or(mload(ptr), mload(add(ptr, 0x20))))))
786 }
787 if iszero(valid) {
788 mstore(0x00, POINT_AT_INFINITY_SELECTOR)
789 revert(0x00, 0x04)
790 }
791
792 // 3. Fr elements: each < P
793 // - Sumcheck univariates + evaluations (161 slots)
794 for { ptr := SUMCHECK_UNIVARIATE_0_0_LOC } lt(ptr, GEMINI_FOLD_UNIVARIATE_0_X_LOC) {
795 ptr := add(ptr, 0x20)
796 } {
797 valid := and(valid, lt(mload(ptr), p))
798 }
799 // - Gemini evaluations (15 slots)
800 for { ptr := GEMINI_A_EVAL_0 } lt(ptr, SHPLONK_Q_X_LOC) { ptr := add(ptr, 0x20) } {
801 valid := and(valid, lt(mload(ptr), p))
802 }
803 if iszero(valid) {
804 mstore(0x00, VALUE_GE_FIELD_ORDER_SELECTOR)
805 revert(0x00, 0x04)
806 }
807 }
808
809 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
810 /* GENERATE BETA and GAMMAA CHALLENGE */
811 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
812
813 // Generate Beta and Gamma Chalenges
814 // - prevChallenge
815 // - LOOKUP_READ_COUNTS
816 // - LOOKUP_READ_TAGS
817 // - W4
818 mcopy(0x20, LOOKUP_READ_COUNTS_X_LOC, 0xc0)
819
820 prev_challenge := mod(keccak256(0x00, 0xe0), p)
821 mstore(0x00, prev_challenge)
822 let beta := and(prev_challenge, LOWER_127_MASK)
823 let gamma := shr(127, prev_challenge)
824
825 mstore(BETA_CHALLENGE, beta)
826 mstore(GAMMA_CHALLENGE, gamma)
827
828 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
829 /* ALPHA CHALLENGES */
830 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
831 // Generate Alpha challenges - non-linearise the gate contributions
832 //
833 // There are 26 total subrelations in this honk relation, we do not need to non linearise the first sub relation.
834 // There are 25 total gate contributions, a gate contribution is analogous to
835 // a custom gate, it is an expression which must evaluate to zero for each
836 // row in the constraint matrix
837 //
838 // If we do not non-linearise sub relations, then sub relations which rely
839 // on the same wire will interact with each other's sums.
840
841 mcopy(0x20, LOOKUP_INVERSES_X_LOC, 0x80)
842
843 // Generate single alpha challenge and compute its powers
844 prev_challenge := mod(keccak256(0x00, 0xa0), p)
845 mstore(0x00, prev_challenge)
846 let alpha := and(prev_challenge, LOWER_127_MASK)
847 mstore(ALPHA_CHALLENGE_0, alpha)
848
849 // Compute powers of alpha: alpha^2, alpha^3, ..., alpha^26
850 let alpha_off_set := ALPHA_CHALLENGE_1
851 for {} lt(alpha_off_set, add(ALPHA_CHALLENGE_26, 0x20)) {} {
852 let prev_alpha := mload(sub(alpha_off_set, 0x20))
853 mstore(alpha_off_set, mulmod(prev_alpha, alpha, p))
854 alpha_off_set := add(alpha_off_set, 0x20)
855 }
856
857 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
858 /* GATE CHALLENGES */
859 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
860
861 // Store the first gate challenge
862 prev_challenge := mod(keccak256(0x00, 0x20), p)
863 mstore(0x00, prev_challenge)
864 let gate_challenge := and(prev_challenge, LOWER_127_MASK)
865 mstore(GATE_CHALLENGE_0, gate_challenge)
866
867 let gate_off := GATE_CHALLENGE_1
868 for {} lt(gate_off, SUM_U_CHALLENGE_0) {} {
869 let prev := mload(sub(gate_off, 0x20))
870
871 mstore(gate_off, mulmod(prev, prev, p))
872 gate_off := add(gate_off, 0x20)
873 }
874
875 // Sumcheck Univariate challenges
876 // The algebraic relations of the Honk protocol are max degree-7.
877 // To prove satifiability, we multiply the relation by a random (POW) polynomial. We do this as we want all of our relations
878 // to be zero on every row - not for the sum of the relations to be zero. (Which is all sumcheck can do without this modification)
879 //
880 // As a result, in every round of sumcheck, the prover sends an degree-8 univariate polynomial.
881 // The sumcheck univariate challenge produces a challenge for each round of sumcheck, hashing the prev_challenge with
882 // a hash of the degree 8 univariate polynomial provided by the prover.
883 //
884 // 8 points are sent as it is enough to uniquely identify the polynomial
885 let read_off := SUMCHECK_UNIVARIATE_0_0_LOC
886 let write_off := SUM_U_CHALLENGE_0
887 for {} lt(read_off, QM_EVAL_LOC) {} {
888 // Increase by 20 * batched relation length (8)
889 // 0x20 * 0x8 = 0x100
890 mcopy(0x20, read_off, 0x100)
891
892 // Hash 0x100 + 0x20 (prev hash) = 0x120
893 prev_challenge := mod(keccak256(0x00, 0x120), p)
894 mstore(0x00, prev_challenge)
895
896 let sumcheck_u_challenge := and(prev_challenge, LOWER_127_MASK)
897 mstore(write_off, sumcheck_u_challenge)
898
899 // Progress read / write pointers
900 read_off := add(read_off, 0x100)
901 write_off := add(write_off, 0x20)
902 }
903
904 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
905 /* RHO CHALLENGES */
906 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
907 // The RHO challenge is the hash of the evaluations of all of the wire values
908 // As per usual, it includes the previous challenge
909 // Evaluations of the following wires and their shifts (for relevant wires):
910 // - QM
911 // - QC
912 // - Q1 (QL)
913 // - Q2 (QR)
914 // - Q3 (QO)
915 // - Q4
916 // - QLOOKUP
917 // - QARITH
918 // - QRANGE
919 // - QELLIPTIC
920 // - QMEMORY
921 // - QNNF (NNF = Non Native Field)
922 // - QPOSEIDON2_EXTERNAL
923 // - QPOSEIDON2_INTERNAL
924 // - SIGMA1
925 // - SIGMA2
926 // - SIGMA3
927 // - SIGMA4
928 // - ID1
929 // - ID2
930 // - ID3
931 // - ID4
932 // - TABLE1
933 // - TABLE2
934 // - TABLE3
935 // - TABLE4
936 // - W1 (WL)
937 // - W2 (WR)
938 // - W3 (WO)
939 // - W4
940 // - Z_PERM
941 // - LOOKUP_INVERSES
942 // - LOOKUP_READ_COUNTS
943 // - LOOKUP_READ_TAGS
944 // - W1_SHIFT
945 // - W2_SHIFT
946 // - W3_SHIFT
947 // - W4_SHIFT
948 // - Z_PERM_SHIFT
949 //
950 // Hash of all of the above evaluations
951 // Number of bytes to copy = 0x20 * NUMBER_OF_ENTITIES (41) = 0x520
952 mcopy(0x20, QM_EVAL_LOC, 0x520)
953 prev_challenge := mod(keccak256(0x00, 0x540), p)
954 mstore(0x00, prev_challenge)
955
956 let rho := and(prev_challenge, LOWER_127_MASK)
957
958 mstore(RHO_CHALLENGE, rho)
959
960 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
961 /* GEMINI R CHALLENGE */
962 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
963 // The Gemini R challenge contains a of all of commitments to all of the univariates
964 // evaluated in the Gemini Protocol
965 // So for multivariate polynomials in l variables, we will hash l - 1 commitments.
966 // For this implementation, we have logN number of of rounds and thus logN - 1 committments
967 // The format of these commitments are proof points, which are explained above
968 // 0x40 * (logN - 1)
969
970 mcopy(0x20, GEMINI_FOLD_UNIVARIATE_0_X_LOC, {{ GEMINI_FOLD_UNIVARIATE_LENGTH }})
971
972 prev_challenge := mod(keccak256(0x00, {{ GEMINI_FOLD_UNIVARIATE_HASH_LENGTH }}), p)
973 mstore(0x00, prev_challenge)
974
975 let geminiR := and(prev_challenge, LOWER_127_MASK)
976
977 mstore(GEMINI_R_CHALLENGE, geminiR)
978
979 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
980 /* SHPLONK NU CHALLENGE */
981 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
982 // The shplonk nu challenge hashes the evaluations of the above gemini univariates
983 // 0x20 * logN = 0x20 * 15 = 0x1e0
984
985 mcopy(0x20, GEMINI_A_EVAL_0, {{ GEMINI_EVALS_LENGTH }})
986 prev_challenge := mod(keccak256(0x00, {{ GEMINI_EVALS_HASH_LENGTH }}), p)
987 mstore(0x00, prev_challenge)
988
989 let shplonkNu := and(prev_challenge, LOWER_127_MASK)
990 mstore(SHPLONK_NU_CHALLENGE, shplonkNu)
991
992 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
993 /* SHPLONK Z CHALLENGE */
994 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
995 // Generate Shplonk Z
996 // Hash of the single shplonk Q commitment
997 mcopy(0x20, SHPLONK_Q_X_LOC, 0x40)
998 prev_challenge := mod(keccak256(0x00, 0x60), p)
999
1000 let shplonkZ := and(prev_challenge, LOWER_127_MASK)
1001 mstore(SHPLONK_Z_CHALLENGE, shplonkZ)
1002
1003 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1004 /* CHALLENGES COMPLETE */
1005 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1006 }
1007
1008 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1009 /* PUBLIC INPUT DELTA */
1010 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1039 {
1040 let beta := mload(BETA_CHALLENGE)
1041 let gamma := mload(GAMMA_CHALLENGE)
1042 let pub_off := PUBLIC_INPUTS_OFFSET
1043
1044 let numerator_value := 1
1045 let denominator_value := 1
1046
1047 let p_clone := p // move p to the front of the stack
1048
1049 // Assume offset is less than p
1050 // numerator_acc = gamma + (beta * (PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset))
1051 let numerator_acc :=
1052 addmod(gamma, mulmod(beta, add(PERMUTATION_ARGUMENT_VALUE_SEPARATOR, pub_off), p_clone), p_clone)
1053 // demonimator_acc = gamma - (beta * (offset + 1))
1054 let beta_x_off := mulmod(beta, add(pub_off, 1), p_clone)
1055 let denominator_acc := addmod(gamma, sub(p_clone, beta_x_off), p_clone)
1056
1057 let valid_inputs := true
1058 // Load the starting point of the public inputs (jump over the selector and the length of public inputs [0x24])
1059 let public_inputs_ptr := add(calldataload(0x24), 0x24)
1060
1061 // endpoint_ptr = public_inputs_ptr + num_inputs * 0x20. // every public input is 0x20 bytes
1062 let endpoint_ptr := add(public_inputs_ptr, mul(REAL_NUMBER_PUBLIC_INPUTS, 0x20))
1063
1064 for {} lt(public_inputs_ptr, endpoint_ptr) { public_inputs_ptr := add(public_inputs_ptr, 0x20) } {
1065 // Get public inputs from calldata
1066 let input := calldataload(public_inputs_ptr)
1067
1068 valid_inputs := and(valid_inputs, lt(input, p_clone))
1069
1070 numerator_value := mulmod(numerator_value, addmod(numerator_acc, input, p_clone), p_clone)
1071 denominator_value := mulmod(denominator_value, addmod(denominator_acc, input, p_clone), p_clone)
1072
1073 numerator_acc := addmod(numerator_acc, beta, p_clone)
1074 denominator_acc := addmod(denominator_acc, sub(p_clone, beta), p_clone)
1075 }
1076
1077 // Revert if not all public inputs are field elements (i.e. < p)
1078 if iszero(valid_inputs) {
1079 mstore(0x00, VALUE_GE_FIELD_ORDER_SELECTOR)
1080 revert(0x00, 0x04)
1081 }
1082
1083 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1084 /* PUBLIC INPUT DELTA - Pairing points accum */
1085 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1086 // Pairing points contribution to public inputs delta
1087 let pairing_points_ptr := PAIRING_POINT_0_X_0_LOC
1088 for {} lt(pairing_points_ptr, W_L_X_LOC) { pairing_points_ptr := add(pairing_points_ptr, 0x20) } {
1089 let input := mload(pairing_points_ptr)
1090
1091 numerator_value := mulmod(numerator_value, addmod(numerator_acc, input, p_clone), p_clone)
1092 denominator_value := mulmod(denominator_value, addmod(denominator_acc, input, p_clone), p_clone)
1093
1094 numerator_acc := addmod(numerator_acc, beta, p_clone)
1095 denominator_acc := addmod(denominator_acc, sub(p_clone, beta), p_clone)
1096 }
1097
1098 mstore(PUBLIC_INPUTS_DELTA_NUMERATOR_CHALLENGE, numerator_value)
1099 mstore(PUBLIC_INPUTS_DELTA_DENOMINATOR_CHALLENGE, denominator_value)
1100
1101 // TODO: batch with barycentric inverses
1102 let dom_inverse := 0
1103 {
1104 mstore(0, 0x20)
1105 mstore(0x20, 0x20)
1106 mstore(0x40, 0x20)
1107 mstore(0x60, denominator_value)
1108 mstore(0x80, P_SUB_2)
1109 mstore(0xa0, p)
1110 if iszero(staticcall(gas(), 0x05, 0x00, 0xc0, 0x00, 0x20)) {
1111 mstore(0x00, MODEXP_FAILED_SELECTOR)
1112 revert(0x00, 0x04)
1113 }
1114 // 1 / (0 . 1 . 2 . 3 . 4 . 5 . 6 . 7)
1115 dom_inverse := mload(0x00)
1116 }
1117 // Calculate the public inputs delta
1118 mstore(PUBLIC_INPUTS_DELTA_NUMERATOR_CHALLENGE, mulmod(numerator_value, dom_inverse, p))
1119 }
1120 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1121 /* PUBLIC INPUT DELTA - complete */
1122 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1123
1124 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1125 /* SUMCHECK */
1126 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1127 //
1128 // Sumcheck is used to prove that every relation 0 on each row of the witness.
1129 //
1130 // Given each of the columns of our trace is a multilinear polynomial 𝑃1,…,𝑃𝑁∈𝔽[𝑋0,…,𝑋𝑑−1]. We run sumcheck over the polynomial
1131 //
1132 // 𝐹̃ (𝑋0,…,𝑋𝑑−1)=𝑝𝑜𝑤𝛽(𝑋0,…,𝑋𝑑−1)⋅𝐹(𝑃1(𝑋0,…,𝑋𝑑−1),…,𝑃𝑁(𝑋0,…,𝑋𝑑−1))
1133 //
1134 // The Pow polynomial is a random polynomial that allows us to ceritify that the relations sum to 0 on each row of the witness,
1135 // rather than the entire sum just targeting 0.
1136 //
1137 // Each polynomial P in our implementation are the polys in the proof and the verification key. (W_1, W_2, W_3, W_4, Z_PERM, etc....)
1138 //
1139 // We start with a LOG_N variate multilinear polynomial, each round fixes a variable to a challenge value.
1140 // Each round the prover sends a round univariate poly, since the degree of our honk relations is 7 + the pow polynomial the prover
1141 // sends a degree-8 univariate on each round.
1142 // This is sent efficiently by sending 8 values, enough to represent a unique polynomial.
1143 // Barycentric evaluation is used to evaluate the polynomial at any point on the domain, given these 8 unique points.
1144 //
1145 // In the sumcheck protocol, the target sum for each round is the sum of the round univariate evaluated on 0 and 1.
1146 // 𝜎𝑖=?𝑆̃ 𝑖(0)+𝑆̃ 𝑖(1)
1147 // This is efficiently checked as S(0) and S(1) are sent by the prover as values of the round univariate.
1148 //
1149 // We compute the next challenge by evaluating the round univariate at a random challenge value.
1150 // 𝜎𝑖+1←𝑆̃ 𝑖(𝑢𝑖)
1151 // This evaluation is performed via barycentric evaluation.
1152 //
1153 // Once we have reduced the multilinear polynomials into single dimensional polys, we check the entire sumcheck relation matches the target sum.
1154 //
1155 // Below this is composed of 8 relations:
1156 // 1. Arithmetic relation - constrains arithmetic
1157 // 2. Permutaiton Relation - efficiently encodes copy constraints
1158 // 3. Log Derivative Lookup Relation - used for lookup operations
1159 // 4. Delta Range Relation - used for efficient range checks
1160 // 5. Memory Relation - used for efficient memory operations
1161 // 6. NNF Relation - used for efficient Non Native Field operations
1162 // 7. Poseidon2 External Relation - used for efficient in-circuit hashing
1163 // 8. Poseidon2 Internal Relation - used for efficient in-circuit hashing
1164 //
1165 // These are batched together and evaluated at the same time using the alpha challenges.
1166 //
1167 {
1168 // We write the barycentric domain values into memory
1169 // These are written once per program execution, and reused across all
1170 // sumcheck rounds
1171 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_0_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_0)
1172 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_1_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_1)
1173 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_2_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_2)
1174 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_3_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_3)
1175 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_4_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_4)
1176 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_5_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_5)
1177 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_6_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_6)
1178 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_7_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_7)
1179
1180 // Compute the target sums for each round of sumcheck
1181 {
1182 // This requires the barycentric inverses to be computed for each round
1183 // Write all of the non inverted barycentric denominators into memory
1184 let accumulator := 1
1185 let temp := LATER_SCRATCH_SPACE
1186 let bary_centric_inverses_off := BARYCENTRIC_DENOMINATOR_INVERSES_0_0_LOC
1187 {
1188 let round_challenge_off := SUM_U_CHALLENGE_0
1189 for { let round := 0 } lt(round, LOG_N) { round := add(round, 1) } {
1190 let round_challenge := mload(round_challenge_off)
1191 let bary_lagrange_denominator_off := BARYCENTRIC_LAGRANGE_DENOMINATOR_0_LOC
1192
1193 // Unrolled as this loop as it only has 8 iterations
1194 {
1195 let bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
1196 let pre_inv :=
1197 mulmod(
1198 bary_lagrange_denominator,
1199 addmod(round_challenge, p, p), // sub(p, 0) = p
1200 p
1201 )
1202 mstore(bary_centric_inverses_off, pre_inv)
1203 temp := add(temp, 0x20)
1204 mstore(temp, accumulator)
1205 accumulator := mulmod(accumulator, pre_inv, p)
1206
1207 // increase offsets
1208 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
1209 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
1210
1211 // barycentric_index = 1
1212 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
1213 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, P_SUB_1, p), p)
1214 mstore(bary_centric_inverses_off, pre_inv)
1215 temp := add(temp, 0x20)
1216 mstore(temp, accumulator)
1217 accumulator := mulmod(accumulator, pre_inv, p)
1218
1219 // increase offsets
1220 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
1221 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
1222
1223 // barycentric_index = 2
1224 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
1225 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, P_SUB_2, p), p)
1226 mstore(bary_centric_inverses_off, pre_inv)
1227 temp := add(temp, 0x20)
1228 mstore(temp, accumulator)
1229 accumulator := mulmod(accumulator, pre_inv, p)
1230
1231 // increase offsets
1232 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
1233 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
1234
1235 // barycentric_index = 3
1236 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
1237 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, P_SUB_3, p), p)
1238 mstore(bary_centric_inverses_off, pre_inv)
1239 temp := add(temp, 0x20)
1240 mstore(temp, accumulator)
1241 accumulator := mulmod(accumulator, pre_inv, p)
1242
1243 // increase offsets
1244 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
1245 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
1246
1247 // barycentric_index = 4
1248 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
1249 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, P_SUB_4, p), p)
1250 mstore(bary_centric_inverses_off, pre_inv)
1251 temp := add(temp, 0x20)
1252 mstore(temp, accumulator)
1253 accumulator := mulmod(accumulator, pre_inv, p)
1254
1255 // increase offsets
1256 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
1257 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
1258
1259 // barycentric_index = 5
1260 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
1261 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, P_SUB_5, p), p)
1262 mstore(bary_centric_inverses_off, pre_inv)
1263 temp := add(temp, 0x20)
1264 mstore(temp, accumulator)
1265 accumulator := mulmod(accumulator, pre_inv, p)
1266
1267 // increase offsets
1268 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
1269 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
1270
1271 // barycentric_index = 6
1272 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
1273 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, P_SUB_6, p), p)
1274 mstore(bary_centric_inverses_off, pre_inv)
1275 temp := add(temp, 0x20)
1276 mstore(temp, accumulator)
1277 accumulator := mulmod(accumulator, pre_inv, p)
1278
1279 // increase offsets
1280 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
1281 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
1282
1283 // barycentric_index = 7
1284 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
1285 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, P_SUB_7, p), p)
1286 mstore(bary_centric_inverses_off, pre_inv)
1287 temp := add(temp, 0x20)
1288 mstore(temp, accumulator)
1289 accumulator := mulmod(accumulator, pre_inv, p)
1290
1291 // increase offsets
1292 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
1293 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
1294 }
1295 round_challenge_off := add(round_challenge_off, 0x20)
1296 }
1297 }
1298
1299 // Invert all of the barycentric denominators as a single batch
1300 {
1301 {
1302 mstore(0, 0x20)
1303 mstore(0x20, 0x20)
1304 mstore(0x40, 0x20)
1305 mstore(0x60, accumulator)
1306 mstore(0x80, P_SUB_2)
1307 mstore(0xa0, p)
1308 if iszero(staticcall(gas(), 0x05, 0x00, 0xc0, 0x00, 0x20)) {
1309 mstore(0x00, MODEXP_FAILED_SELECTOR)
1310 revert(0x00, 0x04)
1311 }
1312
1313 accumulator := mload(0x00)
1314 }
1315
1316 // Normalise as last loop will have incremented the offset
1317 bary_centric_inverses_off := sub(bary_centric_inverses_off, 0x20)
1318 for {} gt(bary_centric_inverses_off, BARYCENTRIC_LAGRANGE_DENOMINATOR_7_LOC) {
1319 bary_centric_inverses_off := sub(bary_centric_inverses_off, 0x20)
1320 } {
1321 let tmp := mulmod(accumulator, mload(temp), p)
1322 accumulator := mulmod(accumulator, mload(bary_centric_inverses_off), p)
1323 mstore(bary_centric_inverses_off, tmp)
1324
1325 temp := sub(temp, 0x20)
1326 }
1327 }
1328 }
1329
1330 let valid := true
1331 let round_target := 0
1332 let pow_partial_evaluation := 1
1333 let gate_challenge_off := GATE_CHALLENGE_0
1334 let round_univariates_off := SUMCHECK_UNIVARIATE_0_0_LOC
1335
1336 let challenge_off := SUM_U_CHALLENGE_0
1337 let bary_inverses_off := BARYCENTRIC_DENOMINATOR_INVERSES_0_0_LOC
1338
1339 for { let round := 0 } lt(round, LOG_N) { round := add(round, 1) } {
1340 let round_challenge := mload(challenge_off)
1341
1342 // Total sum = u[0] + u[1]
1343 let total_sum := addmod(mload(round_univariates_off), mload(add(round_univariates_off, 0x20)), p)
1344 valid := and(valid, eq(total_sum, round_target))
1345
1346 // Compute next target sum
1347 let numerator_value := round_challenge
1348 numerator_value := mulmod(numerator_value, addmod(round_challenge, P_SUB_1, p), p)
1349 numerator_value := mulmod(numerator_value, addmod(round_challenge, P_SUB_2, p), p)
1350 numerator_value := mulmod(numerator_value, addmod(round_challenge, P_SUB_3, p), p)
1351 numerator_value := mulmod(numerator_value, addmod(round_challenge, P_SUB_4, p), p)
1352 numerator_value := mulmod(numerator_value, addmod(round_challenge, P_SUB_5, p), p)
1353 numerator_value := mulmod(numerator_value, addmod(round_challenge, P_SUB_6, p), p)
1354 numerator_value := mulmod(numerator_value, addmod(round_challenge, P_SUB_7, p), p)
1355
1356 // // Compute the next round target
1357 round_target := 0
1358 for { let i := 0 } lt(i, BATCHED_RELATION_PARTIAL_LENGTH) { i := add(i, 1) } {
1359 let term := mload(round_univariates_off)
1360 let inverse := mload(bary_inverses_off)
1361
1362 term := mulmod(term, inverse, p)
1363 round_target := addmod(round_target, term, p)
1364 round_univariates_off := add(round_univariates_off, 0x20)
1365 bary_inverses_off := add(bary_inverses_off, 0x20)
1366 }
1367
1368 round_target := mulmod(round_target, numerator_value, p)
1369
1370 // Partially evaluate POW
1371 let gate_challenge := mload(gate_challenge_off)
1372 let gate_challenge_minus_one := sub(gate_challenge, 1)
1373
1374 let univariate_evaluation := addmod(1, mulmod(round_challenge, gate_challenge_minus_one, p), p)
1375
1376 pow_partial_evaluation := mulmod(pow_partial_evaluation, univariate_evaluation, p)
1377
1378 gate_challenge_off := add(gate_challenge_off, 0x20)
1379 challenge_off := add(challenge_off, 0x20)
1380 }
1381
1382 if iszero(valid) {
1383 mstore(0x00, SUMCHECK_FAILED_SELECTOR)
1384 revert(0x00, 0x04)
1385 }
1386
1387 // The final sumcheck round; accumulating evaluations
1388 // Uses pow partial evaluation as the gate scaling factor
1389
1390 mstore(POW_PARTIAL_EVALUATION_LOC, pow_partial_evaluation)
1391 mstore(FINAL_ROUND_TARGET_LOC, round_target)
1392
1393 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1394 /* LOGUP RELATION */
1395 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1396 {
1432 let w1q1 := mulmod(mload(W1_EVAL_LOC), mload(QL_EVAL_LOC), p)
1433 let w2q2 := mulmod(mload(W2_EVAL_LOC), mload(QR_EVAL_LOC), p)
1434 let w3q3 := mulmod(mload(W3_EVAL_LOC), mload(QO_EVAL_LOC), p)
1435 let w4q3 := mulmod(mload(W4_EVAL_LOC), mload(Q4_EVAL_LOC), p)
1436
1437 let q_arith := mload(QARITH_EVAL_LOC)
1438 // w1w2qm := (w_1 . w_2 . q_m . (QARITH_EVAL_LOC - 3)) / 2
1439 let w1w2qm :=
1440 mulmod(
1441 mulmod(
1442 mulmod(mulmod(mload(W1_EVAL_LOC), mload(W2_EVAL_LOC), p), mload(QM_EVAL_LOC), p),
1443 addmod(q_arith, P_SUB_3, p),
1444 p
1445 ),
1446 NEG_HALF_MODULO_P,
1447 p
1448 )
1449
1450 // (w_1 . w_2 . q_m . (q_arith - 3)) / -2) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c
1451 let identity :=
1452 addmod(
1453 mload(QC_EVAL_LOC),
1454 addmod(w4q3, addmod(w3q3, addmod(w2q2, addmod(w1q1, w1w2qm, p), p), p), p),
1455 p
1456 )
1457
1458 // if q_arith == 3 we evaluate an additional mini addition gate (on top of the regular one), where:
1459 // w_1 + w_4 - w_1_omega + q_m = 0
1460 // we use this gate to save an addition gate when adding or subtracting non-native field elements
1461 // α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m)
1462 let extra_small_addition_gate_identity :=
1463 mulmod(
1464 addmod(q_arith, P_SUB_2, p),
1465 addmod(
1466 mload(QM_EVAL_LOC),
1467 addmod(
1468 sub(p, mload(W1_SHIFT_EVAL_LOC)),
1469 addmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p),
1470 p
1471 ),
1472 p
1473 ),
1474 p
1475 )
1476
1477 // Split up the two relations
1478 let contribution_0 :=
1479 addmod(identity, mulmod(addmod(q_arith, P_SUB_1, p), mload(W4_SHIFT_EVAL_LOC), p), p)
1480 contribution_0 := mulmod(mulmod(contribution_0, q_arith, p), mload(POW_PARTIAL_EVALUATION_LOC), p)
1481 mstore(SUBRELATION_EVAL_0_LOC, contribution_0)
1482
1483 let contribution_1 := mulmod(extra_small_addition_gate_identity, addmod(q_arith, P_SUB_1, p), p)
1484 contribution_1 := mulmod(contribution_1, q_arith, p)
1485 contribution_1 := mulmod(contribution_1, mload(POW_PARTIAL_EVALUATION_LOC), p)
1486 mstore(SUBRELATION_EVAL_1_LOC, contribution_1)
1487 }
1488
1489 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1490 /* PERMUTATION RELATION */
1491 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1492 {
1493 let beta := mload(BETA_CHALLENGE)
1494 let gamma := mload(GAMMA_CHALLENGE)
1495
1504 let t1 :=
1505 mulmod(
1506 add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(ID1_EVAL_LOC), p)),
1507 add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(ID2_EVAL_LOC), p)),
1508 p
1509 )
1510 let t2 :=
1511 mulmod(
1512 add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(ID3_EVAL_LOC), p)),
1513 add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(ID4_EVAL_LOC), p)),
1514 p
1515 )
1516 let numerator := mulmod(t1, t2, p)
1517 t1 := mulmod(
1518 add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA1_EVAL_LOC), p)),
1519 add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA2_EVAL_LOC), p)),
1520 p
1521 )
1522 t2 := mulmod(
1523 add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA3_EVAL_LOC), p)),
1524 add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA4_EVAL_LOC), p)),
1525 p
1526 )
1527 let denominator := mulmod(t1, t2, p)
1528
1529 {
1530 let acc :=
1531 mulmod(addmod(mload(Z_PERM_EVAL_LOC), mload(LAGRANGE_FIRST_EVAL_LOC), p), numerator, p)
1532
1533 acc := addmod(
1534 acc,
1535 sub(
1536 p,
1537 mulmod(
1538 addmod(
1539 mload(Z_PERM_SHIFT_EVAL_LOC),
1540 mulmod(
1541 mload(LAGRANGE_LAST_EVAL_LOC),
1542 mload(PUBLIC_INPUTS_DELTA_NUMERATOR_CHALLENGE),
1543 p
1544 ),
1545 p
1546 ),
1547 denominator,
1548 p
1549 )
1550 ),
1551 p
1552 )
1553
1554 acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p)
1555 mstore(SUBRELATION_EVAL_2_LOC, acc)
1556
1557 acc := mulmod(
1558 mulmod(mload(LAGRANGE_LAST_EVAL_LOC), mload(Z_PERM_SHIFT_EVAL_LOC), p),
1559 mload(POW_PARTIAL_EVALUATION_LOC),
1560 p
1561 )
1562 mstore(SUBRELATION_EVAL_3_LOC, acc)
1563 }
1564 }
1565
1566 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1567 /* LOGUP WIDGET EVALUATION */
1568 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1569 // Note: Using beta powers for column batching and gamma for offset ensures soundness
1570 // beta and gamma must be independent challenges (they come from splitting the same hash)
1571 {
1572 let gamma := mload(GAMMA_CHALLENGE)
1573 let beta := mload(BETA_CHALLENGE)
1574 // Compute beta powers inline (β², β³) for lookup column batching
1575 let beta_sqr := mulmod(beta, beta, p)
1576 let beta_cube := mulmod(beta_sqr, beta, p)
1577
1578 // table_term = table_1 + γ + table_2 * β + table_3 * β² + table_4 * β³
1579 let t0 :=
1580 addmod(addmod(mload(TABLE1_EVAL_LOC), gamma, p), mulmod(mload(TABLE2_EVAL_LOC), beta, p), p)
1581 let t1 :=
1582 addmod(
1583 mulmod(mload(TABLE3_EVAL_LOC), beta_sqr, p),
1584 mulmod(mload(TABLE4_EVAL_LOC), beta_cube, p),
1585 p
1586 )
1587 let table_term := addmod(t0, t1, p)
1588
1589 // lookup_term = derived_entry_1 + γ + derived_entry_2 * β + derived_entry_3 * β² + q_index * β³
1590 t0 := addmod(
1591 addmod(mload(W1_EVAL_LOC), gamma, p),
1592 mulmod(mload(QR_EVAL_LOC), mload(W1_SHIFT_EVAL_LOC), p),
1593 p
1594 )
1595 t1 := addmod(mload(W2_EVAL_LOC), mulmod(mload(QM_EVAL_LOC), mload(W2_SHIFT_EVAL_LOC), p), p)
1596 let t2 := addmod(mload(W3_EVAL_LOC), mulmod(mload(QC_EVAL_LOC), mload(W3_SHIFT_EVAL_LOC), p), p)
1597
1598 let lookup_term := addmod(t0, mulmod(t1, beta, p), p)
1599 lookup_term := addmod(lookup_term, mulmod(t2, beta_sqr, p), p)
1600 lookup_term := addmod(lookup_term, mulmod(mload(QO_EVAL_LOC), beta_cube, p), p)
1601
1602 let lookup_inverse := mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), table_term, p)
1603 let table_inverse := mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), lookup_term, p)
1604
1605 let inverse_exists_xor := addmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), mload(QLOOKUP_EVAL_LOC), p)
1606 inverse_exists_xor := addmod(
1607 inverse_exists_xor,
1608 sub(p, mulmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), mload(QLOOKUP_EVAL_LOC), p)),
1609 p
1610 )
1611
1612 let accumulator_none := mulmod(mulmod(lookup_term, table_term, p), mload(LOOKUP_INVERSES_EVAL_LOC), p)
1613 accumulator_none := addmod(accumulator_none, sub(p, inverse_exists_xor), p)
1614 accumulator_none := mulmod(accumulator_none, mload(POW_PARTIAL_EVALUATION_LOC), p)
1615
1616 let accumulator_one := mulmod(mload(QLOOKUP_EVAL_LOC), lookup_inverse, p)
1617 accumulator_one := addmod(
1618 accumulator_one,
1619 sub(p, mulmod(mload(LOOKUP_READ_COUNTS_EVAL_LOC), table_inverse, p)),
1620 p
1621 )
1622
1623 let read_tag := mload(LOOKUP_READ_TAGS_EVAL_LOC)
1624 let read_tag_boolean_relation := mulmod(read_tag, addmod(read_tag, P_SUB_1, p), p)
1625 read_tag_boolean_relation := mulmod(read_tag_boolean_relation, mload(POW_PARTIAL_EVALUATION_LOC), p)
1626
1627 mstore(SUBRELATION_EVAL_4_LOC, accumulator_none)
1628 mstore(SUBRELATION_EVAL_5_LOC, accumulator_one)
1629 mstore(SUBRELATION_EVAL_6_LOC, read_tag_boolean_relation)
1630 }
1631
1632 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1633 /* DELTA RANGE RELATION */
1634 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1635 {
1636 // TODO(md): optimise the calculations
1637 let minus_one := P_SUB_1
1638 let minus_two := P_SUB_2
1639 let minus_three := P_SUB_3
1640
1641 let delta_1 := addmod(mload(W2_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p)
1642 let delta_2 := addmod(mload(W3_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p)
1643 let delta_3 := addmod(mload(W4_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p)
1644 let delta_4 := addmod(mload(W1_SHIFT_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p)
1645
1646 {
1647 let acc := delta_1
1648 acc := mulmod(acc, addmod(delta_1, minus_one, p), p)
1649 acc := mulmod(acc, addmod(delta_1, minus_two, p), p)
1650 acc := mulmod(acc, addmod(delta_1, minus_three, p), p)
1651 acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p)
1652 acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p)
1653 mstore(SUBRELATION_EVAL_7_LOC, acc)
1654 }
1655
1656 {
1657 let acc := delta_2
1658 acc := mulmod(acc, addmod(delta_2, minus_one, p), p)
1659 acc := mulmod(acc, addmod(delta_2, minus_two, p), p)
1660 acc := mulmod(acc, addmod(delta_2, minus_three, p), p)
1661 acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p)
1662 acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p)
1663 mstore(SUBRELATION_EVAL_8_LOC, acc)
1664 }
1665
1666 {
1667 let acc := delta_3
1668 acc := mulmod(acc, addmod(delta_3, minus_one, p), p)
1669 acc := mulmod(acc, addmod(delta_3, minus_two, p), p)
1670 acc := mulmod(acc, addmod(delta_3, minus_three, p), p)
1671 acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p)
1672 acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p)
1673 mstore(SUBRELATION_EVAL_9_LOC, acc)
1674 }
1675
1676 {
1677 let acc := delta_4
1678 acc := mulmod(acc, addmod(delta_4, minus_one, p), p)
1679 acc := mulmod(acc, addmod(delta_4, minus_two, p), p)
1680 acc := mulmod(acc, addmod(delta_4, minus_three, p), p)
1681 acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p)
1682 acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p)
1683 mstore(SUBRELATION_EVAL_10_LOC, acc)
1684 }
1685 }
1686
1687 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1688 /* ELLIPTIC CURVE RELATION */
1689 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1690 {
1691 // Contribution 10 point addition, x-coordinate check
1692 // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
1693 let x_diff := addmod(mload(EC_X_2), sub(p, mload(EC_X_1)), p)
1694 let y1_sqr := mulmod(mload(EC_Y_1), mload(EC_Y_1), p)
1695 {
1696 let y2_sqr := mulmod(mload(EC_Y_2), mload(EC_Y_2), p)
1697 let y1y2 := mulmod(mulmod(mload(EC_Y_1), mload(EC_Y_2), p), mload(EC_Q_SIGN), p)
1698 let x_add_identity := addmod(mload(EC_X_3), addmod(mload(EC_X_2), mload(EC_X_1), p), p)
1699 x_add_identity := mulmod(mulmod(x_add_identity, x_diff, p), x_diff, p)
1700 x_add_identity := addmod(x_add_identity, sub(p, y2_sqr), p)
1701 x_add_identity := addmod(x_add_identity, sub(p, y1_sqr), p)
1702 x_add_identity := addmod(x_add_identity, y1y2, p)
1703 x_add_identity := addmod(x_add_identity, y1y2, p)
1704
1705 let eval := mulmod(x_add_identity, mload(POW_PARTIAL_EVALUATION_LOC), p)
1706 eval := mulmod(eval, mload(QELLIPTIC_EVAL_LOC), p)
1707 eval := mulmod(eval, addmod(1, sub(p, mload(EC_Q_IS_DOUBLE)), p), p)
1708 mstore(SUBRELATION_EVAL_11_LOC, eval)
1709 }
1710
1711 {
1712 let y1_plus_y3 := addmod(mload(EC_Y_1), mload(EC_Y_3), p)
1713 let y_diff := mulmod(mload(EC_Y_2), mload(EC_Q_SIGN), p)
1714 y_diff := addmod(y_diff, sub(p, mload(EC_Y_1)), p)
1715 let y_add_identity := mulmod(y1_plus_y3, x_diff, p)
1716 y_add_identity := addmod(
1717 y_add_identity,
1718 mulmod(addmod(mload(EC_X_3), sub(p, mload(EC_X_1)), p), y_diff, p),
1719 p
1720 )
1721
1722 let eval := mulmod(y_add_identity, mload(POW_PARTIAL_EVALUATION_LOC), p)
1723 eval := mulmod(eval, mload(QELLIPTIC_EVAL_LOC), p)
1724 eval := mulmod(eval, addmod(1, sub(p, mload(EC_Q_IS_DOUBLE)), p), p)
1725 mstore(SUBRELATION_EVAL_12_LOC, eval)
1726 }
1727
1728 {
1729 let x_pow_4 := mulmod(addmod(y1_sqr, GRUMPKIN_CURVE_B_PARAMETER_NEGATED, p), mload(EC_X_1), p)
1730 let y1_sqr_mul_4 := addmod(y1_sqr, y1_sqr, p)
1731 y1_sqr_mul_4 := addmod(y1_sqr_mul_4, y1_sqr_mul_4, p)
1732
1733 let x1_pow_4_mul_9 := mulmod(x_pow_4, 9, p)
1734
1735 let ep_x_double_identity := addmod(mload(EC_X_3), addmod(mload(EC_X_1), mload(EC_X_1), p), p)
1736 ep_x_double_identity := mulmod(ep_x_double_identity, y1_sqr_mul_4, p)
1737 ep_x_double_identity := addmod(ep_x_double_identity, sub(p, x1_pow_4_mul_9), p)
1738
1739 let acc := mulmod(ep_x_double_identity, mload(POW_PARTIAL_EVALUATION_LOC), p)
1740 acc := mulmod(mulmod(acc, mload(QELLIPTIC_EVAL_LOC), p), mload(EC_Q_IS_DOUBLE), p)
1741 acc := addmod(acc, mload(SUBRELATION_EVAL_11_LOC), p)
1742
1743 // Add to existing contribution - and double check that numbers here
1744 mstore(SUBRELATION_EVAL_11_LOC, acc)
1745 }
1746
1747 {
1748 let x1_sqr_mul_3 :=
1749 mulmod(addmod(addmod(mload(EC_X_1), mload(EC_X_1), p), mload(EC_X_1), p), mload(EC_X_1), p)
1750 let y_double_identity :=
1751 mulmod(x1_sqr_mul_3, addmod(mload(EC_X_1), sub(p, mload(EC_X_3)), p), p)
1752 y_double_identity := addmod(
1753 y_double_identity,
1754 sub(
1755 p,
1756 mulmod(
1757 addmod(mload(EC_Y_1), mload(EC_Y_1), p),
1758 addmod(mload(EC_Y_1), mload(EC_Y_3), p),
1759 p
1760 )
1761 ),
1762 p
1763 )
1764
1765 let acc := mulmod(y_double_identity, mload(POW_PARTIAL_EVALUATION_LOC), p)
1766 acc := mulmod(mulmod(acc, mload(QELLIPTIC_EVAL_LOC), p), mload(EC_Q_IS_DOUBLE), p)
1767 acc := addmod(acc, mload(SUBRELATION_EVAL_12_LOC), p)
1768
1769 // Add to existing contribution - and double check that numbers here
1770 mstore(SUBRELATION_EVAL_12_LOC, acc)
1771 }
1772 }
1773
1774 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1775 /* MEMORY RELATION */
1776 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1777 {
1778 {
1830 // TODO(md): update these - formula has changed with lower degree
1831 let memory_record_check := mulmod(mload(W3_EVAL_LOC), mload(ETA_THREE_CHALLENGE), p)
1832 memory_record_check := addmod(
1833 memory_record_check,
1834 mulmod(mload(W2_EVAL_LOC), mload(ETA_TWO_CHALLENGE), p),
1835 p
1836 )
1837 memory_record_check := addmod(
1838 memory_record_check,
1839 mulmod(mload(W1_EVAL_LOC), mload(ETA_CHALLENGE), p),
1840 p
1841 )
1842 memory_record_check := addmod(memory_record_check, mload(QC_EVAL_LOC), p)
1843
1844 let partial_record_check := memory_record_check
1845 memory_record_check := addmod(memory_record_check, sub(p, mload(W4_EVAL_LOC)), p)
1846
1847 mstore(AUX_MEMORY_CHECK_IDENTITY, memory_record_check)
1848
1864 // index_delta = w_1_omega - w_1
1865 let index_delta := addmod(mload(W1_SHIFT_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p)
1866
1867 // record_delta = w_4_omega - w_4
1868 let record_delta := addmod(mload(W4_SHIFT_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p)
1869
1870 // index_is_monotonically_increasing = index_delta * (index_delta - 1)
1871 let index_is_monotonically_increasing := mulmod(index_delta, addmod(index_delta, P_SUB_1, p), p)
1872
1873 // adjacent_values_match_if_adjacent_indices_match = record_delta * (1 - index_delta)
1874 let adjacent_values_match_if_adjacent_indices_match :=
1875 mulmod(record_delta, addmod(1, sub(p, index_delta), p), p)
1876
1877 mstore(
1878 SUBRELATION_EVAL_14_LOC,
1879 mulmod(
1880 adjacent_values_match_if_adjacent_indices_match,
1881 mulmod(
1882 mload(QL_EVAL_LOC),
1883 mulmod(
1884 mload(QR_EVAL_LOC),
1885 mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
1886 p
1887 ),
1888 p
1889 ),
1890 p
1891 )
1892 )
1893
1894 // ROM_CONSISTENCY_CHECK_2
1895 mstore(
1896 SUBRELATION_EVAL_15_LOC,
1897 mulmod(
1898 index_is_monotonically_increasing,
1899 mulmod(
1900 mload(QL_EVAL_LOC),
1901 mulmod(
1902 mload(QR_EVAL_LOC),
1903 mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
1904 p
1905 ),
1906 p
1907 ),
1908 p
1909 )
1910 )
1911
1912 mstore(
1913 AUX_ROM_CONSISTENCY_CHECK_IDENTITY,
1914 mulmod(memory_record_check, mulmod(mload(QL_EVAL_LOC), mload(QR_EVAL_LOC), p), p)
1915 )
1916
1917 {
1944 let next_gate_access_type := mulmod(mload(W3_SHIFT_EVAL_LOC), mload(ETA_THREE_CHALLENGE), p)
1945 next_gate_access_type := addmod(
1946 next_gate_access_type,
1947 mulmod(mload(W2_SHIFT_EVAL_LOC), mload(ETA_TWO_CHALLENGE), p),
1948 p
1949 )
1950 next_gate_access_type := addmod(
1951 next_gate_access_type,
1952 mulmod(mload(W1_SHIFT_EVAL_LOC), mload(ETA_CHALLENGE), p),
1953 p
1954 )
1955 next_gate_access_type := addmod(mload(W4_SHIFT_EVAL_LOC), sub(p, next_gate_access_type), p)
1956
1957 // value_delta = w_3_omega - w_3
1958 let value_delta := addmod(mload(W3_SHIFT_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p)
1959 // adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = (1 - index_delta) * value_delta * (1 - next_gate_access_type);
1960
1961 let adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation :=
1962 mulmod(
1963 addmod(1, sub(p, index_delta), p),
1964 mulmod(value_delta, addmod(1, sub(p, next_gate_access_type), p), p),
1965 p
1966 )
1967
1968 // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the
1969 // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't
1970 // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access
1971 // type is correct, to cover this edge case
1972 // deg 2 or 4
1978 let access_type := addmod(mload(W4_EVAL_LOC), sub(p, partial_record_check), p)
1979 let access_check := mulmod(access_type, addmod(access_type, P_SUB_1, p), p)
1980 let next_gate_access_type_is_boolean :=
1981 mulmod(next_gate_access_type, addmod(next_gate_access_type, P_SUB_1, p), p)
1982
1983 // scaled_activation_selector = q_arith * q_aux * alpha
1984 let scaled_activation_selector :=
1985 mulmod(
1986 mload(QO_EVAL_LOC),
1987 mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
1988 p
1989 )
1990
1991 mstore(
1992 SUBRELATION_EVAL_16_LOC,
1993 mulmod(
1994 adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation,
1995 scaled_activation_selector,
1996 p
1997 )
1998 )
1999
2000 mstore(
2001 SUBRELATION_EVAL_17_LOC,
2002 mulmod(index_is_monotonically_increasing, scaled_activation_selector, p)
2003 )
2004
2005 mstore(
2006 SUBRELATION_EVAL_18_LOC,
2007 mulmod(next_gate_access_type_is_boolean, scaled_activation_selector, p)
2008 )
2009
2010 mstore(AUX_RAM_CONSISTENCY_CHECK_IDENTITY, mulmod(access_check, mload(QO_EVAL_LOC), p))
2011 }
2012
2013 {
2014 // timestamp_delta = w_2_omega - w_2
2015 let timestamp_delta := addmod(mload(W2_SHIFT_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p)
2016
2017 // RAM_timestamp_check_identity = (1 - index_delta) * timestamp_delta - w_3
2018 let RAM_TIMESTAMP_CHECK_IDENTITY :=
2019 addmod(
2020 mulmod(timestamp_delta, addmod(1, sub(p, index_delta), p), p),
2021 sub(p, mload(W3_EVAL_LOC)),
2022 p
2023 )
2024
2036 let memory_identity := mload(AUX_ROM_CONSISTENCY_CHECK_IDENTITY)
2037 memory_identity := addmod(
2038 memory_identity,
2039 mulmod(
2040 RAM_TIMESTAMP_CHECK_IDENTITY,
2041 mulmod(mload(Q4_EVAL_LOC), mload(QL_EVAL_LOC), p),
2042 p
2043 ),
2044 p
2045 )
2046
2047 memory_identity := addmod(
2048 memory_identity,
2049 mulmod(
2050 mload(AUX_MEMORY_CHECK_IDENTITY),
2051 mulmod(mload(QM_EVAL_LOC), mload(QL_EVAL_LOC), p),
2052 p
2053 ),
2054 p
2055 )
2056 memory_identity := addmod(memory_identity, mload(AUX_RAM_CONSISTENCY_CHECK_IDENTITY), p)
2057
2058 memory_identity := mulmod(
2059 memory_identity,
2060 mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
2061 p
2062 )
2063 mstore(SUBRELATION_EVAL_13_LOC, memory_identity)
2064 }
2065 }
2066 }
2067
2068 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
2069 /* NON NATIVE FIELD RELATION */
2070 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
2071 {
2091 let limb_subproduct :=
2092 addmod(
2093 mulmod(mload(W1_EVAL_LOC), mload(W2_SHIFT_EVAL_LOC), p),
2094 mulmod(mload(W1_SHIFT_EVAL_LOC), mload(W2_EVAL_LOC), p),
2095 p
2096 )
2097
2098 let non_native_field_gate_2 :=
2099 addmod(
2100 addmod(
2101 mulmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p),
2102 mulmod(mload(W2_EVAL_LOC), mload(W3_EVAL_LOC), p),
2103 p
2104 ),
2105 sub(p, mload(W3_SHIFT_EVAL_LOC)),
2106 p
2107 )
2108 non_native_field_gate_2 := mulmod(non_native_field_gate_2, LIMB_SIZE, p)
2109 non_native_field_gate_2 := addmod(non_native_field_gate_2, sub(p, mload(W4_SHIFT_EVAL_LOC)), p)
2110 non_native_field_gate_2 := addmod(non_native_field_gate_2, limb_subproduct, p)
2111 non_native_field_gate_2 := mulmod(non_native_field_gate_2, mload(Q4_EVAL_LOC), p)
2112
2113 limb_subproduct := mulmod(limb_subproduct, LIMB_SIZE, p)
2114 limb_subproduct := addmod(
2115 limb_subproduct,
2116 mulmod(mload(W1_SHIFT_EVAL_LOC), mload(W2_SHIFT_EVAL_LOC), p),
2117 p
2118 )
2119
2120 let non_native_field_gate_1 :=
2121 mulmod(
2122 addmod(limb_subproduct, sub(p, addmod(mload(W3_EVAL_LOC), mload(W4_EVAL_LOC), p)), p),
2123 mload(QO_EVAL_LOC),
2124 p
2125 )
2126
2127 let non_native_field_gate_3 :=
2128 mulmod(
2129 addmod(
2130 addmod(limb_subproduct, mload(W4_EVAL_LOC), p),
2131 sub(p, addmod(mload(W3_SHIFT_EVAL_LOC), mload(W4_SHIFT_EVAL_LOC), p)),
2132 p
2133 ),
2134 mload(QM_EVAL_LOC),
2135 p
2136 )
2137 let non_native_field_identity :=
2138 mulmod(
2139 addmod(
2140 addmod(non_native_field_gate_1, non_native_field_gate_2, p),
2141 non_native_field_gate_3,
2142 p
2143 ),
2144 mload(QR_EVAL_LOC),
2145 p
2146 )
2147
2148 mstore(AUX_NON_NATIVE_FIELD_IDENTITY, non_native_field_identity)
2149 }
2150
2151 {
2165 let limb_accumulator_1 := mulmod(mload(W2_SHIFT_EVAL_LOC), SUBLIMB_SHIFT, p)
2166 limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_SHIFT_EVAL_LOC), p)
2167 limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
2168 limb_accumulator_1 := addmod(limb_accumulator_1, mload(W3_EVAL_LOC), p)
2169 limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
2170 limb_accumulator_1 := addmod(limb_accumulator_1, mload(W2_EVAL_LOC), p)
2171 limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
2172 limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_EVAL_LOC), p)
2173 limb_accumulator_1 := addmod(limb_accumulator_1, sub(p, mload(W4_EVAL_LOC)), p)
2174 limb_accumulator_1 := mulmod(limb_accumulator_1, mload(Q4_EVAL_LOC), p)
2175
2189 let limb_accumulator_2 := mulmod(mload(W3_SHIFT_EVAL_LOC), SUBLIMB_SHIFT, p)
2190 limb_accumulator_2 := addmod(limb_accumulator_2, mload(W2_SHIFT_EVAL_LOC), p)
2191 limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
2192 limb_accumulator_2 := addmod(limb_accumulator_2, mload(W1_SHIFT_EVAL_LOC), p)
2193 limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
2194 limb_accumulator_2 := addmod(limb_accumulator_2, mload(W4_EVAL_LOC), p)
2195 limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
2196 limb_accumulator_2 := addmod(limb_accumulator_2, mload(W3_EVAL_LOC), p)
2197 limb_accumulator_2 := addmod(limb_accumulator_2, sub(p, mload(W4_SHIFT_EVAL_LOC)), p)
2198 limb_accumulator_2 := mulmod(limb_accumulator_2, mload(QM_EVAL_LOC), p)
2199
2200 let limb_accumulator_identity := addmod(limb_accumulator_1, limb_accumulator_2, p)
2201 limb_accumulator_identity := mulmod(limb_accumulator_identity, mload(QO_EVAL_LOC), p)
2202
2203 let nnf_identity := addmod(mload(AUX_NON_NATIVE_FIELD_IDENTITY), limb_accumulator_identity, p)
2204 nnf_identity := mulmod(
2205 nnf_identity,
2206 mulmod(mload(QNNF_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
2207 p
2208 )
2209
2210 mstore(SUBRELATION_EVAL_19_LOC, nnf_identity)
2211 }
2212
2213 /*
2214 * Poseidon External Relation
2215 */
2216 {
2217 let s1 := addmod(mload(W1_EVAL_LOC), mload(QL_EVAL_LOC), p)
2218 let s2 := addmod(mload(W2_EVAL_LOC), mload(QR_EVAL_LOC), p)
2219 let s3 := addmod(mload(W3_EVAL_LOC), mload(QO_EVAL_LOC), p)
2220 let s4 := addmod(mload(W4_EVAL_LOC), mload(Q4_EVAL_LOC), p)
2221
2222 // u1 := s1 * s1 * s1 * s1 * s1;
2223 let t0 := mulmod(s1, s1, p)
2224 let u1 := mulmod(t0, mulmod(t0, s1, p), p)
2225
2226 // u2 := s2 * s2 * s2 * s2 * s2;
2227 t0 := mulmod(s2, s2, p)
2228 let u2 := mulmod(t0, mulmod(t0, s2, p), p)
2229
2230 // u3 := s3 * s3 * s3 * s3 * s3;
2231 t0 := mulmod(s3, s3, p)
2232 let u3 := mulmod(t0, mulmod(t0, s3, p), p)
2233
2234 // u4 := s4 * s4 * s4 * s4 * s4;
2235 t0 := mulmod(s4, s4, p)
2236 let u4 := mulmod(t0, mulmod(t0, s4, p), p)
2237
2238 // matrix mul v = M_E * u with 14 additions
2239 t0 := addmod(u1, u2, p)
2240 let t1 := addmod(u3, u4, p)
2241
2242 let t2 := addmod(u2, u2, p)
2243 t2 := addmod(t2, t1, p)
2244
2245 let t3 := addmod(u4, u4, p)
2246 t3 := addmod(t3, t0, p)
2247
2248 let v4 := addmod(t1, t1, p)
2249 v4 := addmod(v4, v4, p)
2250 v4 := addmod(v4, t3, p)
2251
2252 let v2 := addmod(t0, t0, p)
2253 v2 := addmod(v2, v2, p)
2254 v2 := addmod(v2, t2, p)
2255
2256 let v1 := addmod(t3, v2, p)
2257 let v3 := addmod(t2, v4, p)
2258
2259 let q_pos_by_scaling :=
2260 mulmod(mload(QPOSEIDON2_EXTERNAL_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p)
2261
2262 mstore(
2263 SUBRELATION_EVAL_20_LOC,
2264 mulmod(q_pos_by_scaling, addmod(v1, sub(p, mload(W1_SHIFT_EVAL_LOC)), p), p)
2265 )
2266
2267 mstore(
2268 SUBRELATION_EVAL_21_LOC,
2269 mulmod(q_pos_by_scaling, addmod(v2, sub(p, mload(W2_SHIFT_EVAL_LOC)), p), p)
2270 )
2271
2272 mstore(
2273 SUBRELATION_EVAL_22_LOC,
2274 mulmod(q_pos_by_scaling, addmod(v3, sub(p, mload(W3_SHIFT_EVAL_LOC)), p), p)
2275 )
2276
2277 mstore(
2278 SUBRELATION_EVAL_23_LOC,
2279 mulmod(q_pos_by_scaling, addmod(v4, sub(p, mload(W4_SHIFT_EVAL_LOC)), p), p)
2280 )
2281 }
2282
2283 /*
2284 * Poseidon Internal Relation
2285 */
2286 {
2287 let s1 := addmod(mload(W1_EVAL_LOC), mload(QL_EVAL_LOC), p)
2288
2289 // apply s-box round
2290 let t0 := mulmod(s1, s1, p)
2291 let u1 := mulmod(t0, mulmod(t0, s1, p), p)
2292 let u2 := mload(W2_EVAL_LOC)
2293 let u3 := mload(W3_EVAL_LOC)
2294 let u4 := mload(W4_EVAL_LOC)
2295
2296 // matrix mul v = M_I * u 4 muls and 7 additions
2297 let u_sum := addmod(u1, u2, p)
2298 u_sum := addmod(u_sum, addmod(u3, u4, p), p)
2299
2300 let q_pos_by_scaling :=
2301 mulmod(mload(QPOSEIDON2_INTERNAL_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p)
2302
2303 let v1 := addmod(mulmod(u1, POS_INTERNAL_MATRIX_D_0, p), u_sum, p)
2304
2305 mstore(
2306 SUBRELATION_EVAL_24_LOC,
2307 mulmod(q_pos_by_scaling, addmod(v1, sub(p, mload(W1_SHIFT_EVAL_LOC)), p), p)
2308 )
2309 let v2 := addmod(mulmod(u2, POS_INTERNAL_MATRIX_D_1, p), u_sum, p)
2310
2311 mstore(
2312 SUBRELATION_EVAL_25_LOC,
2313 mulmod(q_pos_by_scaling, addmod(v2, sub(p, mload(W2_SHIFT_EVAL_LOC)), p), p)
2314 )
2315 let v3 := addmod(mulmod(u3, POS_INTERNAL_MATRIX_D_2, p), u_sum, p)
2316
2317 mstore(
2318 SUBRELATION_EVAL_26_LOC,
2319 mulmod(q_pos_by_scaling, addmod(v3, sub(p, mload(W3_SHIFT_EVAL_LOC)), p), p)
2320 )
2321
2322 let v4 := addmod(mulmod(u4, POS_INTERNAL_MATRIX_D_3, p), u_sum, p)
2323 mstore(
2324 SUBRELATION_EVAL_27_LOC,
2325 mulmod(q_pos_by_scaling, addmod(v4, sub(p, mload(W4_SHIFT_EVAL_LOC)), p), p)
2326 )
2327 }
2328
2329 // Scale and batch subrelations by subrelation challenges
2330 // linear combination of subrelations
2331 let accumulator := mload(SUBRELATION_EVAL_0_LOC)
2332
2333 // Below is an unrolled variant of the following loop
2334 // for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) {
2335 // accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1];
2336 // }
2337
2338 accumulator := addmod(
2339 accumulator,
2340 mulmod(mload(SUBRELATION_EVAL_1_LOC), mload(ALPHA_CHALLENGE_0), p),
2341 p
2342 )
2343 accumulator := addmod(
2344 accumulator,
2345 mulmod(mload(SUBRELATION_EVAL_2_LOC), mload(ALPHA_CHALLENGE_1), p),
2346 p
2347 )
2348 accumulator := addmod(
2349 accumulator,
2350 mulmod(mload(SUBRELATION_EVAL_3_LOC), mload(ALPHA_CHALLENGE_2), p),
2351 p
2352 )
2353 accumulator := addmod(
2354 accumulator,
2355 mulmod(mload(SUBRELATION_EVAL_4_LOC), mload(ALPHA_CHALLENGE_3), p),
2356 p
2357 )
2358 accumulator := addmod(
2359 accumulator,
2360 mulmod(mload(SUBRELATION_EVAL_5_LOC), mload(ALPHA_CHALLENGE_4), p),
2361 p
2362 )
2363 accumulator := addmod(
2364 accumulator,
2365 mulmod(mload(SUBRELATION_EVAL_6_LOC), mload(ALPHA_CHALLENGE_5), p),
2366 p
2367 )
2368 accumulator := addmod(
2369 accumulator,
2370 mulmod(mload(SUBRELATION_EVAL_7_LOC), mload(ALPHA_CHALLENGE_6), p),
2371 p
2372 )
2373 accumulator := addmod(
2374 accumulator,
2375 mulmod(mload(SUBRELATION_EVAL_8_LOC), mload(ALPHA_CHALLENGE_7), p),
2376 p
2377 )
2378 accumulator := addmod(
2379 accumulator,
2380 mulmod(mload(SUBRELATION_EVAL_9_LOC), mload(ALPHA_CHALLENGE_8), p),
2381 p
2382 )
2383 accumulator := addmod(
2384 accumulator,
2385 mulmod(mload(SUBRELATION_EVAL_10_LOC), mload(ALPHA_CHALLENGE_9), p),
2386 p
2387 )
2388 accumulator := addmod(
2389 accumulator,
2390 mulmod(mload(SUBRELATION_EVAL_11_LOC), mload(ALPHA_CHALLENGE_10), p),
2391 p
2392 )
2393 accumulator := addmod(
2394 accumulator,
2395 mulmod(mload(SUBRELATION_EVAL_12_LOC), mload(ALPHA_CHALLENGE_11), p),
2396 p
2397 )
2398 accumulator := addmod(
2399 accumulator,
2400 mulmod(mload(SUBRELATION_EVAL_13_LOC), mload(ALPHA_CHALLENGE_12), p),
2401 p
2402 )
2403 accumulator := addmod(
2404 accumulator,
2405 mulmod(mload(SUBRELATION_EVAL_14_LOC), mload(ALPHA_CHALLENGE_13), p),
2406 p
2407 )
2408 accumulator := addmod(
2409 accumulator,
2410 mulmod(mload(SUBRELATION_EVAL_15_LOC), mload(ALPHA_CHALLENGE_14), p),
2411 p
2412 )
2413 accumulator := addmod(
2414 accumulator,
2415 mulmod(mload(SUBRELATION_EVAL_16_LOC), mload(ALPHA_CHALLENGE_15), p),
2416 p
2417 )
2418 accumulator := addmod(
2419 accumulator,
2420 mulmod(mload(SUBRELATION_EVAL_17_LOC), mload(ALPHA_CHALLENGE_16), p),
2421 p
2422 )
2423 accumulator := addmod(
2424 accumulator,
2425 mulmod(mload(SUBRELATION_EVAL_18_LOC), mload(ALPHA_CHALLENGE_17), p),
2426 p
2427 )
2428 accumulator := addmod(
2429 accumulator,
2430 mulmod(mload(SUBRELATION_EVAL_19_LOC), mload(ALPHA_CHALLENGE_18), p),
2431 p
2432 )
2433 accumulator := addmod(
2434 accumulator,
2435 mulmod(mload(SUBRELATION_EVAL_20_LOC), mload(ALPHA_CHALLENGE_19), p),
2436 p
2437 )
2438 accumulator := addmod(
2439 accumulator,
2440 mulmod(mload(SUBRELATION_EVAL_21_LOC), mload(ALPHA_CHALLENGE_20), p),
2441 p
2442 )
2443 accumulator := addmod(
2444 accumulator,
2445 mulmod(mload(SUBRELATION_EVAL_22_LOC), mload(ALPHA_CHALLENGE_21), p),
2446 p
2447 )
2448 accumulator := addmod(
2449 accumulator,
2450 mulmod(mload(SUBRELATION_EVAL_23_LOC), mload(ALPHA_CHALLENGE_22), p),
2451 p
2452 )
2453 accumulator := addmod(
2454 accumulator,
2455 mulmod(mload(SUBRELATION_EVAL_24_LOC), mload(ALPHA_CHALLENGE_23), p),
2456 p
2457 )
2458 accumulator := addmod(
2459 accumulator,
2460 mulmod(mload(SUBRELATION_EVAL_25_LOC), mload(ALPHA_CHALLENGE_24), p),
2461 p
2462 )
2463 accumulator := addmod(
2464 accumulator,
2465 mulmod(mload(SUBRELATION_EVAL_26_LOC), mload(ALPHA_CHALLENGE_25), p),
2466 p
2467 )
2468 accumulator := addmod(
2469 accumulator,
2470 mulmod(mload(SUBRELATION_EVAL_27_LOC), mload(ALPHA_CHALLENGE_26), p),
2471 p
2472 )
2473
2474 let sumcheck_valid := eq(accumulator, mload(FINAL_ROUND_TARGET_LOC))
2475
2476 if iszero(sumcheck_valid) {
2477 mstore(0x00, SUMCHECK_FAILED_SELECTOR)
2478 revert(0x00, 0x04)
2479 }
2480 }
2481
2482 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
2483 /* SUMCHECK -- Complete */
2484 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
2485
2486 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
2487 /* SHPLEMINI */
2488 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
2489
2490 // Compute powers of evaluation challenge
2491 let cache := mload(GEMINI_R_CHALLENGE)
2492 let off := POWERS_OF_EVALUATION_CHALLENGE_0_LOC
2493 mstore(off, cache)
2494
2495 for { let i := 1 } lt(i, LOG_N) { i := add(i, 1) } {
2496 off := add(off, 0x20)
2497 cache := mulmod(cache, cache, p)
2498 mstore(off, cache)
2499 }
2500
2501 // Compute Inverted Gemini Denominators
2502 let eval_challenge := mload(SHPLONK_Z_CHALLENGE)
2503
2504 // TO be inverted in the batch invert below
2505 // TODO: maybe not needed to go in memory
2506 mstore(
2507 INVERTED_GEMINI_DENOMINATOR_0_LOC,
2508 addmod(eval_challenge, sub(p, mload(POWERS_OF_EVALUATION_CHALLENGE_0_LOC)), p)
2509 )
2510
2511 mstore(
2512 POS_INVERTED_DENOM_0_LOC,
2513 addmod(eval_challenge, sub(p, mload(POWERS_OF_EVALUATION_CHALLENGE_0_LOC)), p)
2514 )
2515 mstore(NEG_INVERTED_DENOM_0_LOC, addmod(eval_challenge, mload(POWERS_OF_EVALUATION_CHALLENGE_0_LOC), p))
2516
2517 // Compute Fold Pos Evaluatios
2518
2519 // In order to compute fold pos evaluations we need
2520 let store_off := INVERTED_CHALLENEGE_POW_MINUS_U_{{ LOG_N_MINUS_ONE }}_LOC
2521 let pow_off := POWERS_OF_EVALUATION_CHALLENGE_{{ LOG_N_MINUS_ONE }}_LOC
2522 let sumcheck_u_off := SUM_U_CHALLENGE_{{ LOG_N_MINUS_ONE }}
2523
2524 // TODO: challengePower * (ONE - u) can be cached - measure performance
2525 for { let i := LOG_N } gt(i, 0) { i := sub(i, 1) } {
2526 let u := mload(sumcheck_u_off)
2527
2528 let challPowerMulMinusU := mulmod(mload(pow_off), addmod(1, sub(p, u), p), p)
2529
2530 mstore(store_off, addmod(challPowerMulMinusU, u, p))
2531
2532 store_off := sub(store_off, 0x20)
2533 pow_off := sub(pow_off, 0x20)
2534 sumcheck_u_off := sub(sumcheck_u_off, 0x20)
2535 }
2536
2537 // Compute
2538 {
2539 let pos_inverted_off := POS_INVERTED_DENOM_1_LOC
2540 let neg_inverted_off := NEG_INVERTED_DENOM_1_LOC
2541 pow_off := POWERS_OF_EVALUATION_CHALLENGE_1_LOC
2542
2543 let shplonk_z := mload(SHPLONK_Z_CHALLENGE)
2544 for { let i := 0 } lt(i, sub(LOG_N, 1)) { i := add(i, 1) } {
2545 let pow := mload(pow_off)
2546
2547 let pos_inv := addmod(shplonk_z, sub(p, pow), p)
2548 mstore(pos_inverted_off, pos_inv)
2549
2550 let neg_inv := addmod(shplonk_z, pow, p)
2551 mstore(neg_inverted_off, neg_inv)
2552
2553 pow_off := add(pow_off, 0x20)
2554 pos_inverted_off := add(pos_inverted_off, 0x20)
2555 neg_inverted_off := add(neg_inverted_off, 0x20)
2556 }
2557 }
2558
2559 // To be inverted
2560 // From: computeFoldPosEvaluations
2561 // Series of challengePower * (ONE - u)
2562 // gemini r challenge
2563 // Inverted denominators
2564 // (shplonkZ - powers of evaluaion challenge[i + 1])
2565 // (shplonkZ + powers of evaluation challenge[i + 1])
2566
2567 // Use scratch space for temps
2568
2569 let accumulator := mload(GEMINI_R_CHALLENGE)
2570
2573
2574 {
2575 mstore(0, 0x20)
2576 mstore(0x20, 0x20)
2577 mstore(0x40, 0x20)
2578 mstore(0x60, accumulator)
2579 mstore(0x80, P_SUB_2)
2580 mstore(0xa0, p)
2581 if iszero(staticcall(gas(), 0x05, 0x00, 0xc0, 0x00, 0x20)) {
2582 mstore(0x00, MODEXP_FAILED_SELECTOR)
2583 revert(0x00, 0x04)
2584 }
2585 accumulator := mload(0x00)
2586 }
2587
2590
2591 let unshifted_scalar := 0
2592 let shifted_scalar := 0
2593 {
2594 let pos_inverted_denominator := mload(POS_INVERTED_DENOM_0_LOC)
2595 let neg_inverted_denominator := mload(NEG_INVERTED_DENOM_0_LOC)
2596 let shplonk_nu := mload(SHPLONK_NU_CHALLENGE)
2597
2598 unshifted_scalar := addmod(pos_inverted_denominator, mulmod(shplonk_nu, neg_inverted_denominator, p), p)
2599
2600 // accumulator takes the value of `INVERTED_GEMINI_DENOMINATOR_0` here
2601 shifted_scalar := mulmod(
2602 accumulator, // (1 / gemini_r_challenge)
2603 // (inverse_vanishing_evals[0]) - (shplonk_nu * inverse_vanishing_evals[1])
2604 addmod(
2605 pos_inverted_denominator,
2606 // - (shplonk_nu * inverse_vanishing_evals[1])
2607 sub(p, mulmod(shplonk_nu, neg_inverted_denominator, p)),
2608 p
2609 ),
2610 p
2611 )
2612 }
2613
2614 // TODO: Write a comment that describes the process of accumulating commitments and scalars
2615 // into one large value that will be used on the rhs of the pairing check
2616
2617 // Accumulators
2618 let batching_challenge := 1
2619 let batched_evaluation := 0
2620
2621 let neg_unshifted_scalar := sub(p, unshifted_scalar)
2622 let neg_shifted_scalar := sub(p, shifted_scalar)
2623
2624 mstore(BATCH_SCALAR_0_LOC, 1)
2625 let rho := mload(RHO_CHALLENGE)
2626
2627 // Unrolled for the loop below - where NUMBER_UNSHIFTED = 36
2628 // for (uint256 i = 1; i <= NUMBER_UNSHIFTED; ++i) {
2629 // scalars[i] = mem.unshiftedScalar.neg() * mem.batchingChallenge;
2630 // mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - 1] * mem.batchingChallenge);
2631 // mem.batchingChallenge = mem.batchingChallenge * tp.rho;
2632 // }
2633
2634 // Calculate the scalars and batching challenge for the unshifted entities
2635 // 0: QM_EVAL_LOC
2636 mstore(BATCH_SCALAR_1_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2637 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QM_EVAL_LOC), batching_challenge, p), p)
2638 batching_challenge := mulmod(batching_challenge, rho, p)
2639
2640 // 1: QC_EVAL_LOC
2641 mstore(BATCH_SCALAR_2_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2642 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QC_EVAL_LOC), batching_challenge, p), p)
2643 batching_challenge := mulmod(batching_challenge, rho, p)
2644
2645 // 2: QL_EVAL_LOC
2646 mstore(BATCH_SCALAR_3_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2647 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QL_EVAL_LOC), batching_challenge, p), p)
2648 batching_challenge := mulmod(batching_challenge, rho, p)
2649
2650 // 3: QR_EVAL_LOC
2651 mstore(BATCH_SCALAR_4_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2652 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QR_EVAL_LOC), batching_challenge, p), p)
2653 batching_challenge := mulmod(batching_challenge, rho, p)
2654
2655 // 4: QO_EVAL_LOC
2656 mstore(BATCH_SCALAR_5_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2657 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QO_EVAL_LOC), batching_challenge, p), p)
2658 batching_challenge := mulmod(batching_challenge, rho, p)
2659
2660 // 5: Q4_EVAL_LOC
2661 mstore(BATCH_SCALAR_6_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2662 batched_evaluation := addmod(batched_evaluation, mulmod(mload(Q4_EVAL_LOC), batching_challenge, p), p)
2663 batching_challenge := mulmod(batching_challenge, rho, p)
2664
2665 // 6: QLOOKUP_EVAL_LOC
2666 mstore(BATCH_SCALAR_7_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2667 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QLOOKUP_EVAL_LOC), batching_challenge, p), p)
2668 batching_challenge := mulmod(batching_challenge, rho, p)
2669
2670 // 7: QARITH_EVAL_LOC
2671 mstore(BATCH_SCALAR_8_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2672 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QARITH_EVAL_LOC), batching_challenge, p), p)
2673 batching_challenge := mulmod(batching_challenge, rho, p)
2674
2675 // 8: QRANGE_EVAL_LOC
2676 mstore(BATCH_SCALAR_9_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2677 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QRANGE_EVAL_LOC), batching_challenge, p), p)
2678 batching_challenge := mulmod(batching_challenge, rho, p)
2679
2680 // 9: QELLIPTIC_EVAL_LOC
2681 mstore(BATCH_SCALAR_10_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2682 batched_evaluation := addmod(
2683 batched_evaluation,
2684 mulmod(mload(QELLIPTIC_EVAL_LOC), batching_challenge, p),
2685 p
2686 )
2687 batching_challenge := mulmod(batching_challenge, rho, p)
2688
2689 // 10: QMEMORY_EVAL_LOC
2690 mstore(BATCH_SCALAR_11_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2691 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QMEMORY_EVAL_LOC), batching_challenge, p), p)
2692 batching_challenge := mulmod(batching_challenge, rho, p)
2693
2694 // 11: QNNF_EVAL_LOC
2695 mstore(BATCH_SCALAR_12_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2696 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QNNF_EVAL_LOC), batching_challenge, p), p)
2697 batching_challenge := mulmod(batching_challenge, rho, p)
2698
2699 // 12: QPOSEIDON2_EXTERNAL_EVAL_LOC
2700 mstore(BATCH_SCALAR_13_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2701 batched_evaluation := addmod(
2702 batched_evaluation,
2703 mulmod(mload(QPOSEIDON2_EXTERNAL_EVAL_LOC), batching_challenge, p),
2704 p
2705 )
2706 batching_challenge := mulmod(batching_challenge, rho, p)
2707
2708 // 13: QPOSEIDON2_INTERNAL_EVAL_LOC
2709 mstore(BATCH_SCALAR_14_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2710 batched_evaluation := addmod(
2711 batched_evaluation,
2712 mulmod(mload(QPOSEIDON2_INTERNAL_EVAL_LOC), batching_challenge, p),
2713 p
2714 )
2715 batching_challenge := mulmod(batching_challenge, rho, p)
2716
2717 // 14: SIGMA1_EVAL_LOC
2718 mstore(BATCH_SCALAR_15_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2719 batched_evaluation := addmod(batched_evaluation, mulmod(mload(SIGMA1_EVAL_LOC), batching_challenge, p), p)
2720 batching_challenge := mulmod(batching_challenge, rho, p)
2721
2722 // 15: SIGMA2_EVAL_LOC
2723 mstore(BATCH_SCALAR_16_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2724 batched_evaluation := addmod(batched_evaluation, mulmod(mload(SIGMA2_EVAL_LOC), batching_challenge, p), p)
2725 batching_challenge := mulmod(batching_challenge, rho, p)
2726
2727 // 16: SIGMA3_EVAL_LOC
2728 mstore(BATCH_SCALAR_17_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2729 batched_evaluation := addmod(batched_evaluation, mulmod(mload(SIGMA3_EVAL_LOC), batching_challenge, p), p)
2730 batching_challenge := mulmod(batching_challenge, rho, p)
2731
2732 // 17: SIGMA4_EVAL_LOC
2733 mstore(BATCH_SCALAR_18_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2734 batched_evaluation := addmod(batched_evaluation, mulmod(mload(SIGMA4_EVAL_LOC), batching_challenge, p), p)
2735 batching_challenge := mulmod(batching_challenge, rho, p)
2736
2737 // 18: ID1_EVAL_LOC
2738 mstore(BATCH_SCALAR_19_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2739 batched_evaluation := addmod(batched_evaluation, mulmod(mload(ID1_EVAL_LOC), batching_challenge, p), p)
2740 batching_challenge := mulmod(batching_challenge, rho, p)
2741
2742 // 19: ID2_EVAL_LOC
2743 mstore(BATCH_SCALAR_20_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2744 batched_evaluation := addmod(batched_evaluation, mulmod(mload(ID2_EVAL_LOC), batching_challenge, p), p)
2745 batching_challenge := mulmod(batching_challenge, rho, p)
2746
2747 // 20: ID3_EVAL_LOC
2748 mstore(BATCH_SCALAR_21_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2749 batched_evaluation := addmod(batched_evaluation, mulmod(mload(ID3_EVAL_LOC), batching_challenge, p), p)
2750 batching_challenge := mulmod(batching_challenge, rho, p)
2751
2752 // 21: ID4_EVAL_LOC
2753 mstore(BATCH_SCALAR_22_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2754 batched_evaluation := addmod(batched_evaluation, mulmod(mload(ID4_EVAL_LOC), batching_challenge, p), p)
2755 batching_challenge := mulmod(batching_challenge, rho, p)
2756
2757 // 22: TABLE1_EVAL_LOC
2758 mstore(BATCH_SCALAR_23_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2759 batched_evaluation := addmod(batched_evaluation, mulmod(mload(TABLE1_EVAL_LOC), batching_challenge, p), p)
2760 batching_challenge := mulmod(batching_challenge, rho, p)
2761
2762 // 23: TABLE2_EVAL_LOC
2763 mstore(BATCH_SCALAR_24_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2764 batched_evaluation := addmod(batched_evaluation, mulmod(mload(TABLE2_EVAL_LOC), batching_challenge, p), p)
2765 batching_challenge := mulmod(batching_challenge, rho, p)
2766
2767 // 24: TABLE3_EVAL_LOC
2768 mstore(BATCH_SCALAR_25_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2769 batched_evaluation := addmod(batched_evaluation, mulmod(mload(TABLE3_EVAL_LOC), batching_challenge, p), p)
2770 batching_challenge := mulmod(batching_challenge, rho, p)
2771
2772 // 25: TABLE4_EVAL_LOC
2773 mstore(BATCH_SCALAR_26_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2774 batched_evaluation := addmod(batched_evaluation, mulmod(mload(TABLE4_EVAL_LOC), batching_challenge, p), p)
2775 batching_challenge := mulmod(batching_challenge, rho, p)
2776
2777 // 26: LAGRANGE_FIRST_EVAL_LOC
2778 mstore(BATCH_SCALAR_27_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2779 batched_evaluation := addmod(
2780 batched_evaluation,
2781 mulmod(mload(LAGRANGE_FIRST_EVAL_LOC), batching_challenge, p),
2782 p
2783 )
2784 batching_challenge := mulmod(batching_challenge, rho, p)
2785
2786 // 27: LAGRANGE_LAST_EVAL_LOC
2787 mstore(BATCH_SCALAR_28_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2788 batched_evaluation := addmod(
2789 batched_evaluation,
2790 mulmod(mload(LAGRANGE_LAST_EVAL_LOC), batching_challenge, p),
2791 p
2792 )
2793 batching_challenge := mulmod(batching_challenge, rho, p)
2794
2795 // 28: W1_EVAL_LOC
2796 mstore(BATCH_SCALAR_29_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2797 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W1_EVAL_LOC), batching_challenge, p), p)
2798 batching_challenge := mulmod(batching_challenge, rho, p)
2799
2800 // 29: W2_EVAL_LOC
2801 mstore(BATCH_SCALAR_30_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2802 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W2_EVAL_LOC), batching_challenge, p), p)
2803 batching_challenge := mulmod(batching_challenge, rho, p)
2804
2805 // 30: W3_EVAL_LOC
2806 mstore(BATCH_SCALAR_31_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2807 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W3_EVAL_LOC), batching_challenge, p), p)
2808 batching_challenge := mulmod(batching_challenge, rho, p)
2809
2810 // 31: W4_EVAL_LOC
2811 mstore(BATCH_SCALAR_32_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2812 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W4_EVAL_LOC), batching_challenge, p), p)
2813 batching_challenge := mulmod(batching_challenge, rho, p)
2814
2815 // 32: Z_PERM_EVAL_LOC
2816 mstore(BATCH_SCALAR_33_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2817 batched_evaluation := addmod(batched_evaluation, mulmod(mload(Z_PERM_EVAL_LOC), batching_challenge, p), p)
2818 batching_challenge := mulmod(batching_challenge, rho, p)
2819
2820 // 33: LOOKUP_INVERSES_EVAL_LOC
2821 mstore(BATCH_SCALAR_34_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2822 batched_evaluation := addmod(
2823 batched_evaluation,
2824 mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), batching_challenge, p),
2825 p
2826 )
2827 batching_challenge := mulmod(batching_challenge, rho, p)
2828
2829 // 34: LOOKUP_READ_COUNTS_EVAL_LOC
2830 mstore(BATCH_SCALAR_35_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2831 batched_evaluation := addmod(
2832 batched_evaluation,
2833 mulmod(mload(LOOKUP_READ_COUNTS_EVAL_LOC), batching_challenge, p),
2834 p
2835 )
2836 batching_challenge := mulmod(batching_challenge, rho, p)
2837
2838 // 35: LOOKUP_READ_TAGS_EVAL_LOC
2839 mstore(BATCH_SCALAR_36_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2840 batched_evaluation := addmod(
2841 batched_evaluation,
2842 mulmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), batching_challenge, p),
2843 p
2844 )
2845 batching_challenge := mulmod(batching_challenge, rho, p)
2846
2847 // Unrolled for NUMBER_OF_SHIFTED_ENTITIES = 5
2848 // for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) {
2849 // scalars[i] = mem.shiftedScalar.neg() * mem.batchingChallenge;
2850 // mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - 1] * mem.batchingChallenge);
2851 // mem.batchingChallenge = mem.batchingChallenge * tp.rho;
2852 // }
2853
2854 // 28: W1_EVAL_LOC
2855 mstore(
2856 BATCH_SCALAR_29_LOC,
2857 addmod(mload(BATCH_SCALAR_29_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p)
2858 )
2859 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W1_SHIFT_EVAL_LOC), batching_challenge, p), p)
2860 batching_challenge := mulmod(batching_challenge, rho, p)
2861
2862 // 29: W2_EVAL_LOC
2863 mstore(
2864 BATCH_SCALAR_30_LOC,
2865 addmod(mload(BATCH_SCALAR_30_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p)
2866 )
2867 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W2_SHIFT_EVAL_LOC), batching_challenge, p), p)
2868 batching_challenge := mulmod(batching_challenge, rho, p)
2869
2870 // 30: W3_EVAL_LOC
2871 mstore(
2872 BATCH_SCALAR_31_LOC,
2873 addmod(mload(BATCH_SCALAR_31_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p)
2874 )
2875 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W3_SHIFT_EVAL_LOC), batching_challenge, p), p)
2876 batching_challenge := mulmod(batching_challenge, rho, p)
2877
2878 // 31: W4_EVAL_LOC
2879 mstore(
2880 BATCH_SCALAR_32_LOC,
2881 addmod(mload(BATCH_SCALAR_32_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p)
2882 )
2883 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W4_SHIFT_EVAL_LOC), batching_challenge, p), p)
2884 batching_challenge := mulmod(batching_challenge, rho, p)
2885
2886 // 32: Z_PERM_EVAL_LOC
2887 mstore(
2888 BATCH_SCALAR_33_LOC,
2889 addmod(mload(BATCH_SCALAR_33_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p)
2890 )
2891 batched_evaluation := addmod(
2892 batched_evaluation,
2893 mulmod(mload(Z_PERM_SHIFT_EVAL_LOC), batching_challenge, p),
2894 p
2895 )
2896 batching_challenge := mulmod(batching_challenge, rho, p)
2897
2898 mstore(BATCHED_EVALUATION_LOC, batched_evaluation)
2899
2900 // Compute fold pos evaluations
2901 {
2902 // TODO: work out the stack here
2903 mstore(CHALL_POW_LOC, POWERS_OF_EVALUATION_CHALLENGE_{{ LOG_N_MINUS_ONE }}_LOC)
2904 mstore(SUMCHECK_U_LOC, SUM_U_CHALLENGE_{{ LOG_N_MINUS_ONE }})
2905 mstore(GEMINI_A_LOC, GEMINI_A_EVAL_{{ LOG_N_MINUS_ONE }})
2906 // Inversion of this value was included in batch inversion above
2907 let inverted_chall_pow_minus_u_loc := INVERTED_CHALLENEGE_POW_MINUS_U_{{ LOG_N_MINUS_ONE }}_LOC
2908 let fold_pos_off := FOLD_POS_EVALUATIONS_{{ LOG_N_MINUS_ONE }}_LOC
2909
2910 let batchedEvalAcc := batched_evaluation
2911 for { let i := LOG_N } gt(i, 0) { i := sub(i, 1) } {
2912 let chall_pow := mload(mload(CHALL_POW_LOC))
2913 let sum_check_u := mload(mload(SUMCHECK_U_LOC))
2914
2915 // challengePower * batchedEvalAccumulator * 2
2916 let batchedEvalRoundAcc := mulmod(chall_pow, mulmod(batchedEvalAcc, 2, p), p)
2917 // (challengePower * (ONE - u) - u)
2918 let chall_pow_times_1_minus_u := mulmod(chall_pow, addmod(1, sub(p, sum_check_u), p), p)
2919
2920 batchedEvalRoundAcc := addmod(
2921 batchedEvalRoundAcc,
2922 sub(
2923 p,
2924 mulmod(
2925 mload(mload(GEMINI_A_LOC)),
2926 addmod(chall_pow_times_1_minus_u, sub(p, sum_check_u), p),
2927 p
2928 )
2929 ),
2930 p
2931 )
2932
2933 batchedEvalRoundAcc := mulmod(batchedEvalRoundAcc, mload(inverted_chall_pow_minus_u_loc), p)
2934
2935 batchedEvalAcc := batchedEvalRoundAcc
2936 mstore(fold_pos_off, batchedEvalRoundAcc)
2937
2938 mstore(CHALL_POW_LOC, sub(mload(CHALL_POW_LOC), 0x20))
2939 mstore(SUMCHECK_U_LOC, sub(mload(SUMCHECK_U_LOC), 0x20))
2940 mstore(GEMINI_A_LOC, sub(mload(GEMINI_A_LOC), 0x20))
2941 inverted_chall_pow_minus_u_loc := sub(inverted_chall_pow_minus_u_loc, 0x20)
2942 fold_pos_off := sub(fold_pos_off, 0x20)
2943 }
2944 }
2945
2946 let constant_term_acc := mulmod(mload(FOLD_POS_EVALUATIONS_0_LOC), mload(POS_INVERTED_DENOM_0_LOC), p)
2947 {
2948 let shplonk_nu := mload(SHPLONK_NU_CHALLENGE)
2949
2950 constant_term_acc := addmod(
2951 constant_term_acc,
2952 mulmod(mload(GEMINI_A_EVAL_0), mulmod(shplonk_nu, mload(NEG_INVERTED_DENOM_0_LOC), p), p),
2953 p
2954 )
2955
2956 let shplonk_nu_sqr := mulmod(shplonk_nu, shplonk_nu, p)
2957 batching_challenge := shplonk_nu_sqr
2958
2959 // TODO: improve scheduling
2960 mstore(SS_POS_INV_DENOM_LOC, POS_INVERTED_DENOM_1_LOC)
2961 mstore(SS_NEG_INV_DENOM_LOC, NEG_INVERTED_DENOM_1_LOC)
2962
2963 mstore(SS_GEMINI_EVALS_LOC, GEMINI_A_EVAL_1)
2964 let fold_pos_evals_loc := FOLD_POS_EVALUATIONS_1_LOC
2965
2966 let scalars_loc := BATCH_SCALAR_37_LOC
2967
2968 for { let i := 0 } lt(i, sub(LOG_N, 1)) { i := add(i, 1) } {
2969 let scaling_factor_pos := mulmod(batching_challenge, mload(mload(SS_POS_INV_DENOM_LOC)), p)
2970 let scaling_factor_neg :=
2971 mulmod(batching_challenge, mulmod(shplonk_nu, mload(mload(SS_NEG_INV_DENOM_LOC)), p), p)
2972
2973 mstore(scalars_loc, addmod(sub(p, scaling_factor_neg), sub(p, scaling_factor_pos), p))
2974
2975 let accum_contribution := mulmod(scaling_factor_neg, mload(mload(SS_GEMINI_EVALS_LOC)), p)
2976 accum_contribution := addmod(
2977 accum_contribution,
2978 mulmod(scaling_factor_pos, mload(fold_pos_evals_loc), p),
2979 p
2980 )
2981
2982 constant_term_acc := addmod(constant_term_acc, accum_contribution, p)
2983
2984 batching_challenge := mulmod(batching_challenge, shplonk_nu_sqr, p)
2985
2986 mstore(SS_POS_INV_DENOM_LOC, add(mload(SS_POS_INV_DENOM_LOC), 0x20))
2987 mstore(SS_NEG_INV_DENOM_LOC, add(mload(SS_NEG_INV_DENOM_LOC), 0x20))
2988 mstore(SS_GEMINI_EVALS_LOC, add(mload(SS_GEMINI_EVALS_LOC), 0x20))
2989 fold_pos_evals_loc := add(fold_pos_evals_loc, 0x20)
2990 scalars_loc := add(scalars_loc, 0x20)
2991 }
2992 }
2993
2994 let precomp_success_flag := 1
2995 let q := Q // EC group order
2996 {
2997 // The initial accumulator = 1 * shplonk_q
2998 // WORKTODO(md): we can ignore this accumulation as we are multiplying by 1,
2999 // Just set the accumulator instead.
3000 mstore(SCALAR_LOCATION, 0x1)
3001 mcopy(G1_LOCATION, SHPLONK_Q_X_LOC, 0x40)
3002 precomp_success_flag := staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR, 0x40)
3003 }
3004
3005 // Accumulate vk points
3006 loadVk()
3007 {
3008 // Acumulator = acumulator + scalar[1] * vk[0]
3009 mcopy(G1_LOCATION, Q_M_X_LOC, 0x40)
3010 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_1_LOC))
3011 precomp_success_flag := and(
3012 precomp_success_flag,
3013 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3014 )
3015 precomp_success_flag := and(
3016 precomp_success_flag,
3017 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3018 )
3019
3020 // Accumulator = accumulator + scalar[2] * vk[1]
3021 mcopy(G1_LOCATION, Q_C_X_LOC, 0x40)
3022 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_2_LOC))
3023 precomp_success_flag := and(
3024 precomp_success_flag,
3025 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3026 )
3027 precomp_success_flag := and(
3028 precomp_success_flag,
3029 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3030 )
3031
3032 // Accumulator = accumulator + scalar[3] * vk[2]
3033 mcopy(G1_LOCATION, Q_L_X_LOC, 0x40)
3034 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_3_LOC))
3035 precomp_success_flag := and(
3036 precomp_success_flag,
3037 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3038 )
3039 precomp_success_flag := and(
3040 precomp_success_flag,
3041 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3042 )
3043
3044 // Accumulator = accumulator + scalar[4] * vk[3]
3045 mcopy(G1_LOCATION, Q_R_X_LOC, 0x40)
3046 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_4_LOC))
3047 precomp_success_flag := and(
3048 precomp_success_flag,
3049 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3050 )
3051 precomp_success_flag := and(
3052 precomp_success_flag,
3053 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3054 )
3055
3056 // Accumulator = accumulator + scalar[5] * vk[4]
3057 mcopy(G1_LOCATION, Q_O_X_LOC, 0x40)
3058 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_5_LOC))
3059 precomp_success_flag := and(
3060 precomp_success_flag,
3061 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3062 )
3063 precomp_success_flag := and(
3064 precomp_success_flag,
3065 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3066 )
3067
3068 // Accumulator = accumulator + scalar[6] * vk[5]
3069 mcopy(G1_LOCATION, Q_4_X_LOC, 0x40)
3070 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_6_LOC))
3071 precomp_success_flag := and(
3072 precomp_success_flag,
3073 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3074 )
3075 precomp_success_flag := and(
3076 precomp_success_flag,
3077 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3078 )
3079
3080 // Accumulator = accumulator + scalar[7] * vk[6]
3081 mcopy(G1_LOCATION, Q_LOOKUP_X_LOC, 0x40)
3082 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_7_LOC))
3083 precomp_success_flag := and(
3084 precomp_success_flag,
3085 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3086 )
3087 precomp_success_flag := and(
3088 precomp_success_flag,
3089 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3090 )
3091
3092 // Accumulator = accumulator + scalar[8] * vk[7]
3093 mcopy(G1_LOCATION, Q_ARITH_X_LOC, 0x40)
3094 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_8_LOC))
3095 precomp_success_flag := and(
3096 precomp_success_flag,
3097 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3098 )
3099 precomp_success_flag := and(
3100 precomp_success_flag,
3101 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3102 )
3103
3104 // Accumulator = accumulator + scalar[9] * vk[8]
3105 mcopy(G1_LOCATION, Q_DELTA_RANGE_X_LOC, 0x40)
3106 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_9_LOC))
3107 precomp_success_flag := and(
3108 precomp_success_flag,
3109 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3110 )
3111 precomp_success_flag := and(
3112 precomp_success_flag,
3113 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3114 )
3115
3116 // Accumulator = accumulator + scalar[10] * vk[9]
3117 mcopy(G1_LOCATION, Q_ELLIPTIC_X_LOC, 0x40)
3118 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_10_LOC))
3119 precomp_success_flag := and(
3120 precomp_success_flag,
3121 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3122 )
3123 precomp_success_flag := and(
3124 precomp_success_flag,
3125 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3126 )
3127
3128 // Accumulator = accumulator + scalar[11] * vk[10]
3129 mcopy(G1_LOCATION, Q_MEMORY_X_LOC, 0x40)
3130 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_11_LOC))
3131 precomp_success_flag := and(
3132 precomp_success_flag,
3133 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3134 )
3135 precomp_success_flag := and(
3136 precomp_success_flag,
3137 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3138 )
3139
3140 // Accumulator = accumulator + scalar[12] * vk[11]
3141 mcopy(G1_LOCATION, Q_NNF_X_LOC, 0x40)
3142 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_12_LOC))
3143 precomp_success_flag := and(
3144 precomp_success_flag,
3145 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3146 )
3147 precomp_success_flag := and(
3148 precomp_success_flag,
3149 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3150 )
3151
3152 // Accumulator = accumulator + scalar[13] * vk[12]
3153 mcopy(G1_LOCATION, Q_POSEIDON_2_EXTERNAL_X_LOC, 0x40)
3154 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_13_LOC))
3155 precomp_success_flag := and(
3156 precomp_success_flag,
3157 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3158 )
3159 precomp_success_flag := and(
3160 precomp_success_flag,
3161 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3162 )
3163
3164 // Accumulator = accumulator + scalar[14] * vk[13]
3165 mcopy(G1_LOCATION, Q_POSEIDON_2_INTERNAL_X_LOC, 0x40)
3166 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_14_LOC))
3167 precomp_success_flag := and(
3168 precomp_success_flag,
3169 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3170 )
3171 precomp_success_flag := and(
3172 precomp_success_flag,
3173 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3174 )
3175
3176 // Accumulator = accumulator + scalar[15] * vk[14]
3177 mcopy(G1_LOCATION, SIGMA_1_X_LOC, 0x40)
3178 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_15_LOC))
3179 precomp_success_flag := and(
3180 precomp_success_flag,
3181 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3182 )
3183 precomp_success_flag := and(
3184 precomp_success_flag,
3185 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3186 )
3187
3188 // Accumulator = accumulator + scalar[16] * vk[15]
3189 mcopy(G1_LOCATION, SIGMA_2_X_LOC, 0x40)
3190 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_16_LOC))
3191 precomp_success_flag := and(
3192 precomp_success_flag,
3193 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3194 )
3195 precomp_success_flag := and(
3196 precomp_success_flag,
3197 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3198 )
3199
3200 // Accumulator = accumulator + scalar[17] * vk[16]
3201 mcopy(G1_LOCATION, SIGMA_3_X_LOC, 0x40)
3202 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_17_LOC))
3203 precomp_success_flag := and(
3204 precomp_success_flag,
3205 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3206 )
3207 precomp_success_flag := and(
3208 precomp_success_flag,
3209 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3210 )
3211
3212 // Accumulator = accumulator + scalar[18] * vk[17]
3213 mcopy(G1_LOCATION, SIGMA_4_X_LOC, 0x40)
3214 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_18_LOC))
3215 precomp_success_flag := and(
3216 precomp_success_flag,
3217 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3218 )
3219 precomp_success_flag := and(
3220 precomp_success_flag,
3221 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3222 )
3223
3224 // Accumulator = accumulator + scalar[19] * vk[18]
3225 mcopy(G1_LOCATION, ID_1_X_LOC, 0x40)
3226 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_19_LOC))
3227 precomp_success_flag := and(
3228 precomp_success_flag,
3229 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3230 )
3231 precomp_success_flag := and(
3232 precomp_success_flag,
3233 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3234 )
3235
3236 // Accumulator = accumulator + scalar[20] * vk[19]
3237 mcopy(G1_LOCATION, ID_2_X_LOC, 0x40)
3238 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_20_LOC))
3239 precomp_success_flag := and(
3240 precomp_success_flag,
3241 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3242 )
3243 precomp_success_flag := and(
3244 precomp_success_flag,
3245 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3246 )
3247
3248 // Accumulator = accumulator + scalar[21] * vk[20]
3249 mcopy(G1_LOCATION, ID_3_X_LOC, 0x40)
3250 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_21_LOC))
3251 precomp_success_flag := and(
3252 precomp_success_flag,
3253 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3254 )
3255 precomp_success_flag := and(
3256 precomp_success_flag,
3257 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3258 )
3259
3260 // Accumulator = accumulator + scalar[22] * vk[21]
3261 mcopy(G1_LOCATION, ID_4_X_LOC, 0x40)
3262 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_22_LOC))
3263 precomp_success_flag := and(
3264 precomp_success_flag,
3265 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3266 )
3267 precomp_success_flag := and(
3268 precomp_success_flag,
3269 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3270 )
3271
3272 // Accumulator = accumulator + scalar[23] * vk[22]
3273 mcopy(G1_LOCATION, TABLE_1_X_LOC, 0x40)
3274 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_23_LOC))
3275 precomp_success_flag := and(
3276 precomp_success_flag,
3277 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3278 )
3279 precomp_success_flag := and(
3280 precomp_success_flag,
3281 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3282 )
3283
3284 // Accumulator = accumulator + scalar[24] * vk[23]
3285 mcopy(G1_LOCATION, TABLE_2_X_LOC, 0x40)
3286 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_24_LOC))
3287 precomp_success_flag := and(
3288 precomp_success_flag,
3289 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3290 )
3291 precomp_success_flag := and(
3292 precomp_success_flag,
3293 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3294 )
3295
3296 // Accumulator = accumulator + scalar[25] * vk[24]
3297 mcopy(G1_LOCATION, TABLE_3_X_LOC, 0x40)
3298 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_25_LOC))
3299 precomp_success_flag := and(
3300 precomp_success_flag,
3301 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3302 )
3303 precomp_success_flag := and(
3304 precomp_success_flag,
3305 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3306 )
3307
3308 // Accumulator = accumulator + scalar[26] * vk[25]
3309 mcopy(G1_LOCATION, TABLE_4_X_LOC, 0x40)
3310 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_26_LOC))
3311 precomp_success_flag := and(
3312 precomp_success_flag,
3313 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3314 )
3315 precomp_success_flag := and(
3316 precomp_success_flag,
3317 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3318 )
3319
3320 // Accumulator = accumulator + scalar[27] * vk[26]
3321 mcopy(G1_LOCATION, LAGRANGE_FIRST_X_LOC, 0x40)
3322 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_27_LOC))
3323 precomp_success_flag := and(
3324 precomp_success_flag,
3325 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3326 )
3327 precomp_success_flag := and(
3328 precomp_success_flag,
3329 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3330 )
3331
3332 // Accumulator = accumulator + scalar[28] * vk[27]
3333 mcopy(G1_LOCATION, LAGRANGE_LAST_X_LOC, 0x40)
3334 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_28_LOC))
3335 precomp_success_flag := and(
3336 precomp_success_flag,
3337 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3338 )
3339 precomp_success_flag := and(
3340 precomp_success_flag,
3341 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3342 )
3343
3344 // Accumulate proof points
3345 // Accumulator = accumulator + scalar[29] * w_l
3346 mcopy(G1_LOCATION, W_L_X_LOC, 0x40)
3347 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_29_LOC))
3348 precomp_success_flag := and(
3349 precomp_success_flag,
3350 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3351 )
3352 precomp_success_flag := and(
3353 precomp_success_flag,
3354 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3355 )
3356
3357 // Accumulator = accumulator + scalar[30] * w_r
3358 mcopy(G1_LOCATION, W_R_X_LOC, 0x40)
3359 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_30_LOC))
3360 precomp_success_flag := and(
3361 precomp_success_flag,
3362 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3363 )
3364 precomp_success_flag := and(
3365 precomp_success_flag,
3366 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3367 )
3368
3369 // Accumulator = accumulator + scalar[31] * w_o
3370 mcopy(G1_LOCATION, W_O_X_LOC, 0x40)
3371 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_31_LOC))
3372 precomp_success_flag := and(
3373 precomp_success_flag,
3374 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3375 )
3376 precomp_success_flag := and(
3377 precomp_success_flag,
3378 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3379 )
3380
3381 // Accumulator = accumulator + scalar[32] * w_4
3382 mcopy(G1_LOCATION, W_4_X_LOC, 0x40)
3383 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_32_LOC))
3384 precomp_success_flag := and(
3385 precomp_success_flag,
3386 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3387 )
3388 precomp_success_flag := and(
3389 precomp_success_flag,
3390 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3391 )
3392
3393 // Accumulator = accumulator + scalar[33] * z_perm
3394 mcopy(G1_LOCATION, Z_PERM_X_LOC, 0x40)
3395 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_33_LOC))
3396 precomp_success_flag := and(
3397 precomp_success_flag,
3398 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3399 )
3400 precomp_success_flag := and(
3401 precomp_success_flag,
3402 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3403 )
3404
3405 // Accumulator = accumulator + scalar[34] * lookup_inverses
3406 mcopy(G1_LOCATION, LOOKUP_INVERSES_X_LOC, 0x40)
3407 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_34_LOC))
3408 precomp_success_flag := and(
3409 precomp_success_flag,
3410 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3411 )
3412 precomp_success_flag := and(
3413 precomp_success_flag,
3414 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3415 )
3416
3417 // Accumulator = accumulator + scalar[35] * lookup_read_counts
3418 mcopy(G1_LOCATION, LOOKUP_READ_COUNTS_X_LOC, 0x40)
3419 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_35_LOC))
3420 precomp_success_flag := and(
3421 precomp_success_flag,
3422 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3423 )
3424 precomp_success_flag := and(
3425 precomp_success_flag,
3426 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3427 )
3428
3429 // Accumulator = accumulator + scalar[36] * lookup_read_tags
3430 mcopy(G1_LOCATION, LOOKUP_READ_TAGS_X_LOC, 0x40)
3431 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_36_LOC))
3432 precomp_success_flag := and(
3433 precomp_success_flag,
3434 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3435 )
3436 precomp_success_flag := and(
3437 precomp_success_flag,
3438 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3439 )
3440
3441 // Accumulate these LOG_N scalars with the gemini fold univariates
3442 {
3443 {
3446 }
3447 }
3448
3449 {
3450 // Accumulate the constant term accumulator
3451 // Accumulator = accumulator + 1 * costant term accumulator
3452 mstore(G1_LOCATION, 0x01)
3453 mstore(G1_Y_LOCATION, 0x02)
3454 mstore(SCALAR_LOCATION, constant_term_acc)
3455 precomp_success_flag := and(
3456 precomp_success_flag,
3457 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3458 )
3459 precomp_success_flag := and(
3460 precomp_success_flag,
3461 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3462 )
3463
3464 // Accumlate final quotient commitment into shplonk check
3465 // Accumulator = accumulator + shplonkZ * quotient commitment
3466 mcopy(G1_LOCATION, KZG_QUOTIENT_X_LOC, 0x40)
3467
3468 mstore(SCALAR_LOCATION, mload(SHPLONK_Z_CHALLENGE))
3469 precomp_success_flag := and(
3470 precomp_success_flag,
3471 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3472 )
3473 precomp_success_flag := and(
3474 precomp_success_flag,
3475 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3476 )
3477 }
3478
3479 // All G1 points were validated on-curve during input validation.
3480 // precomp_success_flag now only tracks ecAdd/ecMul precompile success.
3481 if iszero(precomp_success_flag) {
3482 mstore(0x00, SHPLEMINI_FAILED_SELECTOR)
3483 revert(0x00, 0x04)
3484 }
3485
3486 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
3487 /* SHPLEMINI - complete */
3488 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
3489
3490 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
3491 /* PAIRING CHECK */
3492 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
3493 {
3494 // P_1
3495 mstore(0xc0, mload(KZG_QUOTIENT_X_LOC))
3496 mstore(0xe0, sub(q, mload(KZG_QUOTIENT_Y_LOC)))
3497
3498 // p_0_agg
3499 // 0x80 - p_0_agg x
3500 // 0xa0 - p_0_agg y
3501 mcopy(0x80, ACCUMULATOR, 0x40)
3502
3503 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
3504 /* PAIRING AGGREGATION */
3505 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
3506 // Read the pairing encoded in the first 8 field elements of the proof (2 limbs per coordinate)
3507 let p0_other_x := mload(PAIRING_POINT_0_X_0_LOC)
3508 p0_other_x := or(shl(136, mload(PAIRING_POINT_0_X_1_LOC)), p0_other_x)
3509
3510 let p0_other_y := mload(PAIRING_POINT_0_Y_0_LOC)
3511 p0_other_y := or(shl(136, mload(PAIRING_POINT_0_Y_1_LOC)), p0_other_y)
3512
3513 let p1_other_x := mload(PAIRING_POINT_1_X_0_LOC)
3514 p1_other_x := or(shl(136, mload(PAIRING_POINT_1_X_1_LOC)), p1_other_x)
3515
3516 let p1_other_y := mload(PAIRING_POINT_1_Y_0_LOC)
3517 p1_other_y := or(shl(136, mload(PAIRING_POINT_1_Y_1_LOC)), p1_other_y)
3518
3519 // Check if pairing points are default (all zero = infinity = no recursive verification)
3520 let pairing_points_are_default := iszero(or(or(p0_other_x, p0_other_y), or(p1_other_x, p1_other_y)))
3521
3522 let success := 1
3523 // Only aggregate if pairing points are non-default
3524 if iszero(pairing_points_are_default) {
3525 // Reconstructed coordinates must be < Q to prevent malleability
3526 if iszero(and(
3527 and(lt(p0_other_x, q), lt(p0_other_y, q)),
3528 and(lt(p1_other_x, q), lt(p1_other_y, q))
3529 )) {
3530 mstore(0x00, VALUE_GE_GROUP_ORDER_SELECTOR)
3531 revert(0x00, 0x04)
3532 }
3533
3534 // Validate p_0_other not point of infinity
3535 success := iszero(iszero(or(p0_other_x, p0_other_y)))
3536 // Validate p_1_other not point of infinity
3537 success := and(success, iszero(iszero(or(p1_other_x, p1_other_y))))
3538
3539 // p_0
3540 mstore(0x00, p0_other_x)
3541 mstore(0x20, p0_other_y)
3542
3543 // p_1
3544 mstore(0x40, p1_other_x)
3545 mstore(0x60, p1_other_y)
3546
3547 // p_1_agg is already in the correct location
3548
3549 let recursion_separator := keccak256(0x00, 0x100)
3550
3551 // Write separator back to scratch space
3552 mstore(0x00, p0_other_x)
3553
3554 mstore(0x40, recursion_separator)
3555 // recursion_separator * p_0_other
3556 success := and(success, staticcall(gas(), 0x07, 0x00, 0x60, 0x00, 0x40))
3557
3558 // (recursion_separator * p_0_other) + p_0_agg
3559 mcopy(0x40, 0x80, 0x40)
3560 // p_0 = (recursion_separator * p_0_other) + p_0_agg
3561 success := and(success, staticcall(gas(), 6, 0x00, 0x80, 0x00, 0x40))
3562
3563 mstore(0x40, p1_other_x)
3564 mstore(0x60, p1_other_y)
3565 mstore(0x80, recursion_separator)
3566
3567 success := and(success, staticcall(gas(), 7, 0x40, 0x60, 0x40, 0x40))
3568
3569 // Write p_1_agg back to scratch space
3570 mcopy(0x80, 0xc0, 0x40)
3571
3572 // 0xc0 - (recursion_separator * p_1_other) + p_1_agg
3573 success := and(success, staticcall(gas(), 6, 0x40, 0x80, 0xc0, 0x40))
3574 }
3575 // If default pairing points, use p_0_agg and p_1_agg directly (already at 0x80, 0xc0)
3576 if pairing_points_are_default {
3577 // Copy p_0_agg to 0x00 for pairing input
3578 mcopy(0x00, 0x80, 0x40)
3579 // p_1_agg stays at 0xc0
3580 }
3581
3582 // G2 [1]
3583 mstore(0x40, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2)
3584 mstore(0x60, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed)
3585 mstore(0x80, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b)
3586 mstore(0xa0, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa)
3587
3588 // G2 [x]
3589 mstore(0x100, 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1)
3590 mstore(0x120, 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0)
3591 mstore(0x140, 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4)
3592 mstore(0x160, 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55)
3593
3594 let pairing_success := and(success, staticcall(gas(), 8, 0x00, 0x180, 0x00, 0x20))
3595 if iszero(and(pairing_success, mload(0x00))) {
3596 mstore(0x00, SHPLEMINI_FAILED_SELECTOR)
3597 revert(0x00, 0x04)
3598 }
3599
3600 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
3601 /* PAIRING CHECK - Complete */
3602 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
3603 }
3604 {
3605 mstore(0x00, 0x01)
3606 return(0x00, 0x20) // Proof succeeded!
3607 }
3608 }
3609 }
3610 }
3611}
3612)";
3613
3614template <typename Field> std::string field_to_hex(const Field& f)
3615{
3616 std::ostringstream os;
3617 os << f;
3618 return os.str();
3619}
3620
3621inline std::string int_to_hex(size_t i)
3622{
3623 std::ostringstream os;
3624 os << "0x" << std::hex << i;
3625 return os.str();
3626}
3627
3628inline std::string get_optimized_honk_solidity_verifier(auto const& verification_key)
3629{
3630 std::string template_str = HONK_CONTRACT_OPT_SOURCE;
3631
3632 // Helper function to replace template variables
3633 auto set_template_param = [&template_str](const std::string& key, const std::string& value) {
3634 std::string::size_type pos = 0;
3635 std::string pattern = "{{ " + key + " }}";
3636 while ((pos = template_str.find(pattern, pos)) != std::string::npos) {
3637 template_str.replace(pos, pattern.length(), value);
3638 pos += value.length();
3639 }
3640 };
3641
3642 set_template_param("VK_HASH", field_to_hex(verification_key->hash()));
3643 set_template_param("CIRCUIT_SIZE", std::to_string(1 << verification_key->log_circuit_size));
3644 set_template_param("LOG_CIRCUIT_SIZE", std::to_string(verification_key->log_circuit_size));
3645 set_template_param("NUM_PUBLIC_INPUTS", std::to_string(verification_key->num_public_inputs));
3646 // REAL_NUM_PUBLIC_INPUTS excludes the 8 pairing point limbs that are part of the proof structure
3647 set_template_param("REAL_NUM_PUBLIC_INPUTS",
3648 std::to_string(verification_key->num_public_inputs - bb::PAIRING_POINTS_SIZE));
3649 set_template_param("LOG_N_MINUS_ONE", std::to_string(verification_key->log_circuit_size - 1));
3650 set_template_param("NUMBER_OF_BARYCENTRIC_INVERSES", std::to_string(verification_key->log_circuit_size * 8));
3651
3652 uint32_t gemini_fold_univariate_length = static_cast<uint32_t>((verification_key->log_circuit_size - 1) * 0x40);
3653 uint32_t gemini_fold_univariate_hash_length = static_cast<uint32_t>(gemini_fold_univariate_length + 0x20);
3654 uint32_t gemini_evals_length = static_cast<uint32_t>(verification_key->log_circuit_size * 0x20);
3655 uint32_t gemini_evals_hash_length = static_cast<uint32_t>(gemini_evals_length + 0x20);
3656
3657 set_template_param("GEMINI_FOLD_UNIVARIATE_LENGTH", int_to_hex(gemini_fold_univariate_length));
3658 set_template_param("GEMINI_FOLD_UNIVARIATE_HASH_LENGTH", int_to_hex(gemini_fold_univariate_hash_length));
3659 set_template_param("GEMINI_EVALS_LENGTH", int_to_hex(gemini_evals_length));
3660 set_template_param("GEMINI_EVALS_HASH_LENGTH", int_to_hex(gemini_evals_hash_length));
3661
3662 // Verification Key
3663 set_template_param("Q_L_X_LOC", field_to_hex(verification_key->q_l.x));
3664 set_template_param("Q_L_Y_LOC", field_to_hex(verification_key->q_l.y));
3665 set_template_param("Q_R_X_LOC", field_to_hex(verification_key->q_r.x));
3666 set_template_param("Q_R_Y_LOC", field_to_hex(verification_key->q_r.y));
3667 set_template_param("Q_O_X_LOC", field_to_hex(verification_key->q_o.x));
3668 set_template_param("Q_O_Y_LOC", field_to_hex(verification_key->q_o.y));
3669 set_template_param("Q_4_X_LOC", field_to_hex(verification_key->q_4.x));
3670 set_template_param("Q_4_Y_LOC", field_to_hex(verification_key->q_4.y));
3671 set_template_param("Q_M_X_LOC", field_to_hex(verification_key->q_m.x));
3672 set_template_param("Q_M_Y_LOC", field_to_hex(verification_key->q_m.y));
3673 set_template_param("Q_C_X_LOC", field_to_hex(verification_key->q_c.x));
3674 set_template_param("Q_C_Y_LOC", field_to_hex(verification_key->q_c.y));
3675 set_template_param("Q_LOOKUP_X_LOC", field_to_hex(verification_key->q_lookup.x));
3676 set_template_param("Q_LOOKUP_Y_LOC", field_to_hex(verification_key->q_lookup.y));
3677 set_template_param("Q_ARITH_X_LOC", field_to_hex(verification_key->q_arith.x));
3678 set_template_param("Q_ARITH_Y_LOC", field_to_hex(verification_key->q_arith.y));
3679 set_template_param("Q_DELTA_RANGE_X_LOC", field_to_hex(verification_key->q_delta_range.x));
3680 set_template_param("Q_DELTA_RANGE_Y_LOC", field_to_hex(verification_key->q_delta_range.y));
3681 set_template_param("Q_ELLIPTIC_X_LOC", field_to_hex(verification_key->q_elliptic.x));
3682 set_template_param("Q_ELLIPTIC_Y_LOC", field_to_hex(verification_key->q_elliptic.y));
3683 set_template_param("Q_MEMORY_X_LOC", field_to_hex(verification_key->q_memory.x));
3684 set_template_param("Q_MEMORY_Y_LOC", field_to_hex(verification_key->q_memory.y));
3685 set_template_param("Q_NNF_X_LOC", field_to_hex(verification_key->q_nnf.x));
3686 set_template_param("Q_NNF_Y_LOC", field_to_hex(verification_key->q_nnf.y));
3687 set_template_param("Q_POSEIDON_2_EXTERNAL_X_LOC", field_to_hex(verification_key->q_poseidon2_external.x));
3688 set_template_param("Q_POSEIDON_2_EXTERNAL_Y_LOC", field_to_hex(verification_key->q_poseidon2_external.y));
3689 set_template_param("Q_POSEIDON_2_INTERNAL_X_LOC", field_to_hex(verification_key->q_poseidon2_internal.x));
3690 set_template_param("Q_POSEIDON_2_INTERNAL_Y_LOC", field_to_hex(verification_key->q_poseidon2_internal.y));
3691 set_template_param("SIGMA_1_X_LOC", field_to_hex(verification_key->sigma_1.x));
3692 set_template_param("SIGMA_1_Y_LOC", field_to_hex(verification_key->sigma_1.y));
3693 set_template_param("SIGMA_2_X_LOC", field_to_hex(verification_key->sigma_2.x));
3694 set_template_param("SIGMA_2_Y_LOC", field_to_hex(verification_key->sigma_2.y));
3695 set_template_param("SIGMA_3_X_LOC", field_to_hex(verification_key->sigma_3.x));
3696 set_template_param("SIGMA_3_Y_LOC", field_to_hex(verification_key->sigma_3.y));
3697 set_template_param("SIGMA_4_X_LOC", field_to_hex(verification_key->sigma_4.x));
3698 set_template_param("SIGMA_4_Y_LOC", field_to_hex(verification_key->sigma_4.y));
3699 set_template_param("TABLE_1_X_LOC", field_to_hex(verification_key->table_1.x));
3700 set_template_param("TABLE_1_Y_LOC", field_to_hex(verification_key->table_1.y));
3701 set_template_param("TABLE_2_X_LOC", field_to_hex(verification_key->table_2.x));
3702 set_template_param("TABLE_2_Y_LOC", field_to_hex(verification_key->table_2.y));
3703 set_template_param("TABLE_3_X_LOC", field_to_hex(verification_key->table_3.x));
3704 set_template_param("TABLE_3_Y_LOC", field_to_hex(verification_key->table_3.y));
3705 set_template_param("TABLE_4_X_LOC", field_to_hex(verification_key->table_4.x));
3706 set_template_param("TABLE_4_Y_LOC", field_to_hex(verification_key->table_4.y));
3707 set_template_param("ID_1_X_LOC", field_to_hex(verification_key->id_1.x));
3708 set_template_param("ID_1_Y_LOC", field_to_hex(verification_key->id_1.y));
3709 set_template_param("ID_2_X_LOC", field_to_hex(verification_key->id_2.x));
3710 set_template_param("ID_2_Y_LOC", field_to_hex(verification_key->id_2.y));
3711 set_template_param("ID_3_X_LOC", field_to_hex(verification_key->id_3.x));
3712 set_template_param("ID_3_Y_LOC", field_to_hex(verification_key->id_3.y));
3713 set_template_param("ID_4_X_LOC", field_to_hex(verification_key->id_4.x));
3714 set_template_param("ID_4_Y_LOC", field_to_hex(verification_key->id_4.y));
3715 set_template_param("LAGRANGE_FIRST_X_LOC", field_to_hex(verification_key->lagrange_first.x));
3716 set_template_param("LAGRANGE_FIRST_Y_LOC", field_to_hex(verification_key->lagrange_first.y));
3717 set_template_param("LAGRANGE_LAST_X_LOC", field_to_hex(verification_key->lagrange_last.x));
3718 set_template_param("LAGRANGE_LAST_Y_LOC", field_to_hex(verification_key->lagrange_last.y));
3719
3720 // Generate unrolled sections based on LOG_N
3721 auto generate_unroll_section = [](const std::string& section_name, auto log_n) {
3722 std::ostringstream code;
3723
3724 if (section_name == "ACCUMULATE_INVERSES") {
3725 // Generate INVERTED_CHALLENEGE_POW_MINUS_U accumulations
3726 for (int i = 0; i < log_n; ++i) {
3727 code << " // i = " << i << "\n";
3728 code << " mstore(TEMP_" << i << "_LOC, accumulator)\n";
3729 code << " accumulator := mulmod(accumulator, mload(INVERTED_CHALLENEGE_POW_MINUS_U_" << i
3730 << "_LOC), p)\n";
3731 }
3732
3733 code << "\n // Accumulate pos inverted denom\n";
3734 int temp_idx = log_n;
3735 for (int i = 0; i < log_n; ++i) {
3736 code << " // i = " << i << "\n";
3737 code << " mstore(TEMP_" << temp_idx << "_LOC, accumulator)\n";
3738 code << " accumulator := mulmod(accumulator, mload(POS_INVERTED_DENOM_" << i
3739 << "_LOC), p)\n";
3740 temp_idx++;
3741 }
3742
3743 code << "\n // Accumulate neg inverted denom\n";
3744 for (int i = 0; i < log_n; ++i) {
3745 code << " // i = " << i << "\n";
3746 code << " mstore(TEMP_" << temp_idx << "_LOC, accumulator)\n";
3747 code << " accumulator := mulmod(accumulator, mload(NEG_INVERTED_DENOM_" << i
3748 << "_LOC), p)\n";
3749 temp_idx++;
3750 }
3751 } else if (section_name == "COLLECT_INVERSES") {
3752 int temp_idx = 3 * log_n - 1;
3753
3754 // Process NEG_INVERTED_DENOM in reverse order
3755 code << " // i = " << log_n << "\n";
3756 for (int i = log_n - 1; i >= 0; --i) {
3757 code << " {\n";
3758 code << " let tmp := mulmod(accumulator, mload(TEMP_" << temp_idx << "_LOC), p)\n";
3759 code << " accumulator := mulmod(accumulator, mload(NEG_INVERTED_DENOM_" << i
3760 << "_LOC), p)\n";
3761 code << " mstore(NEG_INVERTED_DENOM_" << i << "_LOC, tmp)\n";
3762 code << " }\n";
3763 if (i > 0) {
3764 code << " // i = " << i << "\n";
3765 }
3766 temp_idx--;
3767 }
3768
3769 code << "\n // Unrolled for LOG_N = " << log_n << "\n";
3770 code << " // i = " << log_n << "\n";
3771
3772 // Process POS_INVERTED_DENOM in reverse order
3773 for (int i = log_n - 1; i >= 0; --i) {
3774 code << " {\n";
3775 code << " let tmp := mulmod(accumulator, mload(TEMP_" << temp_idx << "_LOC), p)\n";
3776 code << " accumulator := mulmod(accumulator, mload(POS_INVERTED_DENOM_" << i
3777 << "_LOC), p)\n";
3778 code << " mstore(POS_INVERTED_DENOM_" << i << "_LOC, tmp)\n";
3779 code << " }\n";
3780 if (i > 0) {
3781 code << " // i = " << i << "\n";
3782 }
3783 temp_idx--;
3784 }
3785
3786 code << "\n // i = " << log_n << "\n";
3787
3788 // Process INVERTED_CHALLENEGE_POW_MINUS_U in reverse order
3789 for (int i = log_n - 1; i >= 0; --i) {
3790 code << " {\n";
3791 code << " let tmp := mulmod(accumulator, mload(TEMP_" << temp_idx << "_LOC), p)\n";
3792 code << " accumulator := mulmod(accumulator, mload(INVERTED_CHALLENEGE_POW_MINUS_U_" << i
3793 << "_LOC), p)\n";
3794 code << " mstore(INVERTED_CHALLENEGE_POW_MINUS_U_" << i << "_LOC, tmp)\n";
3795 code << " }\n";
3796 if (i > 0) {
3797 code << " // i = " << i << "\n";
3798 }
3799 temp_idx--;
3800 }
3801 } else if (section_name == "ACCUMULATE_GEMINI_FOLD_UNIVARIATE") {
3802 // Generate GEMINI_FOLD_UNIVARIATE accumulations
3803 // We need log_n - 1 folding commitments
3804 for (int i = 0; i < log_n - 1; ++i) {
3805 // Validate on curve then accumulate
3806 code << " {\n";
3807 code << " let x := mload(GEMINI_FOLD_UNIVARIATE_" << i << "_X_LOC)\n";
3808 code << " let y := mload(GEMINI_FOLD_UNIVARIATE_" << i << "_Y_LOC)\n";
3809 code << " let xx := mulmod(x, x, q)\n";
3810 code << " // validate on curve\n";
3811 code << " precomp_success_flag := and(eq(mulmod(y, y, q), addmod(mulmod(x, "
3812 "xx, q), 3, q)), precomp_success_flag)\n";
3813 code << " }\n";
3814 code << " mcopy(G1_LOCATION, GEMINI_FOLD_UNIVARIATE_" << i << "_X_LOC, 0x40)\n";
3815 code << " mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_" << (37 + i) << "_LOC))\n";
3816 code << " precomp_success_flag :=\n";
3817 code << " and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, "
3818 "ACCUMULATOR_2, 0x40))\n";
3819 code << " precomp_success_flag :=\n";
3820 code << " and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, "
3821 "ACCUMULATOR, 0x40))\n";
3822 if (i < log_n - 2) {
3823 code << "\n";
3824 }
3825 }
3826 } else if (section_name == "GEMINI_FOLD_UNIVARIATE_ON_CURVE") {
3827 // Generate GEMINI_FOLD_UNIVARIATE_ON_CURVE validations
3828 // We need log_n - 1 folding commitments to validate
3829 for (int i = 0; i < log_n - 1; ++i) {
3830 code << " success_flag := and(success_flag, "
3831 "validateProofPointOnCurve(GEMINI_FOLD_UNIVARIATE_"
3832 << i << "_X_LOC, q))\n";
3833 }
3834 }
3835
3836 return code.str();
3837 };
3838
3839 // Replace UNROLL_SECTION blocks
3840 int log_n = static_cast<int>(verification_key->log_circuit_size);
3841
3842 // Replace ACCUMULATE_INVERSES section
3843 {
3844 std::string::size_type start_pos = template_str.find("/// {{ UNROLL_SECTION_START ACCUMULATE_INVERSES }}");
3845 std::string::size_type end_pos = template_str.find("/// {{UNROLL_SECTION_END ACCUMULATE_INVERSES }}");
3846 if (start_pos != std::string::npos && end_pos != std::string::npos) {
3847 std::string::size_type start_line_end = template_str.find("\n", start_pos);
3848 std::string generated_code = generate_unroll_section("ACCUMULATE_INVERSES", log_n);
3849 template_str = template_str.substr(0, start_line_end + 1) + generated_code + template_str.substr(end_pos);
3850 }
3851 }
3852
3853 // Replace COLLECT_INVERSES section
3854 {
3855 std::string::size_type start_pos = template_str.find("// {{ UNROLL_SECTION_START COLLECT_INVERSES }}");
3856 std::string::size_type end_pos = template_str.find("// {{ UNROLL_SECTION_END COLLECT_INVERSES }}");
3857 if (start_pos != std::string::npos && end_pos != std::string::npos) {
3858 std::string::size_type start_line_end = template_str.find("\n", start_pos);
3859 std::string generated_code = generate_unroll_section("COLLECT_INVERSES", log_n);
3860 template_str = template_str.substr(0, start_line_end + 1) + generated_code + template_str.substr(end_pos);
3861 }
3862 }
3863
3864 // Replace ACCUMULATE_GEMINI_FOLD_UNIVARIATE section
3865 {
3866 std::string::size_type start_pos =
3867 template_str.find("/// {{ UNROLL_SECTION_START ACCUMULATE_GEMINI_FOLD_UNIVARIATE }}");
3868 std::string::size_type end_pos =
3869 template_str.find("/// {{ UNROLL_SECTION_END ACCUMULATE_GEMINI_FOLD_UNIVARIATE }}");
3870 if (start_pos != std::string::npos && end_pos != std::string::npos) {
3871 std::string::size_type start_line_end = template_str.find("\n", start_pos);
3872 std::string generated_code = generate_unroll_section("ACCUMULATE_GEMINI_FOLD_UNIVARIATE", log_n);
3873 template_str = template_str.substr(0, start_line_end + 1) + generated_code + template_str.substr(end_pos);
3874 }
3875 }
3876
3877 // Replace GEMINI_FOLD_UNIVARIATE_ON_CURVE section
3878 {
3879 std::string::size_type start_pos =
3880 template_str.find("/// {{ UNROLL_SECTION_START GEMINI_FOLD_UNIVARIATE_ON_CURVE }}");
3881 std::string::size_type end_pos =
3882 template_str.find("/// {{ UNROLL_SECTION_END GEMINI_FOLD_UNIVARIATE_ON_CURVE }}");
3883 if (start_pos != std::string::npos && end_pos != std::string::npos) {
3884 std::string::size_type start_line_end = template_str.find("\n", start_pos);
3885 std::string generated_code = generate_unroll_section("GEMINI_FOLD_UNIVARIATE_ON_CURVE", log_n);
3886 template_str = template_str.substr(0, start_line_end + 1) + generated_code + template_str.substr(end_pos);
3887 }
3888 }
3889
3890 // Replace Memory Layout
3891 {
3892 std::string::size_type start_pos = template_str.find("// {{ SECTION_START MEMORY_LAYOUT }}");
3893 std::string::size_type end_pos = template_str.find("// {{ SECTION_END MEMORY_LAYOUT }}");
3894 if (start_pos != std::string::npos && end_pos != std::string::npos) {
3895 std::string::size_type start_line_end = template_str.find("\n", start_pos);
3896 std::string generated_code = generate_memory_offsets(log_n);
3897 template_str = template_str.substr(0, start_line_end + 1) + generated_code + template_str.substr(end_pos);
3898 }
3899 }
3900
3901 return template_str;
3902}
std::string field_to_hex(const Field &f)
std::string get_optimized_honk_solidity_verifier(auto const &verification_key)
std::string generate_memory_offsets(int log_n)
std::string int_to_hex(size_t i)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)