16static const char HONK_ZK_CONTRACT_OPT_SOURCE[] = R
"(
17// SPDX-License-Identifier: Apache-2.0
18// Copyright 2022 Aztec
19pragma solidity ^0.8.27;
22 function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool);
27uint256 constant NUMBER_OF_SUBRELATIONS = 31;
28uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 9;
29uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9;
30uint256 constant NUMBER_OF_ENTITIES = 42;
31uint256 constant NUMBER_UNSHIFTED = 37;
32uint256 constant NUMBER_TO_BE_SHIFTED = 5;
33uint256 constant PAIRING_POINTS_SIZE = 8;
35uint256 constant VK_HASH = {{ VK_HASH }};
36uint256 constant CIRCUIT_SIZE = {{ CIRCUIT_SIZE }};
37uint256 constant LOG_N = {{ LOG_CIRCUIT_SIZE }};
38uint256 constant NUMBER_PUBLIC_INPUTS = {{ NUM_PUBLIC_INPUTS }};
39uint256 constant REAL_NUMBER_PUBLIC_INPUTS = {{ REAL_NUM_PUBLIC_INPUTS }};
40uint256 constant PUBLIC_INPUTS_OFFSET = 5; // NUM_DISABLED_ROWS_IN_SUMCHECK + NUM_ZERO_ROWS = 4 + 1
42contract HonkVerifier is IVerifier {
43 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
45 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
102 // {{ SECTION_START MEMORY_LAYOUT }}
103 // {{ SECTION_END MEMORY_LAYOUT }}
105 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
106 /* SUMCHECK - MEMORY ALIASES */
107 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
108 uint256 internal constant EC_X_1 = W2_EVAL_LOC;
109 uint256 internal constant EC_Y_1 = W3_EVAL_LOC;
110 uint256 internal constant EC_X_2 = W1_SHIFT_EVAL_LOC;
111 uint256 internal constant EC_Y_2 = W4_SHIFT_EVAL_LOC;
112 uint256 internal constant EC_Y_3 = W3_SHIFT_EVAL_LOC;
113 uint256 internal constant EC_X_3 = W2_SHIFT_EVAL_LOC;
115 // Aliases for selectors (Elliptic curve gadget)
116 uint256 internal constant EC_Q_SIGN = QL_EVAL_LOC;
117 uint256 internal constant EC_Q_IS_DOUBLE = QM_EVAL_LOC;
119 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
121 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
122 uint256 internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = 17; // -(-17)
124 // Auxiliary relation constants
125 // In the Non Native Field Arithmetic Relation, large field elements are broken up into 4 LIMBs of 68 `LIMB_SIZE` bits each.
126 uint256 internal constant LIMB_SIZE = 0x100000000000000000; // 2<<68
128 // In the Delta Range Check Relation, there is a range checking relation that can validate 14-bit range checks with only 1
129 // extra relation in the execution trace.
130 // For large range checks, we decompose them into a collection of 14-bit range checks.
131 uint256 internal constant SUBLIMB_SHIFT = 0x4000; // 2<<14
133 // Poseidon2 internal constants
134 // https://github.com/HorizenLabs/poseidon2/blob/main/poseidon2_rust_params.sage - derivation code
135 uint256 internal constant POS_INTERNAL_MATRIX_D_0 =
136 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7;
137 uint256 internal constant POS_INTERNAL_MATRIX_D_1 =
138 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b;
139 uint256 internal constant POS_INTERNAL_MATRIX_D_2 =
140 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15;
141 uint256 internal constant POS_INTERNAL_MATRIX_D_3 =
142 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b;
144 // Constants inspecting proof components
145 uint256 internal constant NUMBER_OF_UNSHIFTED_ENTITIES = 37;
146 // Shifted columns are columes that are duplicates of existing columns but right-shifted by 1
147 uint256 internal constant NUMBER_OF_SHIFTED_ENTITIES = 5;
148 uint256 internal constant TOTAL_NUMBER_OF_ENTITIES = 42;
150 // Constants for performing batch multiplication
151 uint256 internal constant ACCUMULATOR = 0x00;
152 uint256 internal constant ACCUMULATOR_2 = 0x40;
153 uint256 internal constant G1_LOCATION = 0x60;
154 uint256 internal constant G1_Y_LOCATION = 0x80;
155 uint256 internal constant SCALAR_LOCATION = 0xa0;
158 uint256 internal constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order
160 // Field order constants
162 uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;
163 uint256 internal constant P = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
164 uint256 internal constant P_SUB_1 = 21888242871839275222246405745257275088548364400416034343698204186575808495616;
165 uint256 internal constant P_SUB_2 = 21888242871839275222246405745257275088548364400416034343698204186575808495615;
166 uint256 internal constant P_SUB_3 = 21888242871839275222246405745257275088548364400416034343698204186575808495614;
168 // Barycentric evaluation constants
169 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_0 =
170 0x0000000000000000000000000000000000000000000000000000000000009d80;
171 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_1 =
172 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51;
173 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_2 =
174 0x00000000000000000000000000000000000000000000000000000000000005a0;
175 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_3 =
176 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31;
177 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_4 =
178 0x0000000000000000000000000000000000000000000000000000000000000240;
179 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_5 =
180 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31;
181 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_6 =
182 0x00000000000000000000000000000000000000000000000000000000000005a0;
183 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_7 =
184 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51;
185 uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_8 =
186 0x0000000000000000000000000000000000000000000000000000000000009d80;
188 // ZK-specific constants
189 uint256 internal constant SUBGROUP_SIZE = 256;
190 uint256 internal constant SUBGROUP_GENERATOR = 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76;
191 uint256 internal constant SUBGROUP_GENERATOR_INVERSE = 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6;
192 uint256 internal constant LIBRA_COMMITMENTS = 3;
193 uint256 internal constant LIBRA_EVALUATIONS = 4;
194 uint256 internal constant SHIFTED_COMMITMENTS_START = 30;
196 // Constants for computing public input delta
197 uint256 internal constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28;
199 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
201 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
202 // The errors match Errors.sol
204 bytes4 internal constant VALUE_GE_LIMB_MAX_SELECTOR = 0xeb73e0bd;
205 bytes4 internal constant VALUE_GE_GROUP_ORDER_SELECTOR = 0x607be13e;
206 bytes4 internal constant VALUE_GE_FIELD_ORDER_SELECTOR = 0x20a33589;
207 bytes4 internal constant SUMCHECK_FAILED_SELECTOR = 0x9fc3a218;
208 bytes4 internal constant SHPLEMINI_FAILED_SELECTOR = 0xa5d82e8a;
210 bytes4 internal constant PROOF_LENGTH_WRONG_WITH_LOG_N_SELECTOR = 0x59895a53;
211 bytes4 internal constant PUBLIC_INPUTS_LENGTH_WRONG_SELECTOR = 0xfa066593;
213 bytes4 internal constant MODEXP_FAILED_SELECTOR = 0xf442f163;
214 bytes4 internal constant CONSISTENCY_CHECK_FAILED_SELECTOR = 0xa2a2ac83;
215 bytes4 internal constant GEMINI_CHALLENGE_IN_SUBGROUP_SELECTOR = 0x835eb8f7;
222 bytes32[] calldata /*public_inputs*/
229 // Load the proof from calldata in one large chunk
231 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
232 /* LOAD VERIFCATION KEY */
233 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
234 // Write the verification key into memory
236 // Although defined at the top of the file, it is used towards the end of the algorithm when batching in the commitment scheme.
238 mstore(Q_L_X_LOC, {{ Q_L_X_LOC }})
239 mstore(Q_L_Y_LOC, {{ Q_L_Y_LOC }})
240 mstore(Q_R_X_LOC, {{ Q_R_X_LOC }})
241 mstore(Q_R_Y_LOC, {{ Q_R_Y_LOC }})
242 mstore(Q_O_X_LOC, {{ Q_O_X_LOC }})
243 mstore(Q_O_Y_LOC, {{ Q_O_Y_LOC }})
244 mstore(Q_4_X_LOC, {{ Q_4_X_LOC }})
245 mstore(Q_4_Y_LOC, {{ Q_4_Y_LOC }})
246 mstore(Q_M_X_LOC, {{ Q_M_X_LOC }})
247 mstore(Q_M_Y_LOC, {{ Q_M_Y_LOC }})
248 mstore(Q_C_X_LOC, {{ Q_C_X_LOC }})
249 mstore(Q_C_Y_LOC, {{ Q_C_Y_LOC }})
250 mstore(Q_LOOKUP_X_LOC, {{ Q_LOOKUP_X_LOC }})
251 mstore(Q_LOOKUP_Y_LOC, {{ Q_LOOKUP_Y_LOC }})
252 mstore(Q_ARITH_X_LOC, {{ Q_ARITH_X_LOC }})
253 mstore(Q_ARITH_Y_LOC, {{ Q_ARITH_Y_LOC }})
254 mstore(Q_DELTA_RANGE_X_LOC, {{ Q_DELTA_RANGE_X_LOC }})
255 mstore(Q_DELTA_RANGE_Y_LOC, {{ Q_DELTA_RANGE_Y_LOC }})
256 mstore(Q_ELLIPTIC_X_LOC, {{ Q_ELLIPTIC_X_LOC }})
257 mstore(Q_ELLIPTIC_Y_LOC, {{ Q_ELLIPTIC_Y_LOC }})
258 mstore(Q_MEMORY_X_LOC, {{ Q_MEMORY_X_LOC }})
259 mstore(Q_MEMORY_Y_LOC, {{ Q_MEMORY_Y_LOC }})
260 mstore(Q_NNF_X_LOC, {{ Q_NNF_X_LOC }})
261 mstore(Q_NNF_Y_LOC, {{ Q_NNF_Y_LOC }})
262 mstore(Q_POSEIDON_2_EXTERNAL_X_LOC, {{ Q_POSEIDON_2_EXTERNAL_X_LOC }})
263 mstore(Q_POSEIDON_2_EXTERNAL_Y_LOC, {{ Q_POSEIDON_2_EXTERNAL_Y_LOC }})
264 mstore(Q_POSEIDON_2_INTERNAL_X_LOC, {{ Q_POSEIDON_2_INTERNAL_X_LOC }})
265 mstore(Q_POSEIDON_2_INTERNAL_Y_LOC, {{ Q_POSEIDON_2_INTERNAL_Y_LOC }})
266 mstore(SIGMA_1_X_LOC, {{ SIGMA_1_X_LOC }})
267 mstore(SIGMA_1_Y_LOC, {{ SIGMA_1_Y_LOC }})
268 mstore(SIGMA_2_X_LOC, {{ SIGMA_2_X_LOC }})
269 mstore(SIGMA_2_Y_LOC, {{ SIGMA_2_Y_LOC }})
270 mstore(SIGMA_3_X_LOC, {{ SIGMA_3_X_LOC }})
271 mstore(SIGMA_3_Y_LOC, {{ SIGMA_3_Y_LOC }})
272 mstore(SIGMA_4_X_LOC, {{ SIGMA_4_X_LOC }})
273 mstore(SIGMA_4_Y_LOC, {{ SIGMA_4_Y_LOC }})
274 mstore(TABLE_1_X_LOC, {{ TABLE_1_X_LOC }})
275 mstore(TABLE_1_Y_LOC, {{ TABLE_1_Y_LOC }})
276 mstore(TABLE_2_X_LOC, {{ TABLE_2_X_LOC }})
277 mstore(TABLE_2_Y_LOC, {{ TABLE_2_Y_LOC }})
278 mstore(TABLE_3_X_LOC, {{ TABLE_3_X_LOC }})
279 mstore(TABLE_3_Y_LOC, {{ TABLE_3_Y_LOC }})
280 mstore(TABLE_4_X_LOC, {{ TABLE_4_X_LOC }})
281 mstore(TABLE_4_Y_LOC, {{ TABLE_4_Y_LOC }})
282 mstore(ID_1_X_LOC, {{ ID_1_X_LOC }})
283 mstore(ID_1_Y_LOC, {{ ID_1_Y_LOC }})
284 mstore(ID_2_X_LOC, {{ ID_2_X_LOC }})
285 mstore(ID_2_Y_LOC, {{ ID_2_Y_LOC }})
286 mstore(ID_3_X_LOC, {{ ID_3_X_LOC }})
287 mstore(ID_3_Y_LOC, {{ ID_3_Y_LOC }})
288 mstore(ID_4_X_LOC, {{ ID_4_X_LOC }})
289 mstore(ID_4_Y_LOC, {{ ID_4_Y_LOC }})
290 mstore(LAGRANGE_FIRST_X_LOC, {{ LAGRANGE_FIRST_X_LOC }})
291 mstore(LAGRANGE_FIRST_Y_LOC, {{ LAGRANGE_FIRST_Y_LOC }})
292 mstore(LAGRANGE_LAST_X_LOC, {{ LAGRANGE_LAST_X_LOC }})
293 mstore(LAGRANGE_LAST_Y_LOC, {{ LAGRANGE_LAST_Y_LOC }})
296 // Prime field order - placing on the stack
300 let proof_ptr := add(calldataload(0x04), 0x24)
302 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
303 /* VALIDATE INPUT LENGTHS */
304 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
305 // Validate proof byte length matches expected size for this circuit's LOG_N (ZK variant).
306 // ZK proof has: 9 witness G1 (18) + 3 libra G1 (6) + LOG_N*9 univariates + 42 evals
307 // + 2 (libraSum,libraEval) + LOG_N gemini evals + 4 libra poly evals
308 // + (LOG_N-1)*2 gemini fold G1 + 2*2 (shplonkQ,kzg) + 8 pairing = (82 + 12*LOG_N) * 32
310 let expected_proof_size := mul(
313 add(24, mul(LOG_N, BATCHED_RELATION_PARTIAL_LENGTH)),
314 add(add(NUMBER_OF_ENTITIES, 2), mul(sub(LOG_N, 1), 2))
316 add(add(LOG_N, LIBRA_EVALUATIONS), add(4, PAIRING_POINTS_SIZE))
320 let proof_length := calldataload(add(calldataload(0x04), 0x04))
321 if iszero(eq(proof_length, expected_proof_size)) {
322 mstore(0x00, PROOF_LENGTH_WRONG_WITH_LOG_N_SELECTOR)
324 mstore(0x24, proof_length)
325 mstore(0x44, expected_proof_size)
329 // Validate public inputs array length matches expected count.
331 let pi_count := calldataload(add(calldataload(0x24), 0x04))
332 if iszero(eq(pi_count, REAL_NUMBER_PUBLIC_INPUTS)) {
333 mstore(0x00, PUBLIC_INPUTS_LENGTH_WRONG_SELECTOR)
338 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
339 /* GENERATE CHALLENGES */
340 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
342 * Proof points (affine coordinates) in the proof are in the following format, where offset is
343 * the offset in the entire proof until the first bit of the x coordinate
348 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
349 /* GENERATE ETA CHALLENGE */
350 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
351 /* ZK Eta challenge participants
354 * - pairing point limbs (8)
355 * - geminiMaskingPoly (G1) <- ZK addition
359 mstore(0x00, VK_HASH)
361 let public_inputs_start := add(calldataload(0x24), 0x24)
362 let public_inputs_size := mul(REAL_NUMBER_PUBLIC_INPUTS, 0x20)
364 // Copy the public inputs into the eta buffer
365 calldatacopy(0x20, public_inputs_start, public_inputs_size)
367 // Copy Pairing points into eta buffer
368 let public_inputs_end := add(0x20, public_inputs_size)
370 calldatacopy(public_inputs_end, proof_ptr, 0x100)
372 // 0x20 * 8 = 0x100 (8 pairing point limbs)
373 // End of public inputs + pairing points
374 // ZK: Copy geminiMaskingPoly(0x40) + w1,w2,w3(0xC0) = 0x100 bytes from proof after pairing
375 calldatacopy(add(0x120, public_inputs_size), add(proof_ptr, 0x100), 0x100)
377 // 0x220 = 0x20 (VK_HASH) + 0x100 (pairing) + 0x40 (geminiMaskingPoly) + 0xC0 (w1,w2,w3)
378 let eta_input_length := add(0x220, public_inputs_size)
380 // Get eta and rom_logup_gamma, and compute eta powers (eta, eta², eta³)
381 let prev_challenge := mod(keccak256(0x00, eta_input_length), p)
382 let eta := prev_challenge
383 mstore(0x00, prev_challenge)
384 prev_challenge := mod(keccak256(0x00, 0x20), p)
385 mstore(0x00, prev_challenge)
387 let eta_two := mulmod(eta, eta, p)
388 let eta_three := mulmod(eta_two, eta, p)
390 mstore(ETA_CHALLENGE, eta)
391 mstore(ETA_TWO_CHALLENGE, eta_two)
392 mstore(ETA_THREE_CHALLENGE, eta_three)
393 mstore(ROM_LOGUP_GAMMA_CHALLENGE, prev_challenge)
395 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
396 /* LOAD PROOF INTO MEMORY */
397 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
398 // As all of our proof points are written in contiguous parts of memory, we call use a single
399 // calldatacopy to place all of our proof into the correct memory regions
400 // We copy the entire proof into memory as we must hash each proof section for challenge
402 // The last item in the proof, and the first item in the proof (pairing point 0)
403 let proof_size := sub(ETA_CHALLENGE, PAIRING_POINT_0_X_0_LOC)
405 calldatacopy(PAIRING_POINT_0_X_0_LOC, proof_ptr, proof_size)
407 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
408 /* VALIDATE PROOF INPUTS */
409 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
410 // Validate all proof elements are within their expected ranges.
411 // Pairing limbs: lo < 2^136, hi < 2^120. G1 coordinates < Q. Fr elements < P.
414 let lo_limb_max := shl(136, 1)
415 let hi_limb_max := shl(120, 1)
418 // 1. Pairing limbs: lo < 2^136, hi < 2^120 (4 pairs, stride 0x40)
419 let ptr := PAIRING_POINT_0_X_0_LOC
420 for {} lt(ptr, GEMINI_MASKING_POLY_X_LOC) { ptr := add(ptr, 0x40) } {
421 valid := and(valid, lt(mload(ptr), lo_limb_max))
422 valid := and(valid, lt(mload(add(ptr, 0x20)), hi_limb_max))
425 mstore(0x00, VALUE_GE_LIMB_MAX_SELECTOR)
429 // 2. G1 coordinates: each < Q
430 // - geminiMaskingPoly + witness commitments + libraConcat (20 slots)
431 for { ptr := GEMINI_MASKING_POLY_X_LOC } lt(ptr, LIBRA_SUM_LOC) { ptr := add(ptr, 0x20) } {
432 valid := and(valid, lt(mload(ptr), q_mod))
434 // - Libra grand product + quotient (4 slots)
435 for { ptr := LIBRA_GRAND_PRODUCT_X_LOC } lt(ptr, GEMINI_FOLD_UNIVARIATE_0_X_LOC) {
436 ptr := add(ptr, 0x20)
438 valid := and(valid, lt(mload(ptr), q_mod))
440 // - Gemini fold commitments (28 slots)
441 for { ptr := GEMINI_FOLD_UNIVARIATE_0_X_LOC } lt(ptr, GEMINI_A_EVAL_0) { ptr := add(ptr, 0x20) } {
442 valid := and(valid, lt(mload(ptr), q_mod))
444 // - Shplonk Q + KZG quotient (4 slots)
445 for { ptr := SHPLONK_Q_X_LOC } lt(ptr, ETA_CHALLENGE) { ptr := add(ptr, 0x20) } {
446 valid := and(valid, lt(mload(ptr), q_mod))
449 mstore(0x00, VALUE_GE_GROUP_ORDER_SELECTOR)
453 // 2b. G1 points: identity (0,0) is accepted.
454 // Polynomial commitments to identically-zero polynomials are
455 // legitimately the identity, and the ecAdd/ecMul precompiles
456 // treat (0,0) as the additive identity per EIP-196. Soundness
457 // against (0,0) substitution for a non-zero commitment is upheld
458 // by sumcheck/Shplemini downstream.
460 // 3. Fr elements: each < P
461 // - libraSum + sumcheck univariates + evals + libraEvaluation (179 slots)
462 for { ptr := LIBRA_SUM_LOC } lt(ptr, LIBRA_GRAND_PRODUCT_X_LOC) {
463 ptr := add(ptr, 0x20)
465 valid := and(valid, lt(mload(ptr), p))
467 // - Gemini evaluations + libra poly evals (19 slots)
468 for { ptr := GEMINI_A_EVAL_0 } lt(ptr, SHPLONK_Q_X_LOC) { ptr := add(ptr, 0x20) } {
469 valid := and(valid, lt(mload(ptr), p))
472 mstore(0x00, VALUE_GE_FIELD_ORDER_SELECTOR)
477 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
478 /* GENERATE BETA and GAMMAA CHALLENGE */
479 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
481 // Generate Beta and Gamma Chalenges
483 // - LOOKUP_READ_COUNTS
484 // - LOOKUP_READ_TAGS
486 mcopy(0x20, LOOKUP_READ_COUNTS_X_LOC, 0xc0)
488 prev_challenge := mod(keccak256(0x00, 0xe0), p)
489 let beta := prev_challenge
490 mstore(0x00, prev_challenge)
491 prev_challenge := mod(keccak256(0x00, 0x20), p)
492 mstore(0x00, prev_challenge)
493 let gamma := prev_challenge
495 mstore(BETA_CHALLENGE, beta)
496 mstore(GAMMA_CHALLENGE, gamma)
498 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
499 /* ALPHA CHALLENGES */
500 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
501 // Generate Alpha challenges - non-linearise the gate contributions
503 // There are 31 total subrelations in this honk relation, we do not need to non linearise the first sub relation.
504 // There are 30 total gate contributions, a gate contribution is analogous to
505 // a custom gate, it is an expression which must evaluate to zero for each
506 // row in the constraint matrix
508 // If we do not non-linearise sub relations, then sub relations which rely
509 // on the same wire will interact with each other's sums.
511 mcopy(0x20, LOOKUP_INVERSES_X_LOC, 0x80)
513 // Generate single alpha challenge and compute its powers
514 prev_challenge := mod(keccak256(0x00, 0xa0), p)
515 mstore(0x00, prev_challenge)
516 let alpha := prev_challenge
517 mstore(ALPHA_CHALLENGE_0, alpha)
519 // Compute powers of alpha: alpha^2, alpha^3, ..., alpha^30
520 let alpha_off_set := ALPHA_CHALLENGE_1
521 for {} lt(alpha_off_set, add(ALPHA_CHALLENGE_29, 0x20)) {} {
522 let prev_alpha := mload(sub(alpha_off_set, 0x20))
523 mstore(alpha_off_set, mulmod(prev_alpha, alpha, p))
524 alpha_off_set := add(alpha_off_set, 0x20)
527 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
528 /* GATE CHALLENGES */
529 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
531 // Store the first gate challenge
532 prev_challenge := mod(keccak256(0x00, 0x20), p)
533 mstore(0x00, prev_challenge)
534 let gate_challenge := prev_challenge
535 mstore(GATE_CHALLENGE_0, gate_challenge)
537 let gate_off := GATE_CHALLENGE_1
538 for {} lt(gate_off, LIBRA_CHALLENGE) {} {
539 let prev := mload(sub(gate_off, 0x20))
541 mstore(gate_off, mulmod(prev, prev, p))
542 gate_off := add(gate_off, 0x20)
545 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
546 /* LIBRA CHALLENGE */
547 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
548 // Generate Libra challenge: hash(prevChallenge, libraConcat.x, libraConcat.y, libraSum)
549 // libraConcat (0x40) + libraSum (0x20) = 0x60 bytes of proof data
550 mcopy(0x20, LIBRA_CONCAT_X_LOC, 0x60)
552 prev_challenge := mod(keccak256(0x00, 0x80), p)
553 mstore(0x00, prev_challenge)
554 let libraChallenge := prev_challenge
555 mstore(LIBRA_CHALLENGE, libraChallenge)
557 // Sumcheck Univariate challenges (ZK variant)
558 // The algebraic relations of the Honk protocol are max degree-7.
559 // To prove satifiability, we multiply the relation by a random (POW) polynomial + masking.
560 // As a result, in every round of sumcheck, the prover sends a degree-9 univariate polynomial.
561 // 9 points are sent as it is enough to uniquely identify the polynomial.
562 let read_off := SUMCHECK_UNIVARIATE_0_0_LOC
563 let write_off := SUM_U_CHALLENGE_0
564 for {} lt(read_off, GEMINI_MASKING_EVAL_LOC) {} {
565 // 0x20 * 9 = 0x120 bytes per round
566 mcopy(0x20, read_off, 0x120)
568 // Hash 0x120 + 0x20 (prev hash) = 0x140
569 prev_challenge := mod(keccak256(0x00, 0x140), p)
570 mstore(0x00, prev_challenge)
572 let sumcheck_u_challenge := prev_challenge
573 mstore(write_off, sumcheck_u_challenge)
575 // Progress read / write pointers
576 read_off := add(read_off, 0x120)
577 write_off := add(write_off, 0x20)
580 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
582 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
583 // The RHO challenge is the hash of the evaluations of all of the wire values
584 // As per usual, it includes the previous challenge
585 // Evaluations of the following wires and their shifts (for relevant wires):
597 // - QNNF (NNF = Non Native Field)
598 // - QPOSEIDON2_EXTERNAL
599 // - QPOSEIDON2_INTERNAL
618 // - LOOKUP_READ_COUNTS
619 // - LOOKUP_READ_TAGS
626 // Hash all evaluations + libraEvaluation + 2 libra commitments (G1)
627 // ZK: 42 evals (GEMINI_MASKING_EVAL through Z_PERM_SHIFT) + libraEval + libraGrandProduct(G1) + libraQuotient(G1)
628 // = 42*0x20 + 0x20 + 2*0x40 = 0x5e0 bytes
629 mcopy(0x20, GEMINI_MASKING_EVAL_LOC, 0x5e0)
630 prev_challenge := mod(keccak256(0x00, 0x600), p)
631 mstore(0x00, prev_challenge)
633 let rho := prev_challenge
635 mstore(RHO_CHALLENGE, rho)
637 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
638 /* GEMINI R CHALLENGE */
639 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
640 // The Gemini R challenge contains a of all of commitments to all of the univariates
641 // evaluated in the Gemini Protocol
642 // So for multivariate polynomials in l variables, we will hash l - 1 commitments.
643 // For this implementation, we have logN number of of rounds and thus logN - 1 committments
644 // The format of these commitments are proof points, which are explained above
647 mcopy(0x20, GEMINI_FOLD_UNIVARIATE_0_X_LOC, {{ GEMINI_FOLD_UNIVARIATE_LENGTH }})
649 prev_challenge := mod(keccak256(0x00, {{ GEMINI_FOLD_UNIVARIATE_HASH_LENGTH }}), p)
650 mstore(0x00, prev_challenge)
652 let geminiR := prev_challenge
654 mstore(GEMINI_R_CHALLENGE, geminiR)
656 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
657 /* SHPLONK NU CHALLENGE */
658 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
659 // The shplonk nu challenge hashes gemini A evaluations + libra poly evaluations
660 // ZK: 0x20 * (logN + 4) = 0x20 * 19 = 0x260
662 mcopy(0x20, GEMINI_A_EVAL_0, {{ GEMINI_EVALS_LENGTH }})
663 prev_challenge := mod(keccak256(0x00, {{ GEMINI_EVALS_HASH_LENGTH }}), p)
664 mstore(0x00, prev_challenge)
666 let shplonkNu := prev_challenge
667 mstore(SHPLONK_NU_CHALLENGE, shplonkNu)
669 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
670 /* SHPLONK Z CHALLENGE */
671 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
672 // Generate Shplonk Z
673 // Hash of the single shplonk Q commitment
674 mcopy(0x20, SHPLONK_Q_X_LOC, 0x40)
675 prev_challenge := mod(keccak256(0x00, 0x60), p)
677 let shplonkZ := prev_challenge
678 mstore(SHPLONK_Z_CHALLENGE, shplonkZ)
680 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
681 /* CHALLENGES COMPLETE */
682 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
685 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
686 /* PUBLIC INPUT DELTA */
687 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
717 let beta := mload(BETA_CHALLENGE)
718 let gamma := mload(GAMMA_CHALLENGE)
719 let pub_off := PUBLIC_INPUTS_OFFSET
721 let numerator_value := 1
722 let denominator_value := 1
724 let p_clone := p // move p to the front of the stack
726 // Assume offset is less than p
727 // numerator_acc = gamma + (beta * (PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset))
729 addmod(gamma, mulmod(beta, add(PERMUTATION_ARGUMENT_VALUE_SEPARATOR, pub_off), p_clone), p_clone)
730 // demonimator_acc = gamma - (beta * (offset + 1))
731 let beta_x_off := mulmod(beta, add(pub_off, 1), p_clone)
732 let denominator_acc := addmod(gamma, sub(p_clone, beta_x_off), p_clone)
734 let valid_inputs := true
735 // Load the starting point of the public inputs (jump over the selector and the length of public inputs [0x24])
736 let public_inputs_ptr := add(calldataload(0x24), 0x24)
738 // endpoint_ptr = public_inputs_ptr + num_inputs * 0x20. // every public input is 0x20 bytes
739 let endpoint_ptr := add(public_inputs_ptr, mul(REAL_NUMBER_PUBLIC_INPUTS, 0x20))
741 for {} lt(public_inputs_ptr, endpoint_ptr) { public_inputs_ptr := add(public_inputs_ptr, 0x20) } {
742 // Get public inputs from calldata
743 let input := calldataload(public_inputs_ptr)
745 valid_inputs := and(valid_inputs, lt(input, p_clone))
747 numerator_value := mulmod(numerator_value, addmod(numerator_acc, input, p_clone), p_clone)
748 denominator_value := mulmod(denominator_value, addmod(denominator_acc, input, p_clone), p_clone)
750 numerator_acc := addmod(numerator_acc, beta, p_clone)
751 denominator_acc := addmod(denominator_acc, sub(p_clone, beta), p_clone)
754 // Revert if not all public inputs are field elements (i.e. < p)
755 if iszero(valid_inputs) {
756 mstore(0x00, VALUE_GE_FIELD_ORDER_SELECTOR)
760 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
761 /* PUBLIC INPUT DELTA - Pairing points accum */
762 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
763 // Pairing points contribution to public inputs delta
764 let pairing_points_ptr := PAIRING_POINT_0_X_0_LOC
765 for {} lt(pairing_points_ptr, GEMINI_MASKING_POLY_X_LOC) { pairing_points_ptr := add(pairing_points_ptr, 0x20) } {
766 let input := mload(pairing_points_ptr)
768 numerator_value := mulmod(numerator_value, addmod(numerator_acc, input, p_clone), p_clone)
769 denominator_value := mulmod(denominator_value, addmod(denominator_acc, input, p_clone), p_clone)
771 numerator_acc := addmod(numerator_acc, beta, p_clone)
772 denominator_acc := addmod(denominator_acc, sub(p_clone, beta), p_clone)
775 mstore(PUBLIC_INPUTS_DELTA_NUMERATOR_CHALLENGE, numerator_value)
776 mstore(PUBLIC_INPUTS_DELTA_DENOMINATOR_CHALLENGE, denominator_value)
778 // PI delta denominator inversion is deferred to the barycentric
779 // batch inversion below.
781 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
782 /* PUBLIC INPUT DELTA - complete */
783 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
785 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
787 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
789 // Sumcheck is used to prove that every relation 0 on each row of the witness.
791 // Given each of the columns of our trace is a multilinear polynomial 𝑃1,…,𝑃𝑁∈𝔽[𝑋0,…,𝑋𝑑−1]. We run sumcheck over the polynomial
793 // 𝐹̃ (𝑋0,…,𝑋𝑑−1)=𝑝𝑜𝑤𝛽(𝑋0,…,𝑋𝑑−1)⋅𝐹(𝑃1(𝑋0,…,𝑋𝑑−1),…,𝑃𝑁(𝑋0,…,𝑋𝑑−1))
795 // The Pow polynomial is a random polynomial that allows us to ceritify that the relations sum to 0 on each row of the witness,
796 // rather than the entire sum just targeting 0.
798 // 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....)
800 // We start with a LOG_N variate multilinear polynomial, each round fixes a variable to a challenge value.
801 // Each round the prover sends a round univariate poly, since the degree of our honk relations is 7 + the pow polynomial the prover
802 // sends a degree-8 univariate on each round.
803 // This is sent efficiently by sending 8 values, enough to represent a unique polynomial.
804 // Barycentric evaluation is used to evaluate the polynomial at any point on the domain, given these 8 unique points.
806 // In the sumcheck protocol, the target sum for each round is the sum of the round univariate evaluated on 0 and 1.
807 // 𝜎𝑖=?𝑆̃ 𝑖(0)+𝑆̃ 𝑖(1)
808 // This is efficiently checked as S(0) and S(1) are sent by the prover as values of the round univariate.
810 // We compute the next challenge by evaluating the round univariate at a random challenge value.
812 // This evaluation is performed via barycentric evaluation.
814 // Once we have reduced the multilinear polynomials into single dimensional polys, we check the entire sumcheck relation matches the target sum.
816 // Below this is composed of 8 relations:
817 // 1. Arithmetic relation - constrains arithmetic
818 // 2. Permutaiton Relation - efficiently encodes copy constraints
819 // 3. Log Derivative Lookup Relation - used for lookup operations
820 // 4. Delta Range Relation - used for efficient range checks
821 // 5. Memory Relation - used for efficient memory operations
822 // 6. NNF Relation - used for efficient Non Native Field operations
823 // 7. Poseidon2 External Relation - used for efficient in-circuit hashing
824 // 8. Poseidon2 Internal Relation - used for efficient in-circuit hashing
826 // These are batched together and evaluated at the same time using the alpha challenges.
829 // We write the barycentric domain values into memory
830 // These are written once per program execution, and reused across all
832 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_0_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_0)
833 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_1_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_1)
834 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_2_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_2)
835 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_3_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_3)
836 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_4_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_4)
837 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_5_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_5)
838 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_6_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_6)
839 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_7_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_7)
840 mstore(BARYCENTRIC_LAGRANGE_DENOMINATOR_8_LOC, BARYCENTRIC_LAGRANGE_DENOMINATOR_8)
842 // Compute the target sums for each round of sumcheck
844 // This requires the barycentric inverses to be computed for each round
845 // Write all of the non inverted barycentric denominators into memory
847 let temp := FOLD_POS_EVALUATIONS_{{ LOG_N_MINUS_ONE }}_LOC // we use fold pos evaluations as we add 0x20 immediately to the pointer to get `BARYCENTRIC_TEMP_0_LOC`
848 let bary_centric_inverses_off := BARYCENTRIC_DENOMINATOR_INVERSES_0_0_LOC
850 let round_challenge_off := SUM_U_CHALLENGE_0
851 for { let round := 0 } lt(round, LOG_N) { round := add(round, 1) } {
852 let round_challenge := mload(round_challenge_off)
853 let bary_lagrange_denominator_off := BARYCENTRIC_LAGRANGE_DENOMINATOR_0_LOC
855 // Unrolled as this loop only has 9 iterations (ZK)
857 let bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
860 bary_lagrange_denominator,
861 addmod(round_challenge, p, p), // sub(p, 0) = p
864 mstore(bary_centric_inverses_off, pre_inv)
865 temp := add(temp, 0x20)
866 mstore(temp, accumulator)
867 accumulator := mulmod(accumulator, pre_inv, p)
870 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
871 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
873 // barycentric_index = 1
874 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
875 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, sub(p, 1), p), p)
876 mstore(bary_centric_inverses_off, pre_inv)
877 temp := add(temp, 0x20)
878 mstore(temp, accumulator)
879 accumulator := mulmod(accumulator, pre_inv, p)
882 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
883 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
885 // barycentric_index = 2
886 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
887 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, sub(p, 2), p), p)
888 mstore(bary_centric_inverses_off, pre_inv)
889 temp := add(temp, 0x20)
890 mstore(temp, accumulator)
891 accumulator := mulmod(accumulator, pre_inv, p)
894 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
895 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
897 // barycentric_index = 3
898 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
899 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, sub(p, 3), p), p)
900 mstore(bary_centric_inverses_off, pre_inv)
901 temp := add(temp, 0x20)
902 mstore(temp, accumulator)
903 accumulator := mulmod(accumulator, pre_inv, p)
906 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
907 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
909 // barycentric_index = 4
910 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
911 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, sub(p, 4), p), p)
912 mstore(bary_centric_inverses_off, pre_inv)
913 temp := add(temp, 0x20)
914 mstore(temp, accumulator)
915 accumulator := mulmod(accumulator, pre_inv, p)
918 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
919 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
921 // barycentric_index = 5
922 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
923 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, sub(p, 5), p), p)
924 mstore(bary_centric_inverses_off, pre_inv)
925 temp := add(temp, 0x20)
926 mstore(temp, accumulator)
927 accumulator := mulmod(accumulator, pre_inv, p)
930 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
931 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
933 // barycentric_index = 6
934 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
935 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, sub(p, 6), p), p)
936 mstore(bary_centric_inverses_off, pre_inv)
937 temp := add(temp, 0x20)
938 mstore(temp, accumulator)
939 accumulator := mulmod(accumulator, pre_inv, p)
942 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
943 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
945 // barycentric_index = 7
946 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
947 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, sub(p, 7), p), p)
948 mstore(bary_centric_inverses_off, pre_inv)
949 temp := add(temp, 0x20)
950 mstore(temp, accumulator)
951 accumulator := mulmod(accumulator, pre_inv, p)
954 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
955 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
957 // barycentric_index = 8 (ZK)
958 bary_lagrange_denominator := mload(bary_lagrange_denominator_off)
959 pre_inv := mulmod(bary_lagrange_denominator, addmod(round_challenge, sub(p, 8), p), p)
960 mstore(bary_centric_inverses_off, pre_inv)
961 temp := add(temp, 0x20)
962 mstore(temp, accumulator)
963 accumulator := mulmod(accumulator, pre_inv, p)
966 bary_lagrange_denominator_off := add(bary_lagrange_denominator_off, 0x20)
967 bary_centric_inverses_off := add(bary_centric_inverses_off, 0x20)
969 round_challenge_off := add(round_challenge_off, 0x20)
973 // Append PI delta denominator to the batch inversion
975 let pi_denom := mload(PUBLIC_INPUTS_DELTA_DENOMINATOR_CHALLENGE)
976 mstore(PUBLIC_INPUTS_DENOM_TEMP_LOC, accumulator)
977 accumulator := mulmod(accumulator, pi_denom, p)
980 // --- Phase 2: Shplemini forward pass ---
981 // Compute shplemini denominators and accumulate into the running product.
982 // Pre-inversion values stored at designated addresses (0x6800+),
983 // which don't overlap with barycentric storage.
985 // Compute powers of evaluation challenge: gemini_r^{2^i}
986 let cache := mload(GEMINI_R_CHALLENGE)
987 mstore(POWERS_OF_EVALUATION_CHALLENGE_0_LOC, cache)
991 // Element 0: gemini_r (seed)
993 let val := mload(GEMINI_R_CHALLENGE)
994 mstore(GEMINI_R_INV_TEMP_LOC, accumulator)
995 accumulator := mulmod(accumulator, val, p)
998 // Append Libra Subgroup Denominator calculation to batch inversion
1001 mload(SHPLONK_Z_CHALLENGE),
1002 sub(p, mulmod(SUBGROUP_GENERATOR, mload(GEMINI_R_CHALLENGE), p)),
1005 mstore(LIBRA_SUBGROUP_DENOM_LOC, val)
1006 mstore(LIBRA_SUBGROUP_DENOM_TEMP_LOC, accumulator)
1007 accumulator := mulmod(accumulator, val, p)
1010 // Elements 1..LOG_N: INVERTED_CHALLENGE_POW_MINUS_U
1011 // Elements LOG_N+1..2*LOG_N: POS_INVERTED_DENOM
1012 // Elements 2*LOG_N+1..3*LOG_N: NEG_INVERTED_DENOM
1017 // Invert all elements (barycentric + PI delta + shplemini) as a single batch
1023 mstore(0x60, accumulator)
1024 mstore(0x80, P_SUB_2)
1026 if iszero(staticcall(gas(), 0x05, 0x00, 0xc0, 0x00, 0x20)) {
1027 mstore(0x00, MODEXP_FAILED_SELECTOR)
1030 accumulator := mload(0x00)
1031 if iszero(accumulator) {
1032 mstore(0x00, MODEXP_FAILED_SELECTOR)
1037 // --- Shplemini backward pass ---
1038 // Extract shplemini inverses in strict reverse order.
1043 // libra subgroup denom
1045 let tmp := mulmod(accumulator, mload(LIBRA_SUBGROUP_DENOM_TEMP_LOC), p)
1046 accumulator := mulmod(accumulator, mload(LIBRA_SUBGROUP_DENOM_LOC), p)
1047 mstore(LIBRA_SUBGROUP_DENOM_LOC, tmp)
1052 let tmp := mulmod(accumulator, mload(GEMINI_R_INV_TEMP_LOC), p)
1053 accumulator := mulmod(accumulator, mload(GEMINI_R_CHALLENGE), p)
1054 mstore(GEMINI_R_INV_LOC, tmp)
1058 // Extract PI delta denominator inverse from the batch
1060 let pi_delta_inv := mulmod(accumulator, mload(PUBLIC_INPUTS_DENOM_TEMP_LOC), p)
1061 accumulator := mulmod(accumulator, mload(PUBLIC_INPUTS_DELTA_DENOMINATOR_CHALLENGE), p)
1063 // Finalize: public_inputs_delta = numerator * (1/denominator)
1065 PUBLIC_INPUTS_DELTA_NUMERATOR_CHALLENGE,
1066 mulmod(mload(PUBLIC_INPUTS_DELTA_NUMERATOR_CHALLENGE), pi_delta_inv, p)
1070 // Normalise as last loop will have incremented the offset
1071 bary_centric_inverses_off := sub(bary_centric_inverses_off, 0x20)
1072 for {} gt(bary_centric_inverses_off, BARYCENTRIC_LAGRANGE_DENOMINATOR_{{ BATCHED_RELATION_PARTIAL_LENGTH_MINUS_ONE }}_LOC) {
1073 bary_centric_inverses_off := sub(bary_centric_inverses_off, 0x20)
1075 let tmp := mulmod(accumulator, mload(temp), p)
1076 accumulator := mulmod(accumulator, mload(bary_centric_inverses_off), p)
1077 mstore(bary_centric_inverses_off, tmp)
1079 temp := sub(temp, 0x20)
1085 // ZK: initial round target = libraChallenge * libraSum
1086 let round_target := mulmod(mload(LIBRA_CHALLENGE), mload(LIBRA_SUM_LOC), p)
1087 let pow_partial_evaluation := 1
1088 let gate_challenge_off := GATE_CHALLENGE_0
1089 let round_univariates_off := SUMCHECK_UNIVARIATE_0_0_LOC
1091 let challenge_off := SUM_U_CHALLENGE_0
1092 let bary_inverses_off := BARYCENTRIC_DENOMINATOR_INVERSES_0_0_LOC
1094 for { let round := 0 } lt(round, LOG_N) { round := add(round, 1) } {
1095 let round_challenge := mload(challenge_off)
1097 // Total sum = u[0] + u[1]
1098 let total_sum := addmod(mload(round_univariates_off), mload(add(round_univariates_off, 0x20)), p)
1099 valid := and(valid, eq(total_sum, round_target))
1101 // Compute next target sum (ZK: 9-element domain)
1102 let numerator_value := round_challenge
1103 numerator_value := mulmod(numerator_value, addmod(round_challenge, sub(p, 1), p), p)
1104 numerator_value := mulmod(numerator_value, addmod(round_challenge, sub(p, 2), p), p)
1105 numerator_value := mulmod(numerator_value, addmod(round_challenge, sub(p, 3), p), p)
1106 numerator_value := mulmod(numerator_value, addmod(round_challenge, sub(p, 4), p), p)
1107 numerator_value := mulmod(numerator_value, addmod(round_challenge, sub(p, 5), p), p)
1108 numerator_value := mulmod(numerator_value, addmod(round_challenge, sub(p, 6), p), p)
1109 numerator_value := mulmod(numerator_value, addmod(round_challenge, sub(p, 7), p), p)
1110 numerator_value := mulmod(numerator_value, addmod(round_challenge, sub(p, 8), p), p)
1112 // // Compute the next round target
1114 for { let i := 0 } lt(i, BATCHED_RELATION_PARTIAL_LENGTH) { i := add(i, 1) } {
1115 let term := mload(round_univariates_off)
1116 let inverse := mload(bary_inverses_off)
1118 term := mulmod(term, inverse, p)
1119 round_target := addmod(round_target, term, p)
1120 round_univariates_off := add(round_univariates_off, 0x20)
1121 bary_inverses_off := add(bary_inverses_off, 0x20)
1124 round_target := mulmod(round_target, numerator_value, p)
1126 // Partially evaluate POW
1127 let gate_challenge := mload(gate_challenge_off)
1128 let gate_challenge_minus_one := sub(gate_challenge, 1)
1130 let univariate_evaluation := addmod(1, mulmod(round_challenge, gate_challenge_minus_one, p), p)
1132 pow_partial_evaluation := mulmod(pow_partial_evaluation, univariate_evaluation, p)
1134 gate_challenge_off := add(gate_challenge_off, 0x20)
1135 challenge_off := add(challenge_off, 0x20)
1139 mstore(0x00, SUMCHECK_FAILED_SELECTOR)
1143 // The final sumcheck round; accumulating evaluations
1144 // Uses pow partial evaluation as the gate scaling factor
1146 mstore(POW_PARTIAL_EVALUATION_LOC, pow_partial_evaluation)
1147 mstore(FINAL_ROUND_TARGET_LOC, round_target)
1149 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1150 /* LOGUP RELATION */
1151 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1188 let w1q1 := mulmod(mload(W1_EVAL_LOC), mload(QL_EVAL_LOC), p)
1189 let w2q2 := mulmod(mload(W2_EVAL_LOC), mload(QR_EVAL_LOC), p)
1190 let w3q3 := mulmod(mload(W3_EVAL_LOC), mload(QO_EVAL_LOC), p)
1191 let w4q3 := mulmod(mload(W4_EVAL_LOC), mload(Q4_EVAL_LOC), p)
1193 let q_arith := mload(QARITH_EVAL_LOC)
1194 // w1w2qm := (w_1 . w_2 . q_m . (QARITH_EVAL_LOC - 3)) / 2
1198 mulmod(mulmod(mload(W1_EVAL_LOC), mload(W2_EVAL_LOC), p), mload(QM_EVAL_LOC), p),
1199 addmod(q_arith, P_SUB_3, p),
1206 // (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
1210 addmod(w4q3, addmod(w3q3, addmod(w2q2, addmod(w1q1, w1w2qm, p), p), p), p),
1214 // if q_arith == 3 we evaluate an additional mini addition gate (on top of the regular one), where:
1215 // w_1 + w_4 - w_1_omega + q_m = 0
1216 // we use this gate to save an addition gate when adding or subtracting non-native field elements
1217 // α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m)
1218 let extra_small_addition_gate_identity :=
1220 addmod(q_arith, P_SUB_2, p),
1224 sub(p, mload(W1_SHIFT_EVAL_LOC)),
1225 addmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p),
1233 // Split up the two relations
1234 let contribution_0 :=
1235 addmod(identity, mulmod(addmod(q_arith, P_SUB_1, p), mload(W4_SHIFT_EVAL_LOC), p), p)
1236 contribution_0 := mulmod(mulmod(contribution_0, q_arith, p), mload(POW_PARTIAL_EVALUATION_LOC), p)
1237 mstore(SUBRELATION_EVAL_6_LOC, contribution_0)
1239 let contribution_1 := mulmod(extra_small_addition_gate_identity, addmod(q_arith, P_SUB_1, p), p)
1240 contribution_1 := mulmod(contribution_1, q_arith, p)
1241 contribution_1 := mulmod(contribution_1, mload(POW_PARTIAL_EVALUATION_LOC), p)
1242 mstore(SUBRELATION_EVAL_7_LOC, contribution_1)
1245 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1246 /* PERMUTATION RELATION */
1247 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1249 let beta := mload(BETA_CHALLENGE)
1250 let gamma := mload(GAMMA_CHALLENGE)
1262 add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(ID1_EVAL_LOC), p)),
1263 add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(ID2_EVAL_LOC), p)),
1268 add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(ID3_EVAL_LOC), p)),
1269 add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(ID4_EVAL_LOC), p)),
1272 let numerator := mulmod(t1, t2, p)
1274 add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA1_EVAL_LOC), p)),
1275 add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA2_EVAL_LOC), p)),
1279 add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA3_EVAL_LOC), p)),
1280 add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA4_EVAL_LOC), p)),
1283 let denominator := mulmod(t1, t2, p)
1287 mulmod(addmod(mload(Z_PERM_EVAL_LOC), mload(LAGRANGE_FIRST_EVAL_LOC), p), numerator, p)
1295 mload(Z_PERM_SHIFT_EVAL_LOC),
1297 mload(LAGRANGE_LAST_EVAL_LOC),
1298 mload(PUBLIC_INPUTS_DELTA_NUMERATOR_CHALLENGE),
1310 acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p)
1311 mstore(SUBRELATION_EVAL_0_LOC, acc)
1314 mulmod(mload(LAGRANGE_LAST_EVAL_LOC), mload(Z_PERM_SHIFT_EVAL_LOC), p),
1315 mload(POW_PARTIAL_EVALUATION_LOC),
1318 mstore(SUBRELATION_EVAL_1_LOC, acc)
1321 // zperm initialization (lagrange_first * z_perm = 0)
1324 mload(LAGRANGE_FIRST_EVAL_LOC),
1325 mload(Z_PERM_EVAL_LOC),
1327 mload(POW_PARTIAL_EVALUATION_LOC),
1329 mstore(SUBRELATION_EVAL_2_LOC, acc)
1333 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1334 /* LOGUP WIDGET EVALUATION */
1335 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1336 // Note: Using beta powers for column batching and gamma for offset ensures soundness
1337 // beta and gamma must be independent challenges (they come from splitting the same hash)
1339 let gamma := mload(GAMMA_CHALLENGE)
1340 let beta := mload(BETA_CHALLENGE)
1341 // Compute beta powers inline (β², β³) for lookup column batching
1342 let beta_sqr := mulmod(beta, beta, p)
1343 let beta_cube := mulmod(beta_sqr, beta, p)
1345 // table_term = table_1 + γ + table_2 * β + table_3 * β² + table_4 * β³
1347 addmod(addmod(mload(TABLE1_EVAL_LOC), gamma, p), mulmod(mload(TABLE2_EVAL_LOC), beta, p), p)
1350 mulmod(mload(TABLE3_EVAL_LOC), beta_sqr, p),
1351 mulmod(mload(TABLE4_EVAL_LOC), beta_cube, p),
1354 let table_term := addmod(t0, t1, p)
1356 // lookup_term = derived_entry_1 + γ + derived_entry_2 * β + derived_entry_3 * β² + q_index * β³
1358 addmod(mload(W1_EVAL_LOC), gamma, p),
1359 mulmod(mload(QR_EVAL_LOC), mload(W1_SHIFT_EVAL_LOC), p),
1362 t1 := addmod(mload(W2_EVAL_LOC), mulmod(mload(QM_EVAL_LOC), mload(W2_SHIFT_EVAL_LOC), p), p)
1363 let t2 := addmod(mload(W3_EVAL_LOC), mulmod(mload(QC_EVAL_LOC), mload(W3_SHIFT_EVAL_LOC), p), p)
1365 let lookup_term := addmod(t0, mulmod(t1, beta, p), p)
1366 lookup_term := addmod(lookup_term, mulmod(t2, beta_sqr, p), p)
1367 lookup_term := addmod(lookup_term, mulmod(mload(QO_EVAL_LOC), beta_cube, p), p)
1369 let lookup_inverse := mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), table_term, p)
1370 let table_inverse := mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), lookup_term, p)
1372 let inverse_exists_xor := addmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), mload(QLOOKUP_EVAL_LOC), p)
1373 inverse_exists_xor := addmod(
1375 sub(p, mulmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), mload(QLOOKUP_EVAL_LOC), p)),
1379 let accumulator_none := mulmod(mulmod(lookup_term, table_term, p), mload(LOOKUP_INVERSES_EVAL_LOC), p)
1380 accumulator_none := addmod(accumulator_none, sub(p, inverse_exists_xor), p)
1381 accumulator_none := mulmod(accumulator_none, mload(POW_PARTIAL_EVALUATION_LOC), p)
1383 let accumulator_one := mulmod(mload(QLOOKUP_EVAL_LOC), lookup_inverse, p)
1384 accumulator_one := addmod(
1386 sub(p, mulmod(mload(LOOKUP_READ_COUNTS_EVAL_LOC), table_inverse, p)),
1390 let read_tag := mload(LOOKUP_READ_TAGS_EVAL_LOC)
1391 let read_tag_boolean_relation := mulmod(read_tag, addmod(read_tag, P_SUB_1, p), p)
1392 read_tag_boolean_relation := mulmod(read_tag_boolean_relation, mload(POW_PARTIAL_EVALUATION_LOC), p)
1394 mstore(SUBRELATION_EVAL_3_LOC, accumulator_none)
1395 mstore(SUBRELATION_EVAL_4_LOC, accumulator_one)
1396 mstore(SUBRELATION_EVAL_5_LOC, read_tag_boolean_relation)
1399 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1400 /* DELTA RANGE RELATION */
1401 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1403 let minus_one := P_SUB_1
1404 let minus_two := P_SUB_2
1405 let minus_three := P_SUB_3
1407 let delta_1 := addmod(mload(W2_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p)
1408 let delta_2 := addmod(mload(W3_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p)
1409 let delta_3 := addmod(mload(W4_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p)
1410 let delta_4 := addmod(mload(W1_SHIFT_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p)
1414 acc := mulmod(acc, addmod(delta_1, minus_one, p), p)
1415 acc := mulmod(acc, addmod(delta_1, minus_two, p), p)
1416 acc := mulmod(acc, addmod(delta_1, minus_three, p), p)
1417 acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p)
1418 acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p)
1419 mstore(SUBRELATION_EVAL_8_LOC, acc)
1424 acc := mulmod(acc, addmod(delta_2, minus_one, p), p)
1425 acc := mulmod(acc, addmod(delta_2, minus_two, p), p)
1426 acc := mulmod(acc, addmod(delta_2, minus_three, p), p)
1427 acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p)
1428 acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p)
1429 mstore(SUBRELATION_EVAL_9_LOC, acc)
1434 acc := mulmod(acc, addmod(delta_3, minus_one, p), p)
1435 acc := mulmod(acc, addmod(delta_3, minus_two, p), p)
1436 acc := mulmod(acc, addmod(delta_3, minus_three, p), p)
1437 acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p)
1438 acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p)
1439 mstore(SUBRELATION_EVAL_10_LOC, acc)
1444 acc := mulmod(acc, addmod(delta_4, minus_one, p), p)
1445 acc := mulmod(acc, addmod(delta_4, minus_two, p), p)
1446 acc := mulmod(acc, addmod(delta_4, minus_three, p), p)
1447 acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p)
1448 acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p)
1449 mstore(SUBRELATION_EVAL_11_LOC, acc)
1453 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1454 /* ELLIPTIC CURVE RELATION */
1455 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1457 // Contribution 10 point addition, x-coordinate check
1458 // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
1459 let x_diff := addmod(mload(EC_X_2), sub(p, mload(EC_X_1)), p)
1460 let y1_sqr := mulmod(mload(EC_Y_1), mload(EC_Y_1), p)
1462 let y2_sqr := mulmod(mload(EC_Y_2), mload(EC_Y_2), p)
1463 let y1y2 := mulmod(mulmod(mload(EC_Y_1), mload(EC_Y_2), p), mload(EC_Q_SIGN), p)
1464 let x_add_identity := addmod(mload(EC_X_3), addmod(mload(EC_X_2), mload(EC_X_1), p), p)
1465 x_add_identity := mulmod(mulmod(x_add_identity, x_diff, p), x_diff, p)
1466 x_add_identity := addmod(x_add_identity, sub(p, y2_sqr), p)
1467 x_add_identity := addmod(x_add_identity, sub(p, y1_sqr), p)
1468 x_add_identity := addmod(x_add_identity, y1y2, p)
1469 x_add_identity := addmod(x_add_identity, y1y2, p)
1471 let eval := mulmod(x_add_identity, mload(POW_PARTIAL_EVALUATION_LOC), p)
1472 eval := mulmod(eval, mload(QELLIPTIC_EVAL_LOC), p)
1473 eval := mulmod(eval, addmod(1, sub(p, mload(EC_Q_IS_DOUBLE)), p), p)
1474 mstore(SUBRELATION_EVAL_12_LOC, eval)
1478 let y1_plus_y3 := addmod(mload(EC_Y_1), mload(EC_Y_3), p)
1479 let y_diff := mulmod(mload(EC_Y_2), mload(EC_Q_SIGN), p)
1480 y_diff := addmod(y_diff, sub(p, mload(EC_Y_1)), p)
1481 let y_add_identity := mulmod(y1_plus_y3, x_diff, p)
1482 y_add_identity := addmod(
1484 mulmod(addmod(mload(EC_X_3), sub(p, mload(EC_X_1)), p), y_diff, p),
1488 let eval := mulmod(y_add_identity, mload(POW_PARTIAL_EVALUATION_LOC), p)
1489 eval := mulmod(eval, mload(QELLIPTIC_EVAL_LOC), p)
1490 eval := mulmod(eval, addmod(1, sub(p, mload(EC_Q_IS_DOUBLE)), p), p)
1491 mstore(SUBRELATION_EVAL_13_LOC, eval)
1495 let x_pow_4 := mulmod(addmod(y1_sqr, GRUMPKIN_CURVE_B_PARAMETER_NEGATED, p), mload(EC_X_1), p)
1496 let y1_sqr_mul_4 := addmod(y1_sqr, y1_sqr, p)
1497 y1_sqr_mul_4 := addmod(y1_sqr_mul_4, y1_sqr_mul_4, p)
1499 let x1_pow_4_mul_9 := mulmod(x_pow_4, 9, p)
1501 let ep_x_double_identity := addmod(mload(EC_X_3), addmod(mload(EC_X_1), mload(EC_X_1), p), p)
1502 ep_x_double_identity := mulmod(ep_x_double_identity, y1_sqr_mul_4, p)
1503 ep_x_double_identity := addmod(ep_x_double_identity, sub(p, x1_pow_4_mul_9), p)
1505 let acc := mulmod(ep_x_double_identity, mload(POW_PARTIAL_EVALUATION_LOC), p)
1506 acc := mulmod(mulmod(acc, mload(QELLIPTIC_EVAL_LOC), p), mload(EC_Q_IS_DOUBLE), p)
1507 acc := addmod(acc, mload(SUBRELATION_EVAL_12_LOC), p)
1509 // Add to existing contribution
1510 mstore(SUBRELATION_EVAL_12_LOC, acc)
1515 mulmod(addmod(addmod(mload(EC_X_1), mload(EC_X_1), p), mload(EC_X_1), p), mload(EC_X_1), p)
1516 let y_double_identity :=
1517 mulmod(x1_sqr_mul_3, addmod(mload(EC_X_1), sub(p, mload(EC_X_3)), p), p)
1518 y_double_identity := addmod(
1523 addmod(mload(EC_Y_1), mload(EC_Y_1), p),
1524 addmod(mload(EC_Y_1), mload(EC_Y_3), p),
1531 let acc := mulmod(y_double_identity, mload(POW_PARTIAL_EVALUATION_LOC), p)
1532 acc := mulmod(mulmod(acc, mload(QELLIPTIC_EVAL_LOC), p), mload(EC_Q_IS_DOUBLE), p)
1533 acc := addmod(acc, mload(SUBRELATION_EVAL_13_LOC), p)
1535 // Add to existing contribution
1536 mstore(SUBRELATION_EVAL_13_LOC, acc)
1540 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1541 /* MEMORY RELATION */
1542 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1596 let memory_record_check := mulmod(mload(W3_EVAL_LOC), mload(ETA_THREE_CHALLENGE), p)
1597 memory_record_check := addmod(
1598 memory_record_check,
1599 mulmod(mload(W2_EVAL_LOC), mload(ETA_TWO_CHALLENGE), p),
1602 memory_record_check := addmod(
1603 memory_record_check,
1604 mulmod(mload(W1_EVAL_LOC), mload(ETA_CHALLENGE), p),
1607 memory_record_check := addmod(memory_record_check, mload(QC_EVAL_LOC), p)
1609 let partial_record_check := memory_record_check
1610 memory_record_check := addmod(memory_record_check, sub(p, mload(W4_EVAL_LOC)), p)
1612 mstore(AUX_MEMORY_CHECK_IDENTITY, memory_record_check)
1629 // index_delta = w_1_omega - w_1
1630 let index_delta := addmod(mload(W1_SHIFT_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p)
1632 // record_delta = w_4_omega - w_4
1633 let record_delta := addmod(mload(W4_SHIFT_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p)
1635 // index_is_monotonically_increasing = index_delta * (index_delta - 1)
1636 let index_is_monotonically_increasing := mulmod(index_delta, addmod(index_delta, P_SUB_1, p), p)
1638 // adjacent_values_match_if_adjacent_indices_match = record_delta * (1 - index_delta)
1639 let adjacent_values_match_if_adjacent_indices_match :=
1640 mulmod(record_delta, addmod(1, sub(p, index_delta), p), p)
1643 SUBRELATION_EVAL_15_LOC,
1645 adjacent_values_match_if_adjacent_indices_match,
1650 mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
1659 // ROM_CONSISTENCY_CHECK_2
1661 SUBRELATION_EVAL_16_LOC,
1663 index_is_monotonically_increasing,
1668 mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
1678 AUX_ROM_CONSISTENCY_CHECK_IDENTITY,
1679 mulmod(memory_record_check, mulmod(mload(QL_EVAL_LOC), mload(QR_EVAL_LOC), p), p)
1709 let next_gate_access_type := mulmod(mload(W3_SHIFT_EVAL_LOC), mload(ETA_THREE_CHALLENGE), p)
1710 next_gate_access_type := addmod(
1711 next_gate_access_type,
1712 mulmod(mload(W2_SHIFT_EVAL_LOC), mload(ETA_TWO_CHALLENGE), p),
1715 next_gate_access_type := addmod(
1716 next_gate_access_type,
1717 mulmod(mload(W1_SHIFT_EVAL_LOC), mload(ETA_CHALLENGE), p),
1720 next_gate_access_type := addmod(mload(W4_SHIFT_EVAL_LOC), sub(p, next_gate_access_type), p)
1722 // value_delta = w_3_omega - w_3
1723 let value_delta := addmod(mload(W3_SHIFT_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p)
1724 // adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = (1 - index_delta) * value_delta * (1 - next_gate_access_type);
1726 let adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation :=
1728 addmod(1, sub(p, index_delta), p),
1729 mulmod(value_delta, addmod(1, sub(p, next_gate_access_type), p), p),
1733 // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the
1734 // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't
1735 // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access
1736 // type is correct, to cover this edge case
1743 let access_type := addmod(mload(W4_EVAL_LOC), sub(p, partial_record_check), p)
1744 let access_check := mulmod(access_type, addmod(access_type, P_SUB_1, p), p)
1745 let next_gate_access_type_is_boolean :=
1746 mulmod(next_gate_access_type, addmod(next_gate_access_type, P_SUB_1, p), p)
1748 // scaled_activation_selector = q_arith * q_aux * alpha
1749 let scaled_activation_selector :=
1752 mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
1757 SUBRELATION_EVAL_17_LOC,
1759 adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation,
1760 scaled_activation_selector,
1766 SUBRELATION_EVAL_18_LOC,
1767 mulmod(index_is_monotonically_increasing, scaled_activation_selector, p)
1771 SUBRELATION_EVAL_19_LOC,
1772 mulmod(next_gate_access_type_is_boolean, scaled_activation_selector, p)
1775 mstore(AUX_RAM_CONSISTENCY_CHECK_IDENTITY, mulmod(access_check, mload(QO_EVAL_LOC), p))
1779 // timestamp_delta = w_2_omega - w_2
1780 let timestamp_delta := addmod(mload(W2_SHIFT_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p)
1782 // RAM_timestamp_check_identity = (1 - index_delta) * timestamp_delta - w_3
1783 let RAM_TIMESTAMP_CHECK_IDENTITY :=
1785 mulmod(timestamp_delta, addmod(1, sub(p, index_delta), p), p),
1786 sub(p, mload(W3_EVAL_LOC)),
1801 let memory_identity := mload(AUX_ROM_CONSISTENCY_CHECK_IDENTITY)
1802 memory_identity := addmod(
1805 RAM_TIMESTAMP_CHECK_IDENTITY,
1806 mulmod(mload(Q4_EVAL_LOC), mload(QL_EVAL_LOC), p),
1812 memory_identity := addmod(
1815 mload(AUX_MEMORY_CHECK_IDENTITY),
1816 mulmod(mload(QM_EVAL_LOC), mload(QL_EVAL_LOC), p),
1821 memory_identity := addmod(memory_identity, mload(AUX_RAM_CONSISTENCY_CHECK_IDENTITY), p)
1823 memory_identity := mulmod(
1825 mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
1828 mstore(SUBRELATION_EVAL_14_LOC, memory_identity)
1833 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1834 /* ROM LOGUP RELATION */
1835 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1837 // Single-value ROM tables. Wire layout: (w_1, w_2, w_3, w_4) = (index, value, multiplicity,
1838 // inverse), q_c = array id. q_logup_table = q_2 * (1 - q_1), q_logup_read = q_4 * (1 - q_1).
1839 let one_minus_q1 := addmod(1, sub(p, mload(QL_EVAL_LOC)), p)
1840 let q_logup_table := mulmod(mload(QR_EVAL_LOC), one_minus_q1, p)
1841 let q_logup_read := mulmod(mload(Q4_EVAL_LOC), one_minus_q1, p)
1843 // denom = rom_logup_gamma + w_1 + eta * w_2 + eta_two * q_c
1844 let denom := mload(ROM_LOGUP_GAMMA_CHALLENGE)
1845 denom := addmod(denom, mload(W1_EVAL_LOC), p)
1846 denom := addmod(denom, mulmod(mload(ETA_CHALLENGE), mload(W2_EVAL_LOC), p), p)
1847 denom := addmod(denom, mulmod(mload(ETA_TWO_CHALLENGE), mload(QC_EVAL_LOC), p), p)
1849 // Inverse correctness: q_logup_any * (w_4 * denom - 1) * q_memory
1850 let inverse_correctness := addmod(mulmod(mload(W4_EVAL_LOC), denom, p), sub(p, 1), p)
1851 inverse_correctness := mulmod(addmod(q_logup_table, q_logup_read, p), inverse_correctness, p)
1852 inverse_correctness :=
1854 inverse_correctness,
1855 mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
1858 mstore(SUBRELATION_EVAL_20_LOC, inverse_correctness)
1860 // LogUp sum identity. Linearly dependent, so not scaled by the pow evaluation.
1861 let logup_sum := addmod(q_logup_read, sub(p, mulmod(q_logup_table, mload(W3_EVAL_LOC), p)), p)
1862 logup_sum := mulmod(logup_sum, mload(W4_EVAL_LOC), p)
1863 logup_sum := mulmod(logup_sum, mload(QMEMORY_EVAL_LOC), p)
1864 mstore(SUBRELATION_EVAL_21_LOC, logup_sum)
1867 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1868 /* NON NATIVE FIELD RELATION */
1869 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
1890 let limb_subproduct :=
1892 mulmod(mload(W1_EVAL_LOC), mload(W2_SHIFT_EVAL_LOC), p),
1893 mulmod(mload(W1_SHIFT_EVAL_LOC), mload(W2_EVAL_LOC), p),
1897 let non_native_field_gate_2 :=
1900 mulmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p),
1901 mulmod(mload(W2_EVAL_LOC), mload(W3_EVAL_LOC), p),
1904 sub(p, mload(W3_SHIFT_EVAL_LOC)),
1907 non_native_field_gate_2 := mulmod(non_native_field_gate_2, LIMB_SIZE, p)
1908 non_native_field_gate_2 := addmod(non_native_field_gate_2, sub(p, mload(W4_SHIFT_EVAL_LOC)), p)
1909 non_native_field_gate_2 := addmod(non_native_field_gate_2, limb_subproduct, p)
1910 non_native_field_gate_2 := mulmod(non_native_field_gate_2, mload(Q4_EVAL_LOC), p)
1912 limb_subproduct := mulmod(limb_subproduct, LIMB_SIZE, p)
1913 limb_subproduct := addmod(
1915 mulmod(mload(W1_SHIFT_EVAL_LOC), mload(W2_SHIFT_EVAL_LOC), p),
1919 let non_native_field_gate_1 :=
1921 addmod(limb_subproduct, sub(p, addmod(mload(W3_EVAL_LOC), mload(W4_EVAL_LOC), p)), p),
1926 let non_native_field_gate_3 :=
1929 addmod(limb_subproduct, mload(W4_EVAL_LOC), p),
1930 sub(p, addmod(mload(W3_SHIFT_EVAL_LOC), mload(W4_SHIFT_EVAL_LOC), p)),
1936 let non_native_field_identity :=
1939 addmod(non_native_field_gate_1, non_native_field_gate_2, p),
1940 non_native_field_gate_3,
1947 mstore(AUX_NON_NATIVE_FIELD_IDENTITY, non_native_field_identity)
1964 let limb_accumulator_1 := mulmod(mload(W2_SHIFT_EVAL_LOC), SUBLIMB_SHIFT, p)
1965 limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_SHIFT_EVAL_LOC), p)
1966 limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
1967 limb_accumulator_1 := addmod(limb_accumulator_1, mload(W3_EVAL_LOC), p)
1968 limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
1969 limb_accumulator_1 := addmod(limb_accumulator_1, mload(W2_EVAL_LOC), p)
1970 limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
1971 limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_EVAL_LOC), p)
1972 limb_accumulator_1 := addmod(limb_accumulator_1, sub(p, mload(W4_EVAL_LOC)), p)
1973 limb_accumulator_1 := mulmod(limb_accumulator_1, mload(Q4_EVAL_LOC), p)
1988 let limb_accumulator_2 := mulmod(mload(W3_SHIFT_EVAL_LOC), SUBLIMB_SHIFT, p)
1989 limb_accumulator_2 := addmod(limb_accumulator_2, mload(W2_SHIFT_EVAL_LOC), p)
1990 limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
1991 limb_accumulator_2 := addmod(limb_accumulator_2, mload(W1_SHIFT_EVAL_LOC), p)
1992 limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
1993 limb_accumulator_2 := addmod(limb_accumulator_2, mload(W4_EVAL_LOC), p)
1994 limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
1995 limb_accumulator_2 := addmod(limb_accumulator_2, mload(W3_EVAL_LOC), p)
1996 limb_accumulator_2 := addmod(limb_accumulator_2, sub(p, mload(W4_SHIFT_EVAL_LOC)), p)
1997 limb_accumulator_2 := mulmod(limb_accumulator_2, mload(QM_EVAL_LOC), p)
1999 let limb_accumulator_identity := addmod(limb_accumulator_1, limb_accumulator_2, p)
2000 limb_accumulator_identity := mulmod(limb_accumulator_identity, mload(QO_EVAL_LOC), p)
2002 let nnf_identity := addmod(mload(AUX_NON_NATIVE_FIELD_IDENTITY), limb_accumulator_identity, p)
2003 nnf_identity := mulmod(
2005 mulmod(mload(QNNF_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p),
2009 mstore(SUBRELATION_EVAL_22_LOC, nnf_identity)
2013 * Poseidon External Relation
2016 let s1 := addmod(mload(W1_EVAL_LOC), mload(QL_EVAL_LOC), p)
2017 let s2 := addmod(mload(W2_EVAL_LOC), mload(QR_EVAL_LOC), p)
2018 let s3 := addmod(mload(W3_EVAL_LOC), mload(QO_EVAL_LOC), p)
2019 let s4 := addmod(mload(W4_EVAL_LOC), mload(Q4_EVAL_LOC), p)
2021 // u1 := s1 * s1 * s1 * s1 * s1;
2022 let t0 := mulmod(s1, s1, p)
2023 let u1 := mulmod(t0, mulmod(t0, s1, p), p)
2025 // u2 := s2 * s2 * s2 * s2 * s2;
2026 t0 := mulmod(s2, s2, p)
2027 let u2 := mulmod(t0, mulmod(t0, s2, p), p)
2029 // u3 := s3 * s3 * s3 * s3 * s3;
2030 t0 := mulmod(s3, s3, p)
2031 let u3 := mulmod(t0, mulmod(t0, s3, p), p)
2033 // u4 := s4 * s4 * s4 * s4 * s4;
2034 t0 := mulmod(s4, s4, p)
2035 let u4 := mulmod(t0, mulmod(t0, s4, p), p)
2037 // matrix mul v = M_E * u with 14 additions
2038 t0 := addmod(u1, u2, p)
2039 let t1 := addmod(u3, u4, p)
2041 let t2 := addmod(u2, u2, p)
2042 t2 := addmod(t2, t1, p)
2044 let t3 := addmod(u4, u4, p)
2045 t3 := addmod(t3, t0, p)
2047 let v4 := addmod(t1, t1, p)
2048 v4 := addmod(v4, v4, p)
2049 v4 := addmod(v4, t3, p)
2051 let v2 := addmod(t0, t0, p)
2052 v2 := addmod(v2, v2, p)
2053 v2 := addmod(v2, t2, p)
2055 let v1 := addmod(t3, v2, p)
2056 let v3 := addmod(t2, v4, p)
2058 let q_pos_by_scaling :=
2059 mulmod(mload(QPOSEIDON2_EXTERNAL_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p)
2062 SUBRELATION_EVAL_23_LOC,
2063 mulmod(q_pos_by_scaling, addmod(v1, sub(p, mload(W1_SHIFT_EVAL_LOC)), p), p)
2067 SUBRELATION_EVAL_24_LOC,
2068 mulmod(q_pos_by_scaling, addmod(v2, sub(p, mload(W2_SHIFT_EVAL_LOC)), p), p)
2072 SUBRELATION_EVAL_25_LOC,
2073 mulmod(q_pos_by_scaling, addmod(v3, sub(p, mload(W3_SHIFT_EVAL_LOC)), p), p)
2077 SUBRELATION_EVAL_26_LOC,
2078 mulmod(q_pos_by_scaling, addmod(v4, sub(p, mload(W4_SHIFT_EVAL_LOC)), p), p)
2083 * Poseidon Internal Relation
2086 let s1 := addmod(mload(W1_EVAL_LOC), mload(QL_EVAL_LOC), p)
2088 // apply s-box round
2089 let t0 := mulmod(s1, s1, p)
2090 let u1 := mulmod(t0, mulmod(t0, s1, p), p)
2091 let u2 := mload(W2_EVAL_LOC)
2092 let u3 := mload(W3_EVAL_LOC)
2093 let u4 := mload(W4_EVAL_LOC)
2095 // matrix mul v = M_I * u 4 muls and 7 additions
2096 let u_sum := addmod(u1, u2, p)
2097 u_sum := addmod(u_sum, addmod(u3, u4, p), p)
2099 let q_pos_by_scaling :=
2100 mulmod(mload(QPOSEIDON2_INTERNAL_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p)
2102 let v1 := addmod(mulmod(u1, POS_INTERNAL_MATRIX_D_0, p), u_sum, p)
2105 SUBRELATION_EVAL_27_LOC,
2106 mulmod(q_pos_by_scaling, addmod(v1, sub(p, mload(W1_SHIFT_EVAL_LOC)), p), p)
2108 let v2 := addmod(mulmod(u2, POS_INTERNAL_MATRIX_D_1, p), u_sum, p)
2111 SUBRELATION_EVAL_28_LOC,
2112 mulmod(q_pos_by_scaling, addmod(v2, sub(p, mload(W2_SHIFT_EVAL_LOC)), p), p)
2114 let v3 := addmod(mulmod(u3, POS_INTERNAL_MATRIX_D_2, p), u_sum, p)
2117 SUBRELATION_EVAL_29_LOC,
2118 mulmod(q_pos_by_scaling, addmod(v3, sub(p, mload(W3_SHIFT_EVAL_LOC)), p), p)
2121 let v4 := addmod(mulmod(u4, POS_INTERNAL_MATRIX_D_3, p), u_sum, p)
2123 SUBRELATION_EVAL_30_LOC,
2124 mulmod(q_pos_by_scaling, addmod(v4, sub(p, mload(W4_SHIFT_EVAL_LOC)), p), p)
2128 // Scale and batch subrelations by subrelation challenges
2129 // linear combination of subrelations
2130 let accumulator := mload(SUBRELATION_EVAL_0_LOC)
2132 // Below is an unrolled variant of the following loop
2133 // for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) {
2134 // accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1];
2137 accumulator := addmod(
2139 mulmod(mload(SUBRELATION_EVAL_1_LOC), mload(ALPHA_CHALLENGE_0), p),
2142 accumulator := addmod(
2144 mulmod(mload(SUBRELATION_EVAL_2_LOC), mload(ALPHA_CHALLENGE_1), p),
2147 accumulator := addmod(
2149 mulmod(mload(SUBRELATION_EVAL_3_LOC), mload(ALPHA_CHALLENGE_2), p),
2152 accumulator := addmod(
2154 mulmod(mload(SUBRELATION_EVAL_4_LOC), mload(ALPHA_CHALLENGE_3), p),
2157 accumulator := addmod(
2159 mulmod(mload(SUBRELATION_EVAL_5_LOC), mload(ALPHA_CHALLENGE_4), p),
2162 accumulator := addmod(
2164 mulmod(mload(SUBRELATION_EVAL_6_LOC), mload(ALPHA_CHALLENGE_5), p),
2167 accumulator := addmod(
2169 mulmod(mload(SUBRELATION_EVAL_7_LOC), mload(ALPHA_CHALLENGE_6), p),
2172 accumulator := addmod(
2174 mulmod(mload(SUBRELATION_EVAL_8_LOC), mload(ALPHA_CHALLENGE_7), p),
2177 accumulator := addmod(
2179 mulmod(mload(SUBRELATION_EVAL_9_LOC), mload(ALPHA_CHALLENGE_8), p),
2182 accumulator := addmod(
2184 mulmod(mload(SUBRELATION_EVAL_10_LOC), mload(ALPHA_CHALLENGE_9), p),
2187 accumulator := addmod(
2189 mulmod(mload(SUBRELATION_EVAL_11_LOC), mload(ALPHA_CHALLENGE_10), p),
2192 accumulator := addmod(
2194 mulmod(mload(SUBRELATION_EVAL_12_LOC), mload(ALPHA_CHALLENGE_11), p),
2197 accumulator := addmod(
2199 mulmod(mload(SUBRELATION_EVAL_13_LOC), mload(ALPHA_CHALLENGE_12), p),
2202 accumulator := addmod(
2204 mulmod(mload(SUBRELATION_EVAL_14_LOC), mload(ALPHA_CHALLENGE_13), p),
2207 accumulator := addmod(
2209 mulmod(mload(SUBRELATION_EVAL_15_LOC), mload(ALPHA_CHALLENGE_14), p),
2212 accumulator := addmod(
2214 mulmod(mload(SUBRELATION_EVAL_16_LOC), mload(ALPHA_CHALLENGE_15), p),
2217 accumulator := addmod(
2219 mulmod(mload(SUBRELATION_EVAL_17_LOC), mload(ALPHA_CHALLENGE_16), p),
2222 accumulator := addmod(
2224 mulmod(mload(SUBRELATION_EVAL_18_LOC), mload(ALPHA_CHALLENGE_17), p),
2227 accumulator := addmod(
2229 mulmod(mload(SUBRELATION_EVAL_19_LOC), mload(ALPHA_CHALLENGE_18), p),
2232 accumulator := addmod(
2234 mulmod(mload(SUBRELATION_EVAL_20_LOC), mload(ALPHA_CHALLENGE_19), p),
2237 accumulator := addmod(
2239 mulmod(mload(SUBRELATION_EVAL_21_LOC), mload(ALPHA_CHALLENGE_20), p),
2242 accumulator := addmod(
2244 mulmod(mload(SUBRELATION_EVAL_22_LOC), mload(ALPHA_CHALLENGE_21), p),
2247 accumulator := addmod(
2249 mulmod(mload(SUBRELATION_EVAL_23_LOC), mload(ALPHA_CHALLENGE_22), p),
2252 accumulator := addmod(
2254 mulmod(mload(SUBRELATION_EVAL_24_LOC), mload(ALPHA_CHALLENGE_23), p),
2257 accumulator := addmod(
2259 mulmod(mload(SUBRELATION_EVAL_25_LOC), mload(ALPHA_CHALLENGE_24), p),
2262 accumulator := addmod(
2264 mulmod(mload(SUBRELATION_EVAL_26_LOC), mload(ALPHA_CHALLENGE_25), p),
2267 accumulator := addmod(
2269 mulmod(mload(SUBRELATION_EVAL_27_LOC), mload(ALPHA_CHALLENGE_26), p),
2272 accumulator := addmod(
2274 mulmod(mload(SUBRELATION_EVAL_28_LOC), mload(ALPHA_CHALLENGE_27), p),
2277 accumulator := addmod(
2279 mulmod(mload(SUBRELATION_EVAL_29_LOC), mload(ALPHA_CHALLENGE_28), p),
2282 accumulator := addmod(
2284 mulmod(mload(SUBRELATION_EVAL_30_LOC), mload(ALPHA_CHALLENGE_29), p),
2288 // ZK final check: grandHonkRelationSum * (1 - evaluation) + libraEvaluation * libraChallenge == roundTargetSum
2289 // where evaluation = product(u[2] * u[3] * ... * u[LOG_N - 1])
2291 // Row-disabling polynomial: 1 - ∏_{i≥2}(1 - u_i)
2293 let u_off := SUM_U_CHALLENGE_2
2294 for { let i := 2 } lt(i, LOG_N) { i := add(i, 1) } {
2295 // evaluation = evaluation * (1 - sumCheckUChallenges[i])
2296 let one_minus_u := addmod(1, sub(p, mload(u_off)), p)
2297 evaluation := mulmod(evaluation, one_minus_u, p)
2298 u_off := add(u_off, 0x20)
2301 // adjustedSum = accumulator * (1 - evaluation) + libraEvaluation * libraChallenge
2302 let one_minus_eval := addmod(1, sub(p, evaluation), p)
2303 let adjusted_sum := addmod(
2304 mulmod(accumulator, one_minus_eval, p),
2305 mulmod(mload(LIBRA_EVALUATION_LOC), mload(LIBRA_CHALLENGE), p),
2309 let sumcheck_valid := eq(adjusted_sum, mload(FINAL_ROUND_TARGET_LOC))
2311 if iszero(sumcheck_valid) {
2312 mstore(0x00, SUMCHECK_FAILED_SELECTOR)
2318 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
2319 /* SUMCHECK -- Complete */
2320 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
2322 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
2324 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
2326 // ============= SHPLEMINI INVERSES ==============
2327 // Inverses are already at their designated addresses from batch inversion.
2328 let unshifted_scalar := 0
2329 let shifted_scalar := 0
2331 let gemini_r_inv := mload(GEMINI_R_INV_LOC)
2333 // Compute unshifted_scalar and shifted_scalar using the copied inverses
2334 let pos_inverted_denominator := mload(POS_INVERTED_DENOM_0_LOC)
2335 let neg_inverted_denominator := mload(NEG_INVERTED_DENOM_0_LOC)
2336 let shplonk_nu := mload(SHPLONK_NU_CHALLENGE)
2338 unshifted_scalar := addmod(pos_inverted_denominator, mulmod(shplonk_nu, neg_inverted_denominator, p), p)
2340 shifted_scalar := mulmod(
2341 gemini_r_inv, // (1 / gemini_r_challenge)
2342 // (inverse_vanishing_evals[0]) - (shplonk_nu * inverse_vanishing_evals[1])
2344 pos_inverted_denominator,
2345 // - (shplonk_nu * inverse_vanishing_evals[1])
2346 sub(p, mulmod(shplonk_nu, neg_inverted_denominator, p)),
2353 // Commitment Accumulation (MSM via sequential ecAdd/ecMul):
2354 // For each commitment C_i with batch scalar s_i, we compute:
2355 // accumulator += s_i * C_i
2356 // The commitments include: shplonk_Q, gemini_masking (ZK), VK points,
2357 // wire commitments, lookup commitments, Z_PERM, libra (ZK),
2358 // gemini fold univariates. The KZG quotient is handled separately.
2359 // The final accumulator is the LHS of the pairing equation.
2362 let batching_challenge := 1
2363 let batched_evaluation := 0
2365 let neg_unshifted_scalar := sub(p, unshifted_scalar)
2366 let neg_shifted_scalar := sub(p, shifted_scalar)
2368 let rho := mload(RHO_CHALLENGE)
2370 // Unrolled for the loop below - where NUMBER_UNSHIFTED = 37 (ZK: includes gemini_masking_poly)
2371 // For ZK: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...]
2372 // for (uint256 i = 1; i <= NUMBER_UNSHIFTED; ++i) {
2373 // scalars[i] = mem.unshiftedScalar.neg() * mem.batchingChallenge;
2374 // mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge);
2375 // mem.batchingChallenge = mem.batchingChallenge * tp.rho;
2378 // Calculate the scalars and batching challenge for the unshifted entities.
2379 // Iteration order matches UltraFlavor_Generated::EntityId. Scalar slot N+1 pairs with vk[N].
2380 // 0: GEMINI_MASKING_EVAL_LOC
2381 mstore(BATCH_SCALAR_1_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2382 batched_evaluation := addmod(batched_evaluation, mulmod(mload(GEMINI_MASKING_EVAL_LOC), batching_challenge, p), p)
2383 batching_challenge := mulmod(batching_challenge, rho, p)
2385 // 1: SIGMA1_EVAL_LOC
2386 mstore(BATCH_SCALAR_2_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2387 batched_evaluation := addmod(batched_evaluation, mulmod(mload(SIGMA1_EVAL_LOC), batching_challenge, p), p)
2388 batching_challenge := mulmod(batching_challenge, rho, p)
2390 // 2: SIGMA2_EVAL_LOC
2391 mstore(BATCH_SCALAR_3_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2392 batched_evaluation := addmod(batched_evaluation, mulmod(mload(SIGMA2_EVAL_LOC), batching_challenge, p), p)
2393 batching_challenge := mulmod(batching_challenge, rho, p)
2395 // 3: SIGMA3_EVAL_LOC
2396 mstore(BATCH_SCALAR_4_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2397 batched_evaluation := addmod(batched_evaluation, mulmod(mload(SIGMA3_EVAL_LOC), batching_challenge, p), p)
2398 batching_challenge := mulmod(batching_challenge, rho, p)
2400 // 4: SIGMA4_EVAL_LOC
2401 mstore(BATCH_SCALAR_5_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2402 batched_evaluation := addmod(batched_evaluation, mulmod(mload(SIGMA4_EVAL_LOC), batching_challenge, p), p)
2403 batching_challenge := mulmod(batching_challenge, rho, p)
2406 mstore(BATCH_SCALAR_6_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2407 batched_evaluation := addmod(batched_evaluation, mulmod(mload(ID1_EVAL_LOC), batching_challenge, p), p)
2408 batching_challenge := mulmod(batching_challenge, rho, p)
2411 mstore(BATCH_SCALAR_7_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2412 batched_evaluation := addmod(batched_evaluation, mulmod(mload(ID2_EVAL_LOC), batching_challenge, p), p)
2413 batching_challenge := mulmod(batching_challenge, rho, p)
2416 mstore(BATCH_SCALAR_8_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2417 batched_evaluation := addmod(batched_evaluation, mulmod(mload(ID3_EVAL_LOC), batching_challenge, p), p)
2418 batching_challenge := mulmod(batching_challenge, rho, p)
2421 mstore(BATCH_SCALAR_9_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2422 batched_evaluation := addmod(batched_evaluation, mulmod(mload(ID4_EVAL_LOC), batching_challenge, p), p)
2423 batching_challenge := mulmod(batching_challenge, rho, p)
2425 // 9: LAGRANGE_FIRST_EVAL_LOC
2426 mstore(BATCH_SCALAR_10_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2427 batched_evaluation := addmod(batched_evaluation, mulmod(mload(LAGRANGE_FIRST_EVAL_LOC), batching_challenge, p), p)
2428 batching_challenge := mulmod(batching_challenge, rho, p)
2430 // 10: LAGRANGE_LAST_EVAL_LOC
2431 mstore(BATCH_SCALAR_11_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2432 batched_evaluation := addmod(batched_evaluation, mulmod(mload(LAGRANGE_LAST_EVAL_LOC), batching_challenge, p), p)
2433 batching_challenge := mulmod(batching_challenge, rho, p)
2435 // 11: QLOOKUP_EVAL_LOC
2436 mstore(BATCH_SCALAR_12_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2437 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QLOOKUP_EVAL_LOC), batching_challenge, p), p)
2438 batching_challenge := mulmod(batching_challenge, rho, p)
2440 // 12: TABLE1_EVAL_LOC
2441 mstore(BATCH_SCALAR_13_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2442 batched_evaluation := addmod(batched_evaluation, mulmod(mload(TABLE1_EVAL_LOC), batching_challenge, p), p)
2443 batching_challenge := mulmod(batching_challenge, rho, p)
2445 // 13: TABLE2_EVAL_LOC
2446 mstore(BATCH_SCALAR_14_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2447 batched_evaluation := addmod(batched_evaluation, mulmod(mload(TABLE2_EVAL_LOC), batching_challenge, p), p)
2448 batching_challenge := mulmod(batching_challenge, rho, p)
2450 // 14: TABLE3_EVAL_LOC
2451 mstore(BATCH_SCALAR_15_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2452 batched_evaluation := addmod(batched_evaluation, mulmod(mload(TABLE3_EVAL_LOC), batching_challenge, p), p)
2453 batching_challenge := mulmod(batching_challenge, rho, p)
2455 // 15: TABLE4_EVAL_LOC
2456 mstore(BATCH_SCALAR_16_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2457 batched_evaluation := addmod(batched_evaluation, mulmod(mload(TABLE4_EVAL_LOC), batching_challenge, p), p)
2458 batching_challenge := mulmod(batching_challenge, rho, p)
2461 mstore(BATCH_SCALAR_17_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2462 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QM_EVAL_LOC), batching_challenge, p), p)
2463 batching_challenge := mulmod(batching_challenge, rho, p)
2466 mstore(BATCH_SCALAR_18_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2467 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QR_EVAL_LOC), batching_challenge, p), p)
2468 batching_challenge := mulmod(batching_challenge, rho, p)
2471 mstore(BATCH_SCALAR_19_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2472 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QO_EVAL_LOC), batching_challenge, p), p)
2473 batching_challenge := mulmod(batching_challenge, rho, p)
2476 mstore(BATCH_SCALAR_20_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2477 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QC_EVAL_LOC), batching_challenge, p), p)
2478 batching_challenge := mulmod(batching_challenge, rho, p)
2481 mstore(BATCH_SCALAR_21_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2482 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QL_EVAL_LOC), batching_challenge, p), p)
2483 batching_challenge := mulmod(batching_challenge, rho, p)
2486 mstore(BATCH_SCALAR_22_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2487 batched_evaluation := addmod(batched_evaluation, mulmod(mload(Q4_EVAL_LOC), batching_challenge, p), p)
2488 batching_challenge := mulmod(batching_challenge, rho, p)
2490 // 22: QARITH_EVAL_LOC
2491 mstore(BATCH_SCALAR_23_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2492 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QARITH_EVAL_LOC), batching_challenge, p), p)
2493 batching_challenge := mulmod(batching_challenge, rho, p)
2495 // 23: QRANGE_EVAL_LOC
2496 mstore(BATCH_SCALAR_24_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2497 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QRANGE_EVAL_LOC), batching_challenge, p), p)
2498 batching_challenge := mulmod(batching_challenge, rho, p)
2500 // 24: QELLIPTIC_EVAL_LOC
2501 mstore(BATCH_SCALAR_25_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2502 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QELLIPTIC_EVAL_LOC), batching_challenge, p), p)
2503 batching_challenge := mulmod(batching_challenge, rho, p)
2505 // 25: QMEMORY_EVAL_LOC
2506 mstore(BATCH_SCALAR_26_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2507 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QMEMORY_EVAL_LOC), batching_challenge, p), p)
2508 batching_challenge := mulmod(batching_challenge, rho, p)
2510 // 26: QNNF_EVAL_LOC
2511 mstore(BATCH_SCALAR_27_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2512 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QNNF_EVAL_LOC), batching_challenge, p), p)
2513 batching_challenge := mulmod(batching_challenge, rho, p)
2515 // 27: QPOSEIDON2_EXTERNAL_EVAL_LOC
2516 mstore(BATCH_SCALAR_28_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2517 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QPOSEIDON2_EXTERNAL_EVAL_LOC), batching_challenge, p), p)
2518 batching_challenge := mulmod(batching_challenge, rho, p)
2520 // 28: QPOSEIDON2_INTERNAL_EVAL_LOC
2521 mstore(BATCH_SCALAR_29_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2522 batched_evaluation := addmod(batched_evaluation, mulmod(mload(QPOSEIDON2_INTERNAL_EVAL_LOC), batching_challenge, p), p)
2523 batching_challenge := mulmod(batching_challenge, rho, p)
2526 mstore(BATCH_SCALAR_30_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2527 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W1_EVAL_LOC), batching_challenge, p), p)
2528 batching_challenge := mulmod(batching_challenge, rho, p)
2531 mstore(BATCH_SCALAR_31_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2532 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W2_EVAL_LOC), batching_challenge, p), p)
2533 batching_challenge := mulmod(batching_challenge, rho, p)
2536 mstore(BATCH_SCALAR_32_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2537 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W3_EVAL_LOC), batching_challenge, p), p)
2538 batching_challenge := mulmod(batching_challenge, rho, p)
2541 mstore(BATCH_SCALAR_33_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2542 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W4_EVAL_LOC), batching_challenge, p), p)
2543 batching_challenge := mulmod(batching_challenge, rho, p)
2545 // 33: Z_PERM_EVAL_LOC
2546 mstore(BATCH_SCALAR_34_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2547 batched_evaluation := addmod(batched_evaluation, mulmod(mload(Z_PERM_EVAL_LOC), batching_challenge, p), p)
2548 batching_challenge := mulmod(batching_challenge, rho, p)
2550 // 34: LOOKUP_INVERSES_EVAL_LOC
2551 mstore(BATCH_SCALAR_35_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2552 batched_evaluation := addmod(batched_evaluation, mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), batching_challenge, p), p)
2553 batching_challenge := mulmod(batching_challenge, rho, p)
2555 // 35: LOOKUP_READ_COUNTS_EVAL_LOC
2556 mstore(BATCH_SCALAR_36_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2557 batched_evaluation := addmod(batched_evaluation, mulmod(mload(LOOKUP_READ_COUNTS_EVAL_LOC), batching_challenge, p), p)
2558 batching_challenge := mulmod(batching_challenge, rho, p)
2560 // 36: LOOKUP_READ_TAGS_EVAL_LOC
2561 mstore(BATCH_SCALAR_37_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p))
2562 batched_evaluation := addmod(batched_evaluation, mulmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), batching_challenge, p), p)
2563 batching_challenge := mulmod(batching_challenge, rho, p)
2565 // Unrolled for NUMBER_OF_SHIFTED_ENTITIES = 5
2566 // for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) {
2567 // scalars[i] = mem.shiftedScalar.neg() * mem.batchingChallenge;
2568 // mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - 1] * mem.batchingChallenge);
2569 // mem.batchingChallenge = mem.batchingChallenge * tp.rho;
2572 // Shifted entities: SHIFTED_COMMITMENTS_START = 30
2573 // scalars[scalarOff] += mem.shiftedScalar.neg() * mem.batchingChallenge
2576 BATCH_SCALAR_30_LOC,
2577 addmod(mload(BATCH_SCALAR_30_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p)
2579 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W1_SHIFT_EVAL_LOC), batching_challenge, p), p)
2580 batching_challenge := mulmod(batching_challenge, rho, p)
2584 BATCH_SCALAR_31_LOC,
2585 addmod(mload(BATCH_SCALAR_31_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p)
2587 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W2_SHIFT_EVAL_LOC), batching_challenge, p), p)
2588 batching_challenge := mulmod(batching_challenge, rho, p)
2592 BATCH_SCALAR_32_LOC,
2593 addmod(mload(BATCH_SCALAR_32_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p)
2595 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W3_SHIFT_EVAL_LOC), batching_challenge, p), p)
2596 batching_challenge := mulmod(batching_challenge, rho, p)
2600 BATCH_SCALAR_33_LOC,
2601 addmod(mload(BATCH_SCALAR_33_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p)
2603 batched_evaluation := addmod(batched_evaluation, mulmod(mload(W4_SHIFT_EVAL_LOC), batching_challenge, p), p)
2604 batching_challenge := mulmod(batching_challenge, rho, p)
2606 // 34: Z_PERM (shifted)
2608 BATCH_SCALAR_34_LOC,
2609 addmod(mload(BATCH_SCALAR_34_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p)
2611 batched_evaluation := addmod(
2613 mulmod(mload(Z_PERM_SHIFT_EVAL_LOC), batching_challenge, p),
2616 batching_challenge := mulmod(batching_challenge, rho, p)
2618 // Compute fold pos evaluations
2620 mstore(CHALL_POW_LOC, POWERS_OF_EVALUATION_CHALLENGE_{{ LOG_N_MINUS_ONE }}_LOC)
2621 mstore(SUMCHECK_U_LOC, SUM_U_CHALLENGE_{{ LOG_N_MINUS_ONE }})
2622 mstore(GEMINI_A_LOC, GEMINI_A_EVAL_{{ LOG_N_MINUS_ONE }})
2624 // Inversion of this value was included in batch inversion above
2625 let inverted_chall_pow_minus_u_loc := INVERTED_CHALLENGE_POW_MINUS_U_{{ LOG_N_MINUS_ONE }}_LOC
2626 let fold_pos_off := FOLD_POS_EVALUATIONS_{{ LOG_N_MINUS_ONE }}_LOC
2628 let batchedEvalAcc := batched_evaluation
2629 for { let i := LOG_N } gt(i, 0) { i := sub(i, 1) } {
2630 let chall_pow := mload(mload(CHALL_POW_LOC))
2631 let sum_check_u := mload(mload(SUMCHECK_U_LOC))
2633 // challengePower * batchedEvalAccumulator * 2
2634 let batchedEvalRoundAcc := mulmod(chall_pow, mulmod(batchedEvalAcc, 2, p), p)
2635 // (challengePower * (ONE - u) - u)
2636 let chall_pow_times_1_minus_u := mulmod(chall_pow, addmod(1, sub(p, sum_check_u), p), p)
2638 batchedEvalRoundAcc := addmod(
2639 batchedEvalRoundAcc,
2643 mload(mload(GEMINI_A_LOC)),
2644 addmod(chall_pow_times_1_minus_u, sub(p, sum_check_u), p),
2651 batchedEvalRoundAcc := mulmod(batchedEvalRoundAcc, mload(inverted_chall_pow_minus_u_loc), p)
2653 batchedEvalAcc := batchedEvalRoundAcc
2654 mstore(fold_pos_off, batchedEvalRoundAcc)
2656 mstore(CHALL_POW_LOC, sub(mload(CHALL_POW_LOC), 0x20))
2657 mstore(SUMCHECK_U_LOC, sub(mload(SUMCHECK_U_LOC), 0x20))
2658 mstore(GEMINI_A_LOC, sub(mload(GEMINI_A_LOC), 0x20))
2659 inverted_chall_pow_minus_u_loc := sub(inverted_chall_pow_minus_u_loc, 0x20)
2660 fold_pos_off := sub(fold_pos_off, 0x20)
2664 let constant_term_acc := mulmod(mload(FOLD_POS_EVALUATIONS_0_LOC), mload(POS_INVERTED_DENOM_0_LOC), p)
2666 let shplonk_nu := mload(SHPLONK_NU_CHALLENGE)
2668 constant_term_acc := addmod(
2670 mulmod(mload(GEMINI_A_EVAL_0), mulmod(shplonk_nu, mload(NEG_INVERTED_DENOM_0_LOC), p), p),
2674 let shplonk_nu_sqr := mulmod(shplonk_nu, shplonk_nu, p)
2675 batching_challenge := shplonk_nu_sqr
2677 mstore(SS_POS_INV_DENOM_LOC, POS_INVERTED_DENOM_1_LOC)
2678 mstore(SS_NEG_INV_DENOM_LOC, NEG_INVERTED_DENOM_1_LOC)
2680 mstore(SS_GEMINI_EVALS_LOC, GEMINI_A_EVAL_1)
2681 let fold_pos_evals_loc := FOLD_POS_EVALUATIONS_1_LOC
2683 let scalars_loc := BATCH_SCALAR_38_LOC
2685 for { let i := 0 } lt(i, sub(LOG_N, 1)) { i := add(i, 1) } {
2686 let scaling_factor_pos := mulmod(batching_challenge, mload(mload(SS_POS_INV_DENOM_LOC)), p)
2687 let scaling_factor_neg :=
2688 mulmod(batching_challenge, mulmod(shplonk_nu, mload(mload(SS_NEG_INV_DENOM_LOC)), p), p)
2690 mstore(scalars_loc, addmod(sub(p, scaling_factor_neg), sub(p, scaling_factor_pos), p))
2692 let accum_contribution := mulmod(scaling_factor_neg, mload(mload(SS_GEMINI_EVALS_LOC)), p)
2693 accum_contribution := addmod(
2695 mulmod(scaling_factor_pos, mload(fold_pos_evals_loc), p),
2699 constant_term_acc := addmod(constant_term_acc, accum_contribution, p)
2701 batching_challenge := mulmod(batching_challenge, shplonk_nu_sqr, p)
2703 mstore(SS_POS_INV_DENOM_LOC, add(mload(SS_POS_INV_DENOM_LOC), 0x20))
2704 mstore(SS_NEG_INV_DENOM_LOC, add(mload(SS_NEG_INV_DENOM_LOC), 0x20))
2705 mstore(SS_GEMINI_EVALS_LOC, add(mload(SS_GEMINI_EVALS_LOC), 0x20))
2706 fold_pos_evals_loc := add(fold_pos_evals_loc, 0x20)
2707 scalars_loc := add(scalars_loc, 0x20)
2711 // Libra polynomial batching: five opening claims for (G, A, A, A, Q) at points {r, g*r, r, 1, r}.
2713 let shplonk_nu := mload(SHPLONK_NU_CHALLENGE)
2715 let libra_denom_0 := mload(POS_INVERTED_DENOM_0_LOC) // 1/(z - r)
2716 let libra_denom_1 := mload(LIBRA_SUBGROUP_DENOM_LOC) // 1/(z - g*r)
2718 // 1/(z - 1): boundary denominator, computed inline via the modexp precompile.
2719 let shplonk_z := mload(SHPLONK_Z_CHALLENGE)
2723 mstore(0x60, addmod(shplonk_z, sub(p, 1), p))
2724 mstore(0x80, sub(p, 2))
2726 if iszero(staticcall(gas(), 0x05, 0x00, 0xc0, 0x00, 0x20)) {
2727 mstore(0x00, MODEXP_FAILED_SELECTOR)
2730 let libra_denom_3 := mload(0x00)
2731 if iszero(libra_denom_3) {
2732 mstore(0x00, MODEXP_FAILED_SELECTOR)
2737 let scaling_factor := mulmod(libra_denom_0, batching_challenge, p)
2738 let libra_scalar_0 := sub(p, scaling_factor)
2739 constant_term_acc := addmod(constant_term_acc, mulmod(scaling_factor, mload(LIBRA_POLY_EVAL_0_LOC), p), p)
2740 batching_challenge := mulmod(batching_challenge, shplonk_nu, p)
2743 scaling_factor := mulmod(libra_denom_1, batching_challenge, p)
2744 let libra_scalar_1 := sub(p, scaling_factor)
2745 constant_term_acc := addmod(constant_term_acc, mulmod(scaling_factor, mload(LIBRA_POLY_EVAL_1_LOC), p), p)
2746 batching_challenge := mulmod(batching_challenge, shplonk_nu, p)
2749 scaling_factor := mulmod(libra_denom_0, batching_challenge, p)
2750 let libra_scalar_2 := sub(p, scaling_factor)
2751 constant_term_acc := addmod(constant_term_acc, mulmod(scaling_factor, mload(LIBRA_POLY_EVAL_2_LOC), p), p)
2752 batching_challenge := mulmod(batching_challenge, shplonk_nu, p)
2754 // i=3: A(1), boundary opening with hardcoded value 0.
2755 scaling_factor := mulmod(libra_denom_3, batching_challenge, p)
2756 let libra_scalar_3 := sub(p, scaling_factor)
2757 batching_challenge := mulmod(batching_challenge, shplonk_nu, p)
2760 scaling_factor := mulmod(libra_denom_0, batching_challenge, p)
2761 let libra_scalar_4 := sub(p, scaling_factor)
2762 constant_term_acc := addmod(constant_term_acc, mulmod(scaling_factor, mload(LIBRA_POLY_EVAL_3_LOC), p), p)
2764 // Group scalars by underlying commitment: [G], [A] (sum of three openings), [Q].
2765 mstore(BATCH_SCALAR_{{ LIBRA_BATCH_SCALAR_0 }}_LOC, libra_scalar_0)
2766 mstore(BATCH_SCALAR_{{ LIBRA_BATCH_SCALAR_1 }}_LOC, addmod(addmod(libra_scalar_1, libra_scalar_2, p), libra_scalar_3, p))
2767 mstore(BATCH_SCALAR_{{ LIBRA_BATCH_SCALAR_2 }}_LOC, libra_scalar_4)
2770 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
2771 /* ZK: checkEvalsConsistency */
2772 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
2773 // Validates Libra polynomial evaluations using small subgroup IPA
2775 let gemini_r := mload(GEMINI_R_CHALLENGE)
2777 // Step 1: Compute vanishingPolyEval = geminiR^SUBGROUP_SIZE - 1
2778 // SUBGROUP_SIZE = 256 = 2^8, so 8 squarings instead of modexp precompile
2779 let v := mulmod(gemini_r, gemini_r, p) // r^2
2780 v := mulmod(v, v, p) // r^4
2781 v := mulmod(v, v, p) // r^8
2782 v := mulmod(v, v, p) // r^16
2783 v := mulmod(v, v, p) // r^32
2784 v := mulmod(v, v, p) // r^64
2785 v := mulmod(v, v, p) // r^128
2786 v := mulmod(v, v, p) // r^256
2787 let vanishing_poly_eval := addmod(v, sub(p, 1), p)
2789 // Require vanishingPolyEval != 0 (geminiR not in subgroup)
2790 if iszero(vanishing_poly_eval) {
2791 mstore(0x00, GEMINI_CHALLENGE_IN_SUBGROUP_SELECTOR)
2795 // Step 2: Build challengePolyLagrange[0..255]
2796 // Memory layout: CHALLENGE_POLY_LAGRANGE_BASE + idx * 0x20
2797 // Zero-initialize all 256 entries (only 1 + 9*LOG_N = 136 will be non-zero)
2799 mstore(CHALLENGE_POLY_LAGRANGE_BASE_0, 1) // [0] = 1
2802 let u_loc := SUM_U_CHALLENGE_0
2803 let challenge_base := CHALLENGE_POLY_LAGRANGE_BASE_1
2804 // Upper bound of this loop is LIBRA_UNIVARIATES_LENGTH * LOG_N - this is inserted in code templating depending on LOG_N
2805 for { } lt(challenge_base, CHALLENGE_POLY_LAGRANGE_BASE_{{ NUMBER_OF_LAGRANGE_BASES }}) { } {
2806 let u_round := mload(u_loc)
2809 mstore(challenge_base, 1)
2810 challenge_base := add(challenge_base, 0x20)
2812 // Calc memory offset inner loop should break at
2813 let loop_target := add(challenge_base, mul(0x20, LIBRA_UNIVARIATES_LENGTH_MINUS_ONE))
2815 // [currIdx+1..currIdx+8] = u^1, u^2, ..., u^8
2817 for { } lt(challenge_base, loop_target) { } {
2818 prev_val := mulmod(prev_val, u_round, p)
2819 mstore(challenge_base, prev_val)
2820 challenge_base := add(challenge_base, 0x20)
2823 u_loc := add(u_loc, 0x20)
2827 // Step 3: Compute the active challenge-poly denominators and L_|H|(r)'s denominator.
2829 let challenge_poly_denom_end := add(CONSISTENCY_DENOMINATORS_BASE_{{ NUMBER_OF_LAGRANGE_BASES }}, 0x20)
2831 let consistency_base := CONSISTENCY_DENOMINATORS_BASE_0
2832 for { } lt(consistency_base, challenge_poly_denom_end) { } {
2833 let denom := addmod(mulmod(root_power, gemini_r, p), sub(p, 1), p)
2834 mstore(consistency_base, denom)
2835 root_power := mulmod(root_power, SUBGROUP_GENERATOR_INVERSE, p)
2836 consistency_base := add(consistency_base, 0x20)
2839 challenge_poly_denom_end,
2840 addmod(mulmod(SUBGROUP_GENERATOR, gemini_r, p), sub(p, 1), p)
2844 // Step 4: Batch invert the active denominators plus L_|H|(r)'s denominator.
2846 let final_product_pointer := add(CONSISTENCY_PRODUCTS_BASE_{{ NUMBER_OF_LAGRANGE_BASES }}, 0x20)
2847 let batch_product_end := add(final_product_pointer, 0x20)
2849 // Forward pass: accumulate products
2850 let product_pointer := CONSISTENCY_PRODUCTS_BASE_0
2851 let next_product_pointer := CONSISTENCY_PRODUCTS_BASE_1
2852 let denom_pointer := CONSISTENCY_DENOMINATORS_BASE_1
2853 mstore(CONSISTENCY_PRODUCTS_BASE_0, mload(CONSISTENCY_DENOMINATORS_BASE_0))
2854 for { } lt(next_product_pointer, batch_product_end) { } {
2856 next_product_pointer,
2858 mload(product_pointer),
2859 mload(denom_pointer),
2863 product_pointer := next_product_pointer
2864 next_product_pointer := add(next_product_pointer, 0x20)
2865 denom_pointer := add(denom_pointer, 0x20)
2868 // Invert the final product
2869 let final_prod := mload(final_product_pointer)
2873 mstore(0x60, final_prod)
2874 mstore(0x80, sub(p, 2))
2876 if iszero(staticcall(gas(), 5, 0x00, 0xc0, 0x00, 0x20)) {
2877 mstore(0x00, CONSISTENCY_CHECK_FAILED_SELECTOR)
2880 let accumulator := mload(0x00)
2881 if iszero(accumulator) {
2882 mstore(0x00, MODEXP_FAILED_SELECTOR)
2886 // Backward pass: compute individual inverses
2887 let products_pointer := CONSISTENCY_PRODUCTS_BASE_{{ NUMBER_OF_LAGRANGE_BASES }}
2888 let denoms_pointer := add(CONSISTENCY_DENOMINATORS_BASE_{{ NUMBER_OF_LAGRANGE_BASES }}, 0x20)
2889 for { } gt(denoms_pointer, CONSISTENCY_DENOMINATORS_BASE_0) { } {
2892 mload(products_pointer),
2895 accumulator := mulmod(
2897 mload(denoms_pointer),
2900 mstore(denoms_pointer, val)
2902 products_pointer := sub(products_pointer, 0x20)
2903 denoms_pointer := sub(denoms_pointer, 0x20)
2905 // idx=0: running_inv is the inverse of denom[0]
2906 mstore(CONSISTENCY_DENOMINATORS_BASE_0, accumulator)
2909 // Step 5: Compute challengePolyEval = sum(lagrange[i] * invDenom[i]) * numerator
2910 let challenge_poly_eval := 0
2911 let challenge_poly_lagrange_end := add(CHALLENGE_POLY_LAGRANGE_BASE_{{ NUMBER_OF_LAGRANGE_BASES }}, 0x20)
2912 let lagrange_pointer := CHALLENGE_POLY_LAGRANGE_BASE_0
2913 let denom_pointer := CONSISTENCY_DENOMINATORS_BASE_0
2914 for { } lt(lagrange_pointer, challenge_poly_lagrange_end) { } {
2915 challenge_poly_eval := addmod(
2916 challenge_poly_eval,
2918 mload(lagrange_pointer),
2919 mload(denom_pointer),
2924 lagrange_pointer := add(lagrange_pointer, 0x20)
2925 denom_pointer := add(denom_pointer, 0x20)
2928 // numerator = vanishingPolyEval / SUBGROUP_SIZE
2929 let numerator := mulmod(vanishing_poly_eval, INV_SUBGROUP_SIZE, p)
2930 challenge_poly_eval := mulmod(challenge_poly_eval, numerator, p)
2932 let lagrange_first := mulmod(mload(CONSISTENCY_DENOMINATORS_BASE_0), numerator, p)
2933 let lagrange_last := mulmod(
2934 mload(CONSISTENCY_DENOMINATORS_BASE_{{ NUMBER_OF_LAGRANGE_BASES_PLUS_ONE }}),
2939 // Step 6: Compute diff and verify == 0
2940 // diff = lagrangeFirst * libraPolyEvals[2]
2941 let diff := mulmod(lagrange_first, mload(LIBRA_POLY_EVAL_2_LOC), p)
2943 // diff += (geminiR - SUBGROUP_GENERATOR_INVERSE) *
2944 // (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * challengePolyEval)
2946 let inner := addmod(
2947 mload(LIBRA_POLY_EVAL_1_LOC),
2951 mload(LIBRA_POLY_EVAL_2_LOC),
2952 mulmod(mload(LIBRA_POLY_EVAL_0_LOC), challenge_poly_eval, p),
2958 let factor := addmod(gemini_r, sub(p, SUBGROUP_GENERATOR_INVERSE), p)
2959 diff := addmod(diff, mulmod(factor, inner, p), p)
2962 // diff += lagrangeLast * (libraPolyEvals[2] - libraEval)
2967 addmod(mload(LIBRA_POLY_EVAL_2_LOC), sub(p, mload(LIBRA_EVALUATION_LOC)), p),
2973 // diff -= vanishingPolyEval * libraPolyEvals[3]
2976 sub(p, mulmod(vanishing_poly_eval, mload(LIBRA_POLY_EVAL_3_LOC), p)),
2981 mstore(0x00, CONSISTENCY_CHECK_FAILED_SELECTOR)
2986 let precomp_success_flag := 1
2987 let q := Q // EC group order
2989 // The initial accumulator = 1 * shplonk_q
2990 mcopy(ACCUMULATOR, SHPLONK_Q_X_LOC, 0x40)
2993 // Accumulate geminiMaskingPoly (ZK commitment[1])
2995 mcopy(G1_LOCATION, GEMINI_MASKING_POLY_X_LOC, 0x40)
2996 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_1_LOC))
2997 precomp_success_flag := and(
2998 precomp_success_flag,
2999 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3001 precomp_success_flag := and(
3002 precomp_success_flag,
3003 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3007 // Accumulate vk points
3010 // VK batchMul order matches UltraFlavor_Generated::EntityId precomputed layout.
3011 // Note: ZK proof has gemini_masking_poly at sumcheckEvaluations[0] paired with SCALAR_1
3012 // (handled separately above), so the precomputed VK entries pair with SCALAR_2..SCALAR_29.
3014 // Accumulator = accumulator + scalar[2] * vk[0] (sigma_1)
3015 mcopy(G1_LOCATION, SIGMA_1_X_LOC, 0x40)
3016 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_2_LOC))
3017 precomp_success_flag := and(
3018 precomp_success_flag,
3019 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3021 precomp_success_flag := and(
3022 precomp_success_flag,
3023 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3026 // Accumulator = accumulator + scalar[3] * vk[1] (sigma_2)
3027 mcopy(G1_LOCATION, SIGMA_2_X_LOC, 0x40)
3028 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_3_LOC))
3029 precomp_success_flag := and(
3030 precomp_success_flag,
3031 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3033 precomp_success_flag := and(
3034 precomp_success_flag,
3035 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3038 // Accumulator = accumulator + scalar[4] * vk[2] (sigma_3)
3039 mcopy(G1_LOCATION, SIGMA_3_X_LOC, 0x40)
3040 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_4_LOC))
3041 precomp_success_flag := and(
3042 precomp_success_flag,
3043 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3045 precomp_success_flag := and(
3046 precomp_success_flag,
3047 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3050 // Accumulator = accumulator + scalar[5] * vk[3] (sigma_4)
3051 mcopy(G1_LOCATION, SIGMA_4_X_LOC, 0x40)
3052 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_5_LOC))
3053 precomp_success_flag := and(
3054 precomp_success_flag,
3055 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3057 precomp_success_flag := and(
3058 precomp_success_flag,
3059 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3062 // Accumulator = accumulator + scalar[6] * vk[4] (id_1)
3063 mcopy(G1_LOCATION, ID_1_X_LOC, 0x40)
3064 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_6_LOC))
3065 precomp_success_flag := and(
3066 precomp_success_flag,
3067 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3069 precomp_success_flag := and(
3070 precomp_success_flag,
3071 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3074 // Accumulator = accumulator + scalar[7] * vk[5] (id_2)
3075 mcopy(G1_LOCATION, ID_2_X_LOC, 0x40)
3076 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_7_LOC))
3077 precomp_success_flag := and(
3078 precomp_success_flag,
3079 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3081 precomp_success_flag := and(
3082 precomp_success_flag,
3083 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3086 // Accumulator = accumulator + scalar[8] * vk[6] (id_3)
3087 mcopy(G1_LOCATION, ID_3_X_LOC, 0x40)
3088 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_8_LOC))
3089 precomp_success_flag := and(
3090 precomp_success_flag,
3091 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3093 precomp_success_flag := and(
3094 precomp_success_flag,
3095 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3098 // Accumulator = accumulator + scalar[9] * vk[7] (id_4)
3099 mcopy(G1_LOCATION, ID_4_X_LOC, 0x40)
3100 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_9_LOC))
3101 precomp_success_flag := and(
3102 precomp_success_flag,
3103 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3105 precomp_success_flag := and(
3106 precomp_success_flag,
3107 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3110 // Accumulator = accumulator + scalar[10] * vk[8] (lagrange_first)
3111 mcopy(G1_LOCATION, LAGRANGE_FIRST_X_LOC, 0x40)
3112 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_10_LOC))
3113 precomp_success_flag := and(
3114 precomp_success_flag,
3115 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3117 precomp_success_flag := and(
3118 precomp_success_flag,
3119 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3122 // Accumulator = accumulator + scalar[11] * vk[9] (lagrange_last)
3123 mcopy(G1_LOCATION, LAGRANGE_LAST_X_LOC, 0x40)
3124 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_11_LOC))
3125 precomp_success_flag := and(
3126 precomp_success_flag,
3127 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3129 precomp_success_flag := and(
3130 precomp_success_flag,
3131 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3134 // Accumulator = accumulator + scalar[12] * vk[10] (q_lookup)
3135 mcopy(G1_LOCATION, Q_LOOKUP_X_LOC, 0x40)
3136 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_12_LOC))
3137 precomp_success_flag := and(
3138 precomp_success_flag,
3139 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3141 precomp_success_flag := and(
3142 precomp_success_flag,
3143 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3146 // Accumulator = accumulator + scalar[13] * vk[11] (table_1)
3147 mcopy(G1_LOCATION, TABLE_1_X_LOC, 0x40)
3148 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_13_LOC))
3149 precomp_success_flag := and(
3150 precomp_success_flag,
3151 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3153 precomp_success_flag := and(
3154 precomp_success_flag,
3155 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3158 // Accumulator = accumulator + scalar[14] * vk[12] (table_2)
3159 mcopy(G1_LOCATION, TABLE_2_X_LOC, 0x40)
3160 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_14_LOC))
3161 precomp_success_flag := and(
3162 precomp_success_flag,
3163 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3165 precomp_success_flag := and(
3166 precomp_success_flag,
3167 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3170 // Accumulator = accumulator + scalar[15] * vk[13] (table_3)
3171 mcopy(G1_LOCATION, TABLE_3_X_LOC, 0x40)
3172 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_15_LOC))
3173 precomp_success_flag := and(
3174 precomp_success_flag,
3175 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3177 precomp_success_flag := and(
3178 precomp_success_flag,
3179 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3182 // Accumulator = accumulator + scalar[16] * vk[14] (table_4)
3183 mcopy(G1_LOCATION, TABLE_4_X_LOC, 0x40)
3184 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_16_LOC))
3185 precomp_success_flag := and(
3186 precomp_success_flag,
3187 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3189 precomp_success_flag := and(
3190 precomp_success_flag,
3191 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3194 // Accumulator = accumulator + scalar[17] * vk[15] (q_m)
3195 mcopy(G1_LOCATION, Q_M_X_LOC, 0x40)
3196 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_17_LOC))
3197 precomp_success_flag := and(
3198 precomp_success_flag,
3199 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3201 precomp_success_flag := and(
3202 precomp_success_flag,
3203 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3206 // Accumulator = accumulator + scalar[18] * vk[16] (q_r)
3207 mcopy(G1_LOCATION, Q_R_X_LOC, 0x40)
3208 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_18_LOC))
3209 precomp_success_flag := and(
3210 precomp_success_flag,
3211 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3213 precomp_success_flag := and(
3214 precomp_success_flag,
3215 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3218 // Accumulator = accumulator + scalar[19] * vk[17] (q_o)
3219 mcopy(G1_LOCATION, Q_O_X_LOC, 0x40)
3220 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_19_LOC))
3221 precomp_success_flag := and(
3222 precomp_success_flag,
3223 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3225 precomp_success_flag := and(
3226 precomp_success_flag,
3227 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3230 // Accumulator = accumulator + scalar[20] * vk[18] (q_c)
3231 mcopy(G1_LOCATION, Q_C_X_LOC, 0x40)
3232 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_20_LOC))
3233 precomp_success_flag := and(
3234 precomp_success_flag,
3235 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3237 precomp_success_flag := and(
3238 precomp_success_flag,
3239 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3242 // Accumulator = accumulator + scalar[21] * vk[19] (q_l)
3243 mcopy(G1_LOCATION, Q_L_X_LOC, 0x40)
3244 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_21_LOC))
3245 precomp_success_flag := and(
3246 precomp_success_flag,
3247 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3249 precomp_success_flag := and(
3250 precomp_success_flag,
3251 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3254 // Accumulator = accumulator + scalar[22] * vk[20] (q_4)
3255 mcopy(G1_LOCATION, Q_4_X_LOC, 0x40)
3256 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_22_LOC))
3257 precomp_success_flag := and(
3258 precomp_success_flag,
3259 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3261 precomp_success_flag := and(
3262 precomp_success_flag,
3263 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3266 // Accumulator = accumulator + scalar[23] * vk[21] (q_arith)
3267 mcopy(G1_LOCATION, Q_ARITH_X_LOC, 0x40)
3268 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_23_LOC))
3269 precomp_success_flag := and(
3270 precomp_success_flag,
3271 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3273 precomp_success_flag := and(
3274 precomp_success_flag,
3275 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3278 // Accumulator = accumulator + scalar[24] * vk[22] (q_delta_range)
3279 mcopy(G1_LOCATION, Q_DELTA_RANGE_X_LOC, 0x40)
3280 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_24_LOC))
3281 precomp_success_flag := and(
3282 precomp_success_flag,
3283 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3285 precomp_success_flag := and(
3286 precomp_success_flag,
3287 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3290 // Accumulator = accumulator + scalar[25] * vk[23] (q_elliptic)
3291 mcopy(G1_LOCATION, Q_ELLIPTIC_X_LOC, 0x40)
3292 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_25_LOC))
3293 precomp_success_flag := and(
3294 precomp_success_flag,
3295 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3297 precomp_success_flag := and(
3298 precomp_success_flag,
3299 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3302 // Accumulator = accumulator + scalar[26] * vk[24] (q_memory)
3303 mcopy(G1_LOCATION, Q_MEMORY_X_LOC, 0x40)
3304 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_26_LOC))
3305 precomp_success_flag := and(
3306 precomp_success_flag,
3307 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3309 precomp_success_flag := and(
3310 precomp_success_flag,
3311 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3314 // Accumulator = accumulator + scalar[27] * vk[25] (q_nnf)
3315 mcopy(G1_LOCATION, Q_NNF_X_LOC, 0x40)
3316 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_27_LOC))
3317 precomp_success_flag := and(
3318 precomp_success_flag,
3319 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3321 precomp_success_flag := and(
3322 precomp_success_flag,
3323 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3326 // Accumulator = accumulator + scalar[28] * vk[26] (q_poseidon2_external)
3327 mcopy(G1_LOCATION, Q_POSEIDON_2_EXTERNAL_X_LOC, 0x40)
3328 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_28_LOC))
3329 precomp_success_flag := and(
3330 precomp_success_flag,
3331 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3333 precomp_success_flag := and(
3334 precomp_success_flag,
3335 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3338 // Accumulator = accumulator + scalar[29] * vk[27] (q_poseidon2_internal)
3339 mcopy(G1_LOCATION, Q_POSEIDON_2_INTERNAL_X_LOC, 0x40)
3340 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_29_LOC))
3341 precomp_success_flag := and(
3342 precomp_success_flag,
3343 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3345 precomp_success_flag := and(
3346 precomp_success_flag,
3347 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3350 // Accumulator = accumulator + constant_term_acc * G (generator)
3351 mstore(G1_LOCATION, 0x01) // G1 generator x
3352 mstore(G1_Y_LOCATION, 0x02) // G1 generator y
3353 mstore(SCALAR_LOCATION, constant_term_acc)
3354 precomp_success_flag := and(
3355 precomp_success_flag,
3356 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3358 precomp_success_flag := and(
3359 precomp_success_flag,
3360 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3363 // Accumulate proof points
3364 // Accumulator = accumulator + scalar[29] * w_l
3365 mcopy(G1_LOCATION, W_L_X_LOC, 0x40)
3366 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_30_LOC))
3367 precomp_success_flag := and(
3368 precomp_success_flag,
3369 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3371 precomp_success_flag := and(
3372 precomp_success_flag,
3373 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3376 // Accumulator = accumulator + scalar[30] * w_r
3377 mcopy(G1_LOCATION, W_R_X_LOC, 0x40)
3378 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_31_LOC))
3379 precomp_success_flag := and(
3380 precomp_success_flag,
3381 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3383 precomp_success_flag := and(
3384 precomp_success_flag,
3385 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3388 // Accumulator = accumulator + scalar[31] * w_o
3389 mcopy(G1_LOCATION, W_O_X_LOC, 0x40)
3390 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_32_LOC))
3391 precomp_success_flag := and(
3392 precomp_success_flag,
3393 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3395 precomp_success_flag := and(
3396 precomp_success_flag,
3397 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3400 // Accumulator = accumulator + scalar[32] * w_4
3401 mcopy(G1_LOCATION, W_4_X_LOC, 0x40)
3402 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_33_LOC))
3403 precomp_success_flag := and(
3404 precomp_success_flag,
3405 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3407 precomp_success_flag := and(
3408 precomp_success_flag,
3409 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3412 // Accumulator = accumulator + scalar[33] * z_perm
3413 mcopy(G1_LOCATION, Z_PERM_X_LOC, 0x40)
3414 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_34_LOC))
3415 precomp_success_flag := and(
3416 precomp_success_flag,
3417 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3419 precomp_success_flag := and(
3420 precomp_success_flag,
3421 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3424 // Accumulator = accumulator + scalar[34] * lookup_inverses
3425 mcopy(G1_LOCATION, LOOKUP_INVERSES_X_LOC, 0x40)
3426 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_35_LOC))
3427 precomp_success_flag := and(
3428 precomp_success_flag,
3429 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3431 precomp_success_flag := and(
3432 precomp_success_flag,
3433 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3436 // Accumulator = accumulator + scalar[35] * lookup_read_counts
3437 mcopy(G1_LOCATION, LOOKUP_READ_COUNTS_X_LOC, 0x40)
3438 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_36_LOC))
3439 precomp_success_flag := and(
3440 precomp_success_flag,
3441 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3443 precomp_success_flag := and(
3444 precomp_success_flag,
3445 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3448 // Accumulator = accumulator + scalar[36] * lookup_read_tags
3449 mcopy(G1_LOCATION, LOOKUP_READ_TAGS_X_LOC, 0x40)
3450 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_37_LOC))
3451 precomp_success_flag := and(
3452 precomp_success_flag,
3453 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3455 precomp_success_flag := and(
3456 precomp_success_flag,
3457 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3460 // Accumulate these LOG_N scalars with the gemini fold univariates
3468 // Accumulate libra commitments (ZK)
3470 // scalar[52] * libraConcat (libraCommitments[0])
3471 mcopy(G1_LOCATION, LIBRA_CONCAT_X_LOC, 0x40)
3472 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_{{ LIBRA_BATCH_SCALAR_0 }}_LOC))
3473 precomp_success_flag := and(
3474 precomp_success_flag,
3475 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3477 precomp_success_flag := and(
3478 precomp_success_flag,
3479 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3482 // scalar[53] * libraGrandProduct (libraCommitments[1])
3483 mcopy(G1_LOCATION, LIBRA_GRAND_PRODUCT_X_LOC, 0x40)
3484 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_{{ LIBRA_BATCH_SCALAR_1 }}_LOC))
3485 precomp_success_flag := and(
3486 precomp_success_flag,
3487 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3489 precomp_success_flag := and(
3490 precomp_success_flag,
3491 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3494 // scalar[54] * libraQuotient (libraCommitments[2])
3495 mcopy(G1_LOCATION, LIBRA_QUOTIENT_X_LOC, 0x40)
3496 mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_{{ LIBRA_BATCH_SCALAR_2 }}_LOC))
3497 precomp_success_flag := and(
3498 precomp_success_flag,
3499 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3501 precomp_success_flag := and(
3502 precomp_success_flag,
3503 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3508 // Accumlate final quotient commitment into shplonk check
3509 // Accumulator = accumulator + shplonkZ * quotient commitment
3510 mcopy(G1_LOCATION, KZG_QUOTIENT_X_LOC, 0x40)
3512 mstore(SCALAR_LOCATION, mload(SHPLONK_Z_CHALLENGE))
3513 precomp_success_flag := and(
3514 precomp_success_flag,
3515 staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)
3517 precomp_success_flag := and(
3518 precomp_success_flag,
3519 staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)
3523 // All G1 points were validated on-curve during input validation.
3524 // precomp_success_flag now only tracks ecAdd/ecMul precompile success.
3525 if iszero(precomp_success_flag) {
3526 mstore(0x00, SHPLEMINI_FAILED_SELECTOR)
3530 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
3531 /* SHPLEMINI - complete */
3532 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
3534 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
3536 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
3539 mstore(0xc0, mload(KZG_QUOTIENT_X_LOC))
3540 mstore(0xe0, sub(q, mload(KZG_QUOTIENT_Y_LOC)))
3545 mcopy(0x80, ACCUMULATOR, 0x40)
3547 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
3548 /* PAIRING AGGREGATION */
3549 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
3550 // Read the pairing encoded in the first 8 field elements of the proof (2 limbs per coordinate)
3551 let p0_other_x := mload(PAIRING_POINT_0_X_0_LOC)
3552 p0_other_x := or(shl(136, mload(PAIRING_POINT_0_X_1_LOC)), p0_other_x)
3554 let p0_other_y := mload(PAIRING_POINT_0_Y_0_LOC)
3555 p0_other_y := or(shl(136, mload(PAIRING_POINT_0_Y_1_LOC)), p0_other_y)
3557 let p1_other_x := mload(PAIRING_POINT_1_X_0_LOC)
3558 p1_other_x := or(shl(136, mload(PAIRING_POINT_1_X_1_LOC)), p1_other_x)
3560 let p1_other_y := mload(PAIRING_POINT_1_Y_0_LOC)
3561 p1_other_y := or(shl(136, mload(PAIRING_POINT_1_Y_1_LOC)), p1_other_y)
3563 // Check if pairing points are default (all zero = infinity = no recursive verification)
3564 let pairing_points_are_default := iszero(or(or(p0_other_x, p0_other_y), or(p1_other_x, p1_other_y)))
3567 // Only aggregate if pairing points are non-default
3568 if iszero(pairing_points_are_default) {
3569 // Reconstructed coordinates must be < Q to prevent malleability
3571 and(lt(p0_other_x, q), lt(p0_other_y, q)),
3572 and(lt(p1_other_x, q), lt(p1_other_y, q))
3574 mstore(0x00, VALUE_GE_GROUP_ORDER_SELECTOR)
3578 // Validate p_0_other not point of infinity
3579 success := iszero(iszero(or(p0_other_x, p0_other_y)))
3580 // Validate p_1_other not point of infinity
3581 success := and(success, iszero(iszero(or(p1_other_x, p1_other_y))))
3584 mstore(0x00, p0_other_x)
3585 mstore(0x20, p0_other_y)
3588 mstore(0x40, p1_other_x)
3589 mstore(0x60, p1_other_y)
3591 // p_1_agg is already in the correct location
3593 let recursion_separator := keccak256(0x00, 0x100)
3595 // Write separator back to scratch space
3596 mstore(0x00, p0_other_x)
3598 mstore(0x40, recursion_separator)
3599 // recursion_separator * p_0_other
3600 success := and(success, staticcall(gas(), 0x07, 0x00, 0x60, 0x00, 0x40))
3602 // (recursion_separator * p_0_other) + p_0_agg
3603 mcopy(0x40, 0x80, 0x40)
3604 // p_0 = (recursion_separator * p_0_other) + p_0_agg
3605 success := and(success, staticcall(gas(), 6, 0x00, 0x80, 0x00, 0x40))
3607 mstore(0x40, p1_other_x)
3608 mstore(0x60, p1_other_y)
3609 mstore(0x80, recursion_separator)
3611 success := and(success, staticcall(gas(), 7, 0x40, 0x60, 0x40, 0x40))
3613 // Write p_1_agg back to scratch space
3614 mcopy(0x80, 0xc0, 0x40)
3616 // 0xc0 - (recursion_separator * p_1_other) + p_1_agg
3617 success := and(success, staticcall(gas(), 6, 0x40, 0x80, 0xc0, 0x40))
3619 // If default pairing points, use p_0_agg and p_1_agg directly (already at 0x80, 0xc0)
3620 if pairing_points_are_default {
3621 // Copy p_0_agg to 0x00 for pairing input
3622 mcopy(0x00, 0x80, 0x40)
3623 // p_1_agg stays at 0xc0
3627 mstore(0x40, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2)
3628 mstore(0x60, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed)
3629 mstore(0x80, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b)
3630 mstore(0xa0, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa)
3633 mstore(0x100, 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1)
3634 mstore(0x120, 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0)
3635 mstore(0x140, 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4)
3636 mstore(0x160, 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55)
3638 let pairing_success := and(success, staticcall(gas(), 8, 0x00, 0x180, 0x00, 0x20))
3639 if iszero(and(pairing_success, mload(0x00))) {
3640 mstore(0x00, SHPLEMINI_FAILED_SELECTOR)
3644 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
3645 /* PAIRING CHECK - Complete */
3646 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
3650 return(0x00, 0x20) // Proof succeeded!
3660 std::string template_str = HONK_ZK_CONTRACT_OPT_SOURCE;
3665 int log_n =
static_cast<int>(verification_key->log_circuit_size);
3677 .barycentric_domain_size = 9,
3683 std::string::size_type start_pos = template_str.find(
"// {{ SECTION_START MEMORY_LAYOUT }}");
3684 std::string::size_type end_pos = template_str.find(
"// {{ SECTION_END MEMORY_LAYOUT }}");
3685 if (start_pos != std::string::npos && end_pos != std::string::npos) {
3686 std::string::size_type start_line_end = template_str.find(
"\n", start_pos);
3688 template_str = template_str.substr(0, start_line_end + 1) + generated_code + template_str.substr(end_pos);
3692 return template_str;
void apply_template_params(std::string &template_str, VK const &verification_key, bool is_zk)
void replace_unroll_section(std::string &template_str, const std::string §ion_name, int log_n, const UnrollConfig &config)
std::string generate_memory_offsets(int log_n, const MemoryLayoutConfig &config)
void replace_memory_layout(std::string &template_str, int log_n, const MemoryLayoutConfig &mem_config)
Find the memory layout tags then insert generated layout into the offsets.
std::string get_optimized_honk_zk_solidity_verifier(auto const &verification_key)
int batched_relation_partial_length