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