Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
translator_circuit_builder.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
21
22#include <cstddef>
23namespace bb {
24
38 const UltraOp& ultra_op,
39 const Fq& previous_accumulator,
40 const Fq& batching_challenge_v,
41 const Fq& evaluation_input_x)
42{
43 // All parameters are well-described in the header, this is just for convenience
44 constexpr size_t TOP_STANDARD_MICROLIMB_BITS = NUM_LAST_LIMB_BITS % MICRO_LIMB_BITS;
45 constexpr size_t TOP_Z_MICROLIMB_BITS = (NUM_Z_BITS % NUM_LIMB_BITS) % MICRO_LIMB_BITS;
46 constexpr size_t TOP_QUOTIENT_MICROLIMB_BITS =
48
56 auto uint512_t_to_limbs = [](const uint512_t& original) {
57 return std::array<Fr, NUM_BINARY_LIMBS>{ Fr(original.slice(0, NUM_LIMB_BITS).lo),
58 Fr(original.slice(NUM_LIMB_BITS, 2 * NUM_LIMB_BITS).lo),
59 Fr(original.slice(2 * NUM_LIMB_BITS, 3 * NUM_LIMB_BITS).lo),
60 Fr(original.slice(3 * NUM_LIMB_BITS, 4 * NUM_LIMB_BITS).lo) };
61 };
62
67 auto split_wide_limb_into_2_limbs = [](const Fr& wide_limb) {
69 Fr(uint256_t(wide_limb).slice(NUM_LIMB_BITS, 2 * NUM_LIMB_BITS)) };
70 };
75 auto split_standard_limb_into_micro_limbs = [](const Fr& limb) {
76 static_assert(MICRO_LIMB_BITS == 14);
85 };
86 };
87
93 auto split_top_limb_into_micro_limbs = [](const Fr& limb, const size_t last_limb_bits) {
94 static_assert(MICRO_LIMB_BITS == 14);
100 << (MICRO_LIMB_BITS - (last_limb_bits % MICRO_LIMB_BITS)),
101 0 };
102 };
103
109 auto split_top_z_limb_into_micro_limbs = [](const Fr& limb, const size_t last_limb_bits) {
110 static_assert(MICRO_LIMB_BITS == 14);
117 << (MICRO_LIMB_BITS - (last_limb_bits % MICRO_LIMB_BITS)) };
118 };
119
125 auto split_relation_limb_into_micro_limbs = [](const Fr& limb) {
126 static_assert(MICRO_LIMB_BITS == 14);
127 return std::array<Fr, 6>{
134 };
135 };
136 // x and powers of v are given to us in challenge form, so the verifier has to deal with this :)
138 Fq v_cubed = v_squared * batching_challenge_v;
139 Fq v_quarted = v_cubed * batching_challenge_v;
140
141 // Convert the accumulator, powers of v and x into "bigfield" form
142 auto previous_accumulator_limbs = split_fq_into_limbs(previous_accumulator);
143 auto v_witnesses = split_fq_into_limbs(batching_challenge_v);
144 auto v_squared_witnesses = split_fq_into_limbs(v_squared);
145 auto v_cubed_witnesses = split_fq_into_limbs(v_cubed);
146 auto v_quarted_witnesses = split_fq_into_limbs(v_quarted);
147 auto x_witnesses = split_fq_into_limbs(evaluation_input_x);
148
149 // To calculate the quotient, we need to evaluate the expression in integers. So we need uint512_t versions of all
150 // elements involved
151 size_t op_code = ultra_op.op_code.value();
152 auto uint_previous_accumulator = uint512_t(previous_accumulator);
153 auto uint_x = uint512_t(evaluation_input_x);
154 auto uint_op = uint512_t(op_code);
155 auto uint_p_x = uint512_t(uint256_t(ultra_op.x_lo) + (uint256_t(ultra_op.x_hi) << (NUM_LIMB_BITS << 1)));
156 auto uint_p_y = uint512_t(uint256_t(ultra_op.y_lo) + (uint256_t(ultra_op.y_hi) << (NUM_LIMB_BITS << 1)));
157 auto uint_z1 = uint512_t(ultra_op.z_1);
158 auto uint_z2 = uint512_t(ultra_op.z_2);
159 auto uint_v = uint512_t(batching_challenge_v);
160 auto uint_v_squared = uint512_t(v_squared);
161 auto uint_v_cubed = uint512_t(v_cubed);
162 auto uint_v_quarted = uint512_t(v_quarted);
163
164 // Construct Fq for op, P.x, P.y, z_1, z_2 for use in witness computation
165 Fq base_op = Fq(uint256_t(op_code));
166 Fq base_p_x = Fq(uint256_t(ultra_op.x_lo) + (uint256_t(ultra_op.x_hi) << (NUM_LIMB_BITS << 1)));
167 Fq base_p_y = Fq(uint256_t(ultra_op.y_lo) + (uint256_t(ultra_op.y_hi) << (NUM_LIMB_BITS << 1)));
168 Fq base_z_1 = Fq(uint256_t(ultra_op.z_1));
169 Fq base_z_2 = Fq(uint256_t(ultra_op.z_2));
170
171 // Construct bigfield representations of P.x and P.y
172 auto [p_x_0, p_x_1] = split_wide_limb_into_2_limbs(ultra_op.x_lo);
173 auto [p_x_2, p_x_3] = split_wide_limb_into_2_limbs(ultra_op.x_hi);
174 std::array<Fr, NUM_BINARY_LIMBS> p_x_limbs = { p_x_0, p_x_1, p_x_2, p_x_3 };
175 auto [p_y_0, p_y_1] = split_wide_limb_into_2_limbs(ultra_op.y_lo);
176 auto [p_y_2, p_y_3] = split_wide_limb_into_2_limbs(ultra_op.y_hi);
177 std::array<Fr, NUM_BINARY_LIMBS> p_y_limbs = { p_y_0, p_y_1, p_y_2, p_y_3 };
178
179 // Construct bigfield representations of ultra_op.z_1 and ultra_op.z_2 only using 2 limbs each
180 auto z_1_limbs = split_wide_limb_into_2_limbs(ultra_op.z_1);
181 auto z_2_limbs = split_wide_limb_into_2_limbs(ultra_op.z_2);
182
183 // The formula is `accumulator = accumulator⋅x + (op + v⋅p.x + v²⋅p.y + v³⋅z₁ + v⁴z₂)`. We need to compute the
184 // remainder (new accumulator value)
185
186 const Fq remainder = previous_accumulator * evaluation_input_x + base_z_2 * v_quarted + base_z_1 * v_cubed +
187 base_p_y * v_squared + base_p_x * batching_challenge_v + base_op;
188
189 // We also need to compute the quotient
190 uint512_t quotient_by_modulus = uint_previous_accumulator * uint_x + uint_z2 * uint_v_quarted +
191 uint_z1 * uint_v_cubed + uint_p_y * uint_v_squared + uint_p_x * uint_v + uint_op -
192 uint512_t(remainder);
193
194 uint512_t quotient = quotient_by_modulus / uint512_t(Fq::modulus);
195
196 BB_ASSERT_EQ(quotient_by_modulus, quotient * uint512_t(Fq::modulus));
197
198 // Compute quotient and remainder bigfield representation
199 auto remainder_limbs = split_fq_into_limbs(remainder);
200 std::array<Fr, NUM_BINARY_LIMBS> quotient_limbs = uint512_t_to_limbs(quotient);
201
202 // We will divide by shift_2 instantly in the relation itself, but first we need to compute the low part (0*0) and
203 // the high part (0*1, 1*0) multiplied by a single limb shift
204 Fr low_wide_relation_limb_part_1 = previous_accumulator_limbs[0] * x_witnesses[0] + op_code +
205 v_witnesses[0] * p_x_limbs[0] + v_squared_witnesses[0] * p_y_limbs[0] +
206 v_cubed_witnesses[0] * z_1_limbs[0] + v_quarted_witnesses[0] * z_2_limbs[0] +
207 quotient_limbs[0] * NEGATIVE_MODULUS_LIMBS[0] -
208 remainder_limbs[0]; // This covers the lowest limb
209
210 Fr low_wide_relation_limb =
211 low_wide_relation_limb_part_1 +
212 (previous_accumulator_limbs[1] * x_witnesses[0] + previous_accumulator_limbs[0] * x_witnesses[1] +
213 v_witnesses[1] * p_x_limbs[0] + p_x_limbs[1] * v_witnesses[0] + v_squared_witnesses[1] * p_y_limbs[0] +
214 v_squared_witnesses[0] * p_y_limbs[1] + v_cubed_witnesses[1] * z_1_limbs[0] +
215 z_1_limbs[1] * v_cubed_witnesses[0] + v_quarted_witnesses[1] * z_2_limbs[0] +
216 v_quarted_witnesses[0] * z_2_limbs[1] + quotient_limbs[0] * NEGATIVE_MODULUS_LIMBS[1] +
217 quotient_limbs[1] * NEGATIVE_MODULUS_LIMBS[0] - remainder_limbs[1]) *
218 SHIFT_1;
219
220 // Low bits have to be zero
221 BB_ASSERT_EQ(uint256_t(low_wide_relation_limb).slice(0, 2 * NUM_LIMB_BITS), 0U);
222
223 Fr low_wide_relation_limb_divided = low_wide_relation_limb * SHIFT_2_INVERSE;
224
225 // The high relation limb is the accumulation of the low limb divided by 2¹³⁶ and the combination of limbs with
226 // indices (0*2,1*1,2*0) with limbs with indices (0*3,1*2,2*1,3*0) multiplied by 2⁶⁸
227
228 Fr high_wide_relation_limb =
229 low_wide_relation_limb_divided + previous_accumulator_limbs[2] * x_witnesses[0] +
230 previous_accumulator_limbs[1] * x_witnesses[1] + previous_accumulator_limbs[0] * x_witnesses[2] +
231 v_witnesses[2] * p_x_limbs[0] + v_witnesses[1] * p_x_limbs[1] + v_witnesses[0] * p_x_limbs[2] +
232 v_squared_witnesses[2] * p_y_limbs[0] + v_squared_witnesses[1] * p_y_limbs[1] +
233 v_squared_witnesses[0] * p_y_limbs[2] + v_cubed_witnesses[2] * z_1_limbs[0] +
234 v_cubed_witnesses[1] * z_1_limbs[1] + v_quarted_witnesses[2] * z_2_limbs[0] +
235 v_quarted_witnesses[1] * z_2_limbs[1] + quotient_limbs[2] * NEGATIVE_MODULUS_LIMBS[0] +
236 quotient_limbs[1] * NEGATIVE_MODULUS_LIMBS[1] + quotient_limbs[0] * NEGATIVE_MODULUS_LIMBS[2] -
237 remainder_limbs[2] +
238 (previous_accumulator_limbs[3] * x_witnesses[0] + previous_accumulator_limbs[2] * x_witnesses[1] +
239 previous_accumulator_limbs[1] * x_witnesses[2] + previous_accumulator_limbs[0] * x_witnesses[3] +
240 v_witnesses[3] * p_x_limbs[0] + v_witnesses[2] * p_x_limbs[1] + v_witnesses[1] * p_x_limbs[2] +
241 v_witnesses[0] * p_x_limbs[3] + v_squared_witnesses[3] * p_y_limbs[0] + v_squared_witnesses[2] * p_y_limbs[1] +
242 v_squared_witnesses[1] * p_y_limbs[2] + v_squared_witnesses[0] * p_y_limbs[3] +
243 v_cubed_witnesses[3] * z_1_limbs[0] + v_cubed_witnesses[2] * z_1_limbs[1] +
244 v_quarted_witnesses[3] * z_2_limbs[0] + v_quarted_witnesses[2] * z_2_limbs[1] +
245 quotient_limbs[3] * NEGATIVE_MODULUS_LIMBS[0] + quotient_limbs[2] * NEGATIVE_MODULUS_LIMBS[1] +
246 quotient_limbs[1] * NEGATIVE_MODULUS_LIMBS[2] + quotient_limbs[0] * NEGATIVE_MODULUS_LIMBS[3] -
247 remainder_limbs[3]) *
248 SHIFT_1;
249
250 // Check that the results lower 136 bits are zero
251 BB_ASSERT_EQ(uint256_t(high_wide_relation_limb).slice(0, 2 * NUM_LIMB_BITS), 0U);
252
253 // Get divided version
254 auto high_wide_relation_limb_divided = high_wide_relation_limb * SHIFT_2_INVERSE;
255
256 const auto last_limb_index = NUM_BINARY_LIMBS - 1;
257
262 std::array<std::array<Fr, NUM_MICRO_LIMBS>, NUM_BINARY_LIMBS> current_accumulator_microlimbs;
264 // Split P_x into microlimbs for range constraining
265 for (size_t i = 0; i < last_limb_index; i++) {
266 P_x_microlimbs[i] = split_standard_limb_into_micro_limbs(p_x_limbs[i]);
267 }
268 P_x_microlimbs[last_limb_index] =
269 split_top_limb_into_micro_limbs(p_x_limbs[last_limb_index], TOP_STANDARD_MICROLIMB_BITS);
270
271 // Split P_y into microlimbs for range constraining
272 for (size_t i = 0; i < last_limb_index; i++) {
273 P_y_microlimbs[i] = split_standard_limb_into_micro_limbs(p_y_limbs[i]);
274 }
275 P_y_microlimbs[last_limb_index] =
276 split_top_limb_into_micro_limbs(p_y_limbs[last_limb_index], TOP_STANDARD_MICROLIMB_BITS);
277
278 // Split z scalars into microlimbs for range constraining
279 for (size_t i = 0; i < NUM_Z_LIMBS - 1; i++) {
280 z_1_microlimbs[i] = split_standard_limb_into_micro_limbs(z_1_limbs[i]);
281 z_2_microlimbs[i] = split_standard_limb_into_micro_limbs(z_2_limbs[i]);
282 }
283 z_1_microlimbs[NUM_Z_LIMBS - 1] =
284 split_top_z_limb_into_micro_limbs(z_1_limbs[NUM_Z_LIMBS - 1], TOP_Z_MICROLIMB_BITS);
285 z_2_microlimbs[NUM_Z_LIMBS - 1] =
286 split_top_z_limb_into_micro_limbs(z_2_limbs[NUM_Z_LIMBS - 1], TOP_Z_MICROLIMB_BITS);
287
288 // Split current accumulator into microlimbs for range constraining
289 for (size_t i = 0; i < last_limb_index; i++) {
290 current_accumulator_microlimbs[i] = split_standard_limb_into_micro_limbs(remainder_limbs[i]);
291 }
292 current_accumulator_microlimbs[last_limb_index] =
293 split_top_limb_into_micro_limbs(remainder_limbs[last_limb_index], TOP_STANDARD_MICROLIMB_BITS);
294
295 // Split quotient into microlimbs for range constraining
296 for (size_t i = 0; i < last_limb_index; i++) {
297 quotient_microlimbs[i] = split_standard_limb_into_micro_limbs(quotient_limbs[i]);
298 }
299 quotient_microlimbs[last_limb_index] =
300 split_top_limb_into_micro_limbs(quotient_limbs[last_limb_index], TOP_QUOTIENT_MICROLIMB_BITS);
301
302 // Start filling the witness container
303 AccumulationInput input{
304 .ultra_op = ultra_op,
305 .P_x_limbs = p_x_limbs,
306 .P_x_microlimbs = P_x_microlimbs,
307 .P_y_limbs = p_y_limbs,
308 .P_y_microlimbs = P_y_microlimbs,
309 .z_1_limbs = z_1_limbs,
310 .z_1_microlimbs = z_1_microlimbs,
311 .z_2_limbs = z_2_limbs,
312 .z_2_microlimbs = z_2_microlimbs,
313 .previous_accumulator = previous_accumulator_limbs,
314 .current_accumulator = remainder_limbs,
315 .current_accumulator_microlimbs = current_accumulator_microlimbs,
316 .quotient_binary_limbs = quotient_limbs,
317 .quotient_microlimbs = quotient_microlimbs,
318 .relation_wide_limbs = { low_wide_relation_limb_divided, high_wide_relation_limb_divided },
319 .relation_wide_microlimbs = { split_relation_limb_into_micro_limbs(low_wide_relation_limb_divided),
320 split_relation_limb_into_micro_limbs(high_wide_relation_limb_divided) },
321
322 };
323
324 return input;
325}
326
328{
329 // Opcode should be {0,3,4,8}
330 size_t op_code = ultra_op.op_code.value();
331 ASSERT(op_code == 0 || op_code == 3 || op_code == 4 || op_code == 8);
332
333 // Check and insert x_lo and y_hi into wire 1
336
337 // Check and insert x_hi and z_1 into wire 2
340
341 // Check and insert y_lo and z_2 into wire 3
344}
345
347{
348 // The first wires OpQueue/Transcript wires
350
351 // Check decomposition of values from the Queue into limbs used in bigfield evaluations
352 BB_ASSERT_EQ(acc_step.ultra_op.x_lo, acc_step.P_x_limbs[0] + acc_step.P_x_limbs[1] * SHIFT_1);
353 BB_ASSERT_EQ(acc_step.ultra_op.x_hi, acc_step.P_x_limbs[2] + acc_step.P_x_limbs[3] * SHIFT_1);
354 BB_ASSERT_EQ(acc_step.ultra_op.y_lo, acc_step.P_y_limbs[0] + acc_step.P_y_limbs[1] * SHIFT_1);
355 BB_ASSERT_EQ(acc_step.ultra_op.y_hi, acc_step.P_y_limbs[2] + acc_step.P_y_limbs[3] * SHIFT_1);
356 BB_ASSERT_EQ(acc_step.ultra_op.z_1, acc_step.z_1_limbs[0] + acc_step.z_1_limbs[1] * SHIFT_1);
357 BB_ASSERT_EQ(acc_step.ultra_op.z_2, acc_step.z_2_limbs[0] + acc_step.z_2_limbs[1] * SHIFT_1);
362 auto check_binary_limbs_maximum_values = []<size_t total_limbs>(const std::array<Fr, total_limbs>& limbs,
363 const uint256_t& MAX_LAST_LIMB =
365 for (size_t i = 0; i < total_limbs - 1; i++) {
366 BB_ASSERT_LT(uint256_t(limbs[i]), SHIFT_1);
367 }
368 BB_ASSERT_LT(uint256_t(limbs[total_limbs - 1]), MAX_LAST_LIMB);
369 };
370
371 const auto MAX_Z_LAST_LIMB = uint256_t(1) << (NUM_Z_BITS - NUM_LIMB_BITS);
372 const auto MAX_QUOTIENT_LAST_LIMB = uint256_t(1) << (NUM_LAST_QUOTIENT_LIMB_BITS);
373 // Check limb values are in 68-bit range
374 check_binary_limbs_maximum_values(acc_step.P_x_limbs);
375 check_binary_limbs_maximum_values(acc_step.P_y_limbs);
376 check_binary_limbs_maximum_values(acc_step.z_1_limbs, /*MAX_LAST_LIMB=*/MAX_Z_LAST_LIMB);
377 check_binary_limbs_maximum_values(acc_step.z_2_limbs, /*MAX_LAST_LIMB=*/MAX_Z_LAST_LIMB);
378 check_binary_limbs_maximum_values(acc_step.previous_accumulator);
379 check_binary_limbs_maximum_values(acc_step.current_accumulator);
380 check_binary_limbs_maximum_values(acc_step.quotient_binary_limbs, /*MAX_LAST_LIMB=*/MAX_QUOTIENT_LAST_LIMB);
381
382 // Check limbs used in range constraints are in range
383 auto check_micro_limbs_maximum_values =
384 []<size_t binary_limb_count, size_t micro_limb_count>(
385 const std::array<std::array<Fr, micro_limb_count>, binary_limb_count>& limbs) {
386 for (size_t i = 0; i < binary_limb_count; i++) {
387 for (size_t j = 0; j < micro_limb_count; j++) {
388 BB_ASSERT_LT(uint256_t(limbs[i][j]), MICRO_SHIFT);
389 }
390 }
391 };
392 check_micro_limbs_maximum_values(acc_step.P_x_microlimbs);
393 check_micro_limbs_maximum_values(acc_step.P_y_microlimbs);
394 check_micro_limbs_maximum_values(acc_step.z_1_microlimbs);
395 check_micro_limbs_maximum_values(acc_step.z_2_microlimbs);
396 check_micro_limbs_maximum_values(acc_step.current_accumulator_microlimbs);
397
398 // Check that relation limbs are in range
401}
402
404{
405 auto& op_wire = std::get<WireIds::OP>(wires);
406 op_wire.push_back(add_variable(ultra_op.op_code.value()));
407 // Similarly to the ColumnPolynomials in the merge protocol, the op_wire is 0 at every second index
408 op_wire.push_back(zero_idx);
409
411
413
415}
416
423{
425
427
428 // Insert limbs used in bigfield evaluations
429 insert_pair_into_wire(P_X_LOW_LIMBS, acc_step.P_x_limbs[0], acc_step.P_x_limbs[1]);
430 insert_pair_into_wire(P_X_HIGH_LIMBS, acc_step.P_x_limbs[2], acc_step.P_x_limbs[3]);
431 insert_pair_into_wire(P_Y_LOW_LIMBS, acc_step.P_y_limbs[0], acc_step.P_y_limbs[1]);
432 insert_pair_into_wire(P_Y_HIGH_LIMBS, acc_step.P_y_limbs[2], acc_step.P_y_limbs[3]);
433 insert_pair_into_wire(Z_LOW_LIMBS, acc_step.z_1_limbs[0], acc_step.z_2_limbs[0]);
434 insert_pair_into_wire(Z_HIGH_LIMBS, acc_step.z_1_limbs[1], acc_step.z_2_limbs[1]);
440
441 // We are using some leftover crevices for relation_wide_microlimbs
442 auto low_relation_microlimbs = acc_step.relation_wide_microlimbs[0];
443 auto high_relation_microlimbs = acc_step.relation_wide_microlimbs[1];
444
445 // We have 4 wires specifically for the relation microlimbs
447 RELATION_WIDE_LIMBS_RANGE_CONSTRAINT_0, low_relation_microlimbs[0], high_relation_microlimbs[0]);
449 RELATION_WIDE_LIMBS_RANGE_CONSTRAINT_1, low_relation_microlimbs[1], high_relation_microlimbs[1]);
451 RELATION_WIDE_LIMBS_RANGE_CONSTRAINT_2, low_relation_microlimbs[2], high_relation_microlimbs[2]);
453 RELATION_WIDE_LIMBS_RANGE_CONSTRAINT_3, low_relation_microlimbs[3], high_relation_microlimbs[3]);
454
455 // Next ones go into top P_x and P_y, current accumulator and quotient unused microlimbs
456
457 // Insert the second highest low relation microlimb into the space left in P_x range constraints highest wire
458 auto top_p_x_microlimbs = acc_step.P_x_microlimbs[NUM_BINARY_LIMBS - 1];
459 top_p_x_microlimbs[NUM_MICRO_LIMBS - 1] = low_relation_microlimbs[NUM_MICRO_LIMBS - 2];
460
461 // Insert the second highest high relation microlimb into the space left in P_y range constraints highest wire
462 auto top_p_y_microlimbs = acc_step.P_y_microlimbs[NUM_BINARY_LIMBS - 1];
463 top_p_y_microlimbs[NUM_MICRO_LIMBS - 1] = high_relation_microlimbs[NUM_MICRO_LIMBS - 2];
464
465 // The highest low relation microlimb goes into the crevice left in current accumulator microlimbs
466 auto top_current_accumulator_microlimbs = acc_step.current_accumulator_microlimbs[NUM_BINARY_LIMBS - 1];
467 top_current_accumulator_microlimbs[NUM_MICRO_LIMBS - 1] = low_relation_microlimbs[NUM_MICRO_LIMBS - 1];
468
469 // The highest high relation microlimb goes into the quotient crevice
470 auto top_quotient_microlimbs = acc_step.quotient_microlimbs[NUM_BINARY_LIMBS - 1];
471 top_quotient_microlimbs[NUM_MICRO_LIMBS - 1] = high_relation_microlimbs[NUM_MICRO_LIMBS - 1];
472
477 auto lay_limbs_in_row = [this]<size_t array_size>(std::array<Fr, array_size> input, WireIds starting_wire) {
478 size_t wire_index = starting_wire;
479 for (auto element : input) {
480 wires[wire_index].push_back(add_variable(element));
481 wire_index++;
482 }
483 };
484
485 // Now put all microlimbs into appropriate wires
486 lay_limbs_in_row(acc_step.P_x_microlimbs[0], P_X_LOW_LIMBS_RANGE_CONSTRAINT_0);
487 lay_limbs_in_row(acc_step.P_x_microlimbs[1], P_X_LOW_LIMBS_RANGE_CONSTRAINT_0);
488 lay_limbs_in_row(acc_step.P_x_microlimbs[2], P_X_HIGH_LIMBS_RANGE_CONSTRAINT_0);
489 lay_limbs_in_row(top_p_x_microlimbs, P_X_HIGH_LIMBS_RANGE_CONSTRAINT_0);
490 lay_limbs_in_row(acc_step.P_y_microlimbs[0], P_Y_LOW_LIMBS_RANGE_CONSTRAINT_0);
491 lay_limbs_in_row(acc_step.P_y_microlimbs[1], P_Y_LOW_LIMBS_RANGE_CONSTRAINT_0);
492 lay_limbs_in_row(acc_step.P_y_microlimbs[2], P_Y_HIGH_LIMBS_RANGE_CONSTRAINT_0);
493 lay_limbs_in_row(top_p_y_microlimbs, P_Y_HIGH_LIMBS_RANGE_CONSTRAINT_0);
494 lay_limbs_in_row(acc_step.z_1_microlimbs[0], Z_LOW_LIMBS_RANGE_CONSTRAINT_0);
495 lay_limbs_in_row(acc_step.z_2_microlimbs[0], Z_LOW_LIMBS_RANGE_CONSTRAINT_0);
496 lay_limbs_in_row(acc_step.z_1_microlimbs[1], Z_HIGH_LIMBS_RANGE_CONSTRAINT_0);
497 lay_limbs_in_row(acc_step.z_2_microlimbs[1], Z_HIGH_LIMBS_RANGE_CONSTRAINT_0);
498 lay_limbs_in_row(acc_step.current_accumulator, ACCUMULATORS_BINARY_LIMBS_0);
499 lay_limbs_in_row(acc_step.previous_accumulator, ACCUMULATORS_BINARY_LIMBS_0);
503 lay_limbs_in_row(top_current_accumulator_microlimbs, ACCUMULATOR_HIGH_LIMBS_RANGE_CONSTRAINT_0);
504 lay_limbs_in_row(acc_step.quotient_microlimbs[0], QUOTIENT_LOW_LIMBS_RANGE_CONSTRAIN_0);
505 lay_limbs_in_row(acc_step.quotient_microlimbs[1], QUOTIENT_LOW_LIMBS_RANGE_CONSTRAIN_0);
506 lay_limbs_in_row(acc_step.quotient_microlimbs[2], QUOTIENT_HIGH_LIMBS_RANGE_CONSTRAIN_0);
507 lay_limbs_in_row(top_quotient_microlimbs, QUOTIENT_HIGH_LIMBS_RANGE_CONSTRAIN_0);
508
509 num_gates += 2;
510
511 // Check that all the wires are filled equally
512 bb::constexpr_for<0, TOTAL_COUNT, 1>([&]<size_t i>() { BB_ASSERT_EQ(std::get<i>(wires).size(), num_gates); });
513}
514
516{
517 using Fq = bb::fq;
518 const auto& ultra_ops = ecc_op_queue->get_ultra_ops();
519 std::vector<Fq> accumulator_trace;
520 Fq current_accumulator(0);
521 if (ultra_ops.empty()) {
522 return;
523 }
524
525 // Process the first UltraOp - a no-op - and populate with zeros the beginning of all other wires to ensure all wire
526 // polynomials in translator start with 0 (required for shifted polynomials in the proving system). Technically,
527 // we'd need only first index to be a zero but, given each "real" UltraOp populates two indices in a polynomial we
528 // add two zeros for consistency.
529 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1360): We'll also have to eventually process random
530 // data in the merge protocol (added for zero knowledge)/
531 populate_wires_from_ultra_op(ultra_ops[0]);
532 for (auto& wire : wires) {
533 if (wire.empty()) {
534 wire.push_back(zero_idx);
535 wire.push_back(zero_idx);
536 }
537 }
538 num_gates += 2;
539
540 // We need to precompute the accumulators at each step, because in the actual circuit we compute the values starting
541 // from the later indices. We need to know the previous accumulator to create the gate
542 for (size_t i = 1; i < ultra_ops.size(); i++) {
543 const auto& ultra_op = ultra_ops[ultra_ops.size() - i];
544 current_accumulator *= evaluation_input_x;
545 const auto [x_256, y_256] = ultra_op.get_base_point_standard_form();
546 current_accumulator +=
547 Fq(ultra_op.op_code.value()) +
549 (x_256 + batching_challenge_v *
550 (y_256 + batching_challenge_v *
551 (uint256_t(ultra_op.z_1) + batching_challenge_v * uint256_t(ultra_op.z_2))));
552 accumulator_trace.push_back(current_accumulator);
553 }
554
555 // We don't care about the last value since we'll recompute it during witness generation anyway
556 accumulator_trace.pop_back();
557
558 // Generate witness values from all the UltraOps
559 for (size_t i = 1; i < ultra_ops.size(); i++) {
560 const auto& ultra_op = ultra_ops[i];
561 Fq previous_accumulator = 0;
562 // Pop the last value from accumulator trace and use it as previous accumulator
563 if (!accumulator_trace.empty()) {
564 previous_accumulator = accumulator_trace.back();
565 accumulator_trace.pop_back();
566 }
567 // Compute witness values
568 AccumulationInput one_accumulation_step =
569 generate_witness_values(ultra_op, previous_accumulator, batching_challenge_v, evaluation_input_x);
570
571 // And put them into the wires
572 create_accumulation_gate(one_accumulation_step);
573 }
574}
575} // namespace bb
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:59
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:129
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:115
#define ASSERT(expression,...)
Definition assert.hpp:49
virtual uint32_t add_variable(const FF &in)
static constexpr std::array< Fr, 5 > NEGATIVE_MODULUS_LIMBS
static AccumulationInput generate_witness_values(const UltraOp &ultra_op, const Fq &previous_accumulator, const Fq &batching_challenge_v, const Fq &evaluation_input_x)
Given the transcript values from the EccOpQueue, the values of the previous accumulator,...
static void assert_well_formed_ultra_op(const UltraOp &ultra_op)
void create_accumulation_gate(const AccumulationInput &acc_step)
Create a single accumulation gate.
static std::array< Fr, NUM_BINARY_LIMBS > split_fq_into_limbs(const Fq &base)
A small function to transform a native element Fq into its bigfield representation in Fr scalars.
void feed_ecc_op_queue_into_circuit(const std::shared_ptr< ECCOpQueue > ecc_op_queue)
Generate all the gates required to prove the correctness of batched evalution of polynomials represen...
static constexpr size_t NUM_LAST_QUOTIENT_LIMB_BITS
static constexpr uint256_t MAX_RELATION_WIDE_LIMB_SIZE
std::array< SlabVector< uint32_t >, NUM_WIRES > wires
void populate_wires_from_ultra_op(const UltraOp &ultra_op)
static void assert_well_formed_accumulation_input(const AccumulationInput &acc_step)
Ensures the accumulation input is well-formed and can be used to create a gate.
WireIds
There are so many wires that naming them has no sense, it is easier to access them with enums.
void insert_pair_into_wire(WireIds wire_index, Fr first, Fr second)
constexpr uint256_t slice(uint64_t start, uint64_t end) const
uintx< uint256_t > uint512_t
Definition uintx.hpp:307
Entry point for Barretenberg command-line interface.
field< Bn254FqParams > fq
Definition fq.hpp:169
C slice(C const &container, size_t start)
Definition container.hpp:9
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
uint32_t value() const
The accumulation input structure contains all the necessary values to initalize an accumulation gate ...
std::array< std::array< Fr, NUM_MICRO_LIMBS >, NUM_Z_LIMBS > z_1_microlimbs
std::array< std::array< Fr, NUM_MICRO_LIMBS >, NUM_BINARY_LIMBS > P_y_microlimbs
std::array< std::array< Fr, NUM_MICRO_LIMBS >, NUM_BINARY_LIMBS > current_accumulator_microlimbs
std::array< std::array< Fr, NUM_MICRO_LIMBS >, 2 > relation_wide_microlimbs
std::array< std::array< Fr, NUM_MICRO_LIMBS >, NUM_BINARY_LIMBS > P_x_microlimbs
std::array< std::array< Fr, NUM_MICRO_LIMBS >, NUM_BINARY_LIMBS > quotient_microlimbs
std::array< std::array< Fr, NUM_MICRO_LIMBS >, NUM_Z_LIMBS > z_2_microlimbs
std::array< Fr, NUM_RELATION_WIDE_LIMBS > relation_wide_limbs
EccOpCode op_code
static constexpr uint256_t modulus