Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
translator_circuit_checker.cpp
Go to the documentation of this file.
3
4namespace bb {
6 const Fq& batching_challenge_v, const Fq& evaluation_input_x)
7{
8 Fq v_squared = batching_challenge_v * batching_challenge_v;
9 Fq v_cubed = v_squared * batching_challenge_v;
10 Fq v_quarted = v_cubed * batching_challenge_v;
11 return RelationInputs{
12 .x_limbs = Builder::split_fq_into_limbs(evaluation_input_x),
13 .v_limbs = Builder::split_fq_into_limbs(batching_challenge_v),
14 .v_squared_limbs = Builder::split_fq_into_limbs(v_squared),
15 .v_cubed_limbs = Builder::split_fq_into_limbs(v_cubed),
16 .v_quarted_limbs = Builder::split_fq_into_limbs(v_quarted),
17 };
18}
19
21{
22
23 auto wires = circuit.wires;
24
25 auto report_fail = [&](const char* message, size_t row_idx) {
26 info(message, row_idx);
27 return false;
28 };
29
30 // Compute the limbs of evaluation_input_x and powers of batching_challenge_v (these go into the relation)
31 RelationInputs relation_inputs =
33 // Get the main wires (we will operate with range constraint wires mainly through indices, since this is
34 // easier)
35 auto& op_wire = std::get<WireIds::OP>(circuit.wires);
36 auto& x_lo_y_hi_wire = std::get<WireIds::X_LOW_Y_HI>(circuit.wires);
37 auto& x_hi_z_1_wire = std::get<WireIds::X_HIGH_Z_1>(circuit.wires);
38 auto& y_lo_z_2_wire = std::get<WireIds::Y_LOW_Z_2>(circuit.wires);
39 auto& p_x_0_p_x_1_wire = std::get<WireIds::P_X_LOW_LIMBS>(circuit.wires);
40 auto& p_x_2_p_x_3_wire = std::get<WireIds::P_X_HIGH_LIMBS>(circuit.wires);
41 auto& p_y_0_p_y_1_wire = std::get<WireIds::P_Y_LOW_LIMBS>(circuit.wires);
42 auto& p_y_2_p_y_3_wire = std::get<WireIds::P_Y_HIGH_LIMBS>(circuit.wires);
43 auto& z_lo_wire = std::get<WireIds::Z_LOW_LIMBS>(circuit.wires);
44 auto& z_hi_wire = std::get<WireIds::Z_HIGH_LIMBS>(circuit.wires);
45 auto& accumulators_binary_limbs_0_wire = std::get<WireIds::ACCUMULATORS_BINARY_LIMBS_0>(circuit.wires);
46 auto& accumulators_binary_limbs_1_wire = std::get<WireIds::ACCUMULATORS_BINARY_LIMBS_1>(circuit.wires);
47 auto& accumulators_binary_limbs_2_wire = std::get<WireIds::ACCUMULATORS_BINARY_LIMBS_2>(circuit.wires);
48 auto& accumulators_binary_limbs_3_wire = std::get<WireIds::ACCUMULATORS_BINARY_LIMBS_3>(circuit.wires);
49 auto& quotient_low_binary_limbs = std::get<WireIds::QUOTIENT_LOW_BINARY_LIMBS>(circuit.wires);
50 auto& quotient_high_binary_limbs = std::get<WireIds::QUOTIENT_HIGH_BINARY_LIMBS>(circuit.wires);
51 auto& relation_wide_limbs_wire = std::get<WireIds::RELATION_WIDE_LIMBS>(circuit.wires);
52 auto reconstructed_evaluation_input_x = Fr(uint256_t(circuit.evaluation_input_x));
53 auto reconstructed_batching_evaluation_v = Fr(uint256_t(circuit.batching_challenge_v));
54 auto reconstructed_batching_evaluation_v2 = Fr(uint256_t(circuit.batching_challenge_v.pow(2)));
55 auto reconstructed_batching_evaluation_v3 = Fr(uint256_t(circuit.batching_challenge_v.pow(3)));
56 auto reconstructed_batching_evaluation_v4 = Fr(uint256_t(circuit.batching_challenge_v.pow(4)));
61 auto get_sequential_micro_chunks = [&](size_t gate_index, WireIds starting_wire_index, size_t chunk_count) {
62 std::vector<Fr> chunks;
63 for (size_t i = starting_wire_index; i < starting_wire_index + chunk_count; i++) {
64 chunks.push_back(circuit.get_variable(circuit.wires[i][gate_index]));
65 }
66 return chunks;
67 };
68
77 auto accumulate_limb_from_micro_chunks = [](const std::vector<Fr>& chunks, const int skipped_at_end = 1) {
78 Fr mini_accumulator(0);
79 auto end = chunks.end();
80 std::advance(end, -skipped_at_end);
81 for (auto it = end; it != chunks.begin();) {
82 --it;
83 mini_accumulator = mini_accumulator * TranslatorCircuitBuilder::MICRO_SHIFT + *it;
84 }
85 return mini_accumulator;
86 };
87
88 // TODO(https: // github.com/AztecProtocol/barretenberg/issues/1367): Report all failures more explicitly and
89 // consider making use of relations.
90
91 for (size_t i = 2; i < circuit.num_gates - 1; i += 2) {
92 {
93 // Get the values of P.x
94 Fr op_code = circuit.get_variable(op_wire[i]);
95 Fr p_x_lo = circuit.get_variable(x_lo_y_hi_wire[i]);
96 Fr p_x_hi = circuit.get_variable(x_hi_z_1_wire[i]);
97 Fr p_x_0 = circuit.get_variable(p_x_0_p_x_1_wire[i]);
98 Fr p_x_1 = circuit.get_variable(p_x_0_p_x_1_wire[i + 1]);
99 Fr p_x_2 = circuit.get_variable(p_x_2_p_x_3_wire[i]);
100 Fr p_x_3 = circuit.get_variable(p_x_2_p_x_3_wire[i + 1]);
101 const std::vector p_x_binary_limbs = { p_x_0, p_x_1, p_x_2, p_x_3 };
102
103 // P.y
104 Fr p_y_lo = circuit.get_variable(y_lo_z_2_wire[i]);
105 Fr p_y_hi = circuit.get_variable(x_lo_y_hi_wire[i + 1]);
106 Fr p_y_0 = circuit.get_variable(p_y_0_p_y_1_wire[i]);
107 Fr p_y_1 = circuit.get_variable(p_y_0_p_y_1_wire[i + 1]);
108 Fr p_y_2 = circuit.get_variable(p_y_2_p_y_3_wire[i]);
109 Fr p_y_3 = circuit.get_variable(p_y_2_p_y_3_wire[i + 1]);
110 const std::vector p_y_binary_limbs = { p_y_0, p_y_1, p_y_2, p_y_3 };
111 // z1, z2
112 Fr z_1 = circuit.get_variable(x_hi_z_1_wire[i + 1]);
113 Fr z_2 = circuit.get_variable(y_lo_z_2_wire[i + 1]);
114
115 Fr z_1_lo = circuit.get_variable(z_lo_wire[i]);
116 Fr z_2_lo = circuit.get_variable(z_lo_wire[i + 1]);
117 Fr z_1_hi = circuit.get_variable(z_hi_wire[i]);
118 Fr z_2_hi = circuit.get_variable(z_hi_wire[i + 1]);
119
120 const std::vector z_1_binary_limbs = { z_1_lo, z_1_hi };
121 const std::vector z_2_binary_limbs = { z_2_lo, z_2_hi };
122 // Relation limbs
123 Fr low_wide_relation_limb = circuit.get_variable(relation_wide_limbs_wire[i]);
124 Fr high_wide_relation_limb = circuit.get_variable(relation_wide_limbs_wire[i + 1]);
125
126 // Current accumulator (updated value)
127 const std::vector current_accumulator_binary_limbs = {
128 circuit.get_variable(accumulators_binary_limbs_0_wire[i]),
129 circuit.get_variable(accumulators_binary_limbs_1_wire[i]),
130 circuit.get_variable(accumulators_binary_limbs_2_wire[i]),
131 circuit.get_variable(accumulators_binary_limbs_3_wire[i]),
132 };
133
134 // Previous accumulator
135 const std::vector previous_accumulator_binary_limbs = {
136 circuit.get_variable(accumulators_binary_limbs_0_wire[i + 1]),
137 circuit.get_variable(accumulators_binary_limbs_1_wire[i + 1]),
138 circuit.get_variable(accumulators_binary_limbs_2_wire[i + 1]),
139 circuit.get_variable(accumulators_binary_limbs_3_wire[i + 1]),
140 };
141
142 // Quotient
143 const std::vector quotient_binary_limbs = {
144 circuit.get_variable(quotient_low_binary_limbs[i]),
145 circuit.get_variable(quotient_low_binary_limbs[i + 1]),
146 circuit.get_variable(quotient_high_binary_limbs[i]),
147 circuit.get_variable(quotient_high_binary_limbs[i + 1]),
148 };
149
150 const size_t NUM_MICRO_LIMBS = Builder::NUM_MICRO_LIMBS;
151
152 // Get micro chunks for checking decomposition and range
153 auto p_x_micro_chunks = {
154 get_sequential_micro_chunks(i, WireIds::P_X_LOW_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
155 get_sequential_micro_chunks(i + 1, WireIds::P_X_LOW_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
156 get_sequential_micro_chunks(i, WireIds::P_X_HIGH_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
157 get_sequential_micro_chunks(i + 1, WireIds::P_X_HIGH_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS)
158 };
159 auto p_y_micro_chunks = {
160 get_sequential_micro_chunks(i, WireIds::P_Y_LOW_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
161 get_sequential_micro_chunks(i + 1, WireIds::P_Y_LOW_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
162 get_sequential_micro_chunks(i, WireIds::P_Y_HIGH_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
163 get_sequential_micro_chunks(i + 1, WireIds::P_Y_HIGH_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS)
164 };
165 auto z_1_micro_chunks = {
166 get_sequential_micro_chunks(i, WireIds::Z_LOW_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
167 get_sequential_micro_chunks(i, WireIds::Z_HIGH_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
168 };
169
170 auto z_2_micro_chunks = {
171
172 get_sequential_micro_chunks(i + 1, WireIds::Z_LOW_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
173 get_sequential_micro_chunks(i + 1, WireIds::Z_HIGH_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS)
174 };
175
176 auto current_accumulator_micro_chunks = {
177 get_sequential_micro_chunks(i, WireIds::ACCUMULATOR_LOW_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
178 get_sequential_micro_chunks(i + 1, WireIds::ACCUMULATOR_LOW_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
179 get_sequential_micro_chunks(i, WireIds::ACCUMULATOR_HIGH_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
180 get_sequential_micro_chunks(i + 1, WireIds::ACCUMULATOR_HIGH_LIMBS_RANGE_CONSTRAINT_0, NUM_MICRO_LIMBS),
181 };
182 auto quotient_micro_chunks = {
183 get_sequential_micro_chunks(i, WireIds::QUOTIENT_LOW_LIMBS_RANGE_CONSTRAIN_0, NUM_MICRO_LIMBS),
184 get_sequential_micro_chunks(i + 1, WireIds::QUOTIENT_LOW_LIMBS_RANGE_CONSTRAIN_0, NUM_MICRO_LIMBS),
185 get_sequential_micro_chunks(i, WireIds::QUOTIENT_HIGH_LIMBS_RANGE_CONSTRAIN_0, NUM_MICRO_LIMBS),
186 get_sequential_micro_chunks(i + 1, WireIds::QUOTIENT_HIGH_LIMBS_RANGE_CONSTRAIN_0, NUM_MICRO_LIMBS),
187 };
188
189 // Lambda for checking the correctness of decomposition of values in the Queue into limbs for
190 // checking the relation
191 auto check_wide_limb_into_binary_limb_relation = [](const std::vector<Fr>& wide_limbs,
192 const std::vector<Fr>& binary_limbs) {
193 BB_ASSERT_EQ(wide_limbs.size() * 2, binary_limbs.size());
194 for (size_t i = 0; i < wide_limbs.size(); i++) {
195 if ((binary_limbs[i * 2] + Fr(Builder::SHIFT_1) * binary_limbs[i * 2 + 1]) != wide_limbs[i]) {
196 return false;
197 }
198 }
199 return true;
200 };
201 // Check that everything has been decomposed correctly
202 // P.xₗₒ = P.xₗₒ_0 + SHIFT_1 * P.xₗₒ_1
203 // P.xₕᵢ = P.xₕᵢ_0 + SHIFT_1 * P.xₕᵢ_1
204 // z_1 = z_1ₗₒ + SHIFT_1 * z_1ₕᵢ
205 // z_2 = z_2ₗₒ + SHIFT_2 * z_1ₕᵢ
206 if (!(check_wide_limb_into_binary_limb_relation({ p_x_lo, p_x_hi }, p_x_binary_limbs) &&
207 check_wide_limb_into_binary_limb_relation({ p_y_lo, p_y_hi }, p_y_binary_limbs) &&
208 check_wide_limb_into_binary_limb_relation({ z_1 }, z_1_binary_limbs) &&
209 check_wide_limb_into_binary_limb_relation({ z_2 }, z_2_binary_limbs))) {
210
211 return report_fail("wide limb decomposition failied at row = ", i);
212 }
213
214 enum LimbSeriesType { STANDARD_COORDINATE, Z_SCALAR, QUOTIENT };
215
216 // Check that limbs have been decomposed into microlimbs correctly
217 // value = ∑ (2ˡ)ⁱ⋅ chunkᵢ, where 2ˡ is the shift
218 auto check_micro_limb_decomposition_correctness = [&accumulate_limb_from_micro_chunks](
219 const std::vector<Fr>& binary_limbs,
220 const std::vector<std::vector<Fr>>& micro_limbs,
221 const LimbSeriesType limb_series_type) {
222 // Shifts for decompositions
223 constexpr auto SHIFT_12_TO_14 = Fr(4);
224 constexpr auto SHIFT_10_TO_14 = Fr(16);
225 constexpr auto SHIFT_8_TO_14 = Fr(64);
226 constexpr auto SHIFT_4_TO_14 = Fr(1024);
227
228 BB_ASSERT_EQ(binary_limbs.size(), micro_limbs.size());
229 // First check that all the microlimbs are properly range constrained
230 for (auto& micro_limb_series : micro_limbs) {
231 for (auto& micro_limb : micro_limb_series) {
232 if (uint256_t(micro_limb) > Builder::MAX_MICRO_LIMB_SIZE) {
233 return false;
234 }
235 }
236 }
237 // For low limbs the last microlimb is used with the shift, so we skip it when reconstructing
238 // the limb
239 const size_t SKIPPED_FOR_LOW_LIMBS = 1;
240 for (size_t i = 0; i < binary_limbs.size() - 1; i++) {
241 if (binary_limbs[i] != accumulate_limb_from_micro_chunks(micro_limbs[i], SKIPPED_FOR_LOW_LIMBS)) {
242 return false;
243 }
244 // Check last additional constraint (68->70)
245 if (micro_limbs[i][NUM_MICRO_LIMBS - 1] != (SHIFT_12_TO_14 * micro_limbs[i][NUM_MICRO_LIMBS - 2])) {
246 return false;
247 }
248 }
249
250 const size_t SKIPPED_FOR_STANDARD = 2;
251 const size_t SKIPPED_FOR_Z_SCALARS = 1;
252 const size_t SKIPPED_FOR_QUOTIENT = 2;
253 switch (limb_series_type) {
254 case STANDARD_COORDINATE:
255 // For standard Fq value the highest limb is 50 bits, so we skip the top 2 microlimbs
256 if (binary_limbs[binary_limbs.size() - 1] !=
257 accumulate_limb_from_micro_chunks(micro_limbs[binary_limbs.size() - 1], SKIPPED_FOR_STANDARD)) {
258 return false;
259 }
260 // Check last additional constraint (50->56)
261 if (micro_limbs[binary_limbs.size() - 1][NUM_MICRO_LIMBS - SKIPPED_FOR_STANDARD] !=
262 (SHIFT_8_TO_14 *
263 micro_limbs[binary_limbs.size() - 1][NUM_MICRO_LIMBS - SKIPPED_FOR_STANDARD - 1])) {
264
265 return false;
266 }
267 break;
268 // For z top limbs we need as many microlimbs as for the low limbs
269 case Z_SCALAR:
270 if (binary_limbs[binary_limbs.size() - 1] !=
271 accumulate_limb_from_micro_chunks(micro_limbs[binary_limbs.size() - 1],
272 SKIPPED_FOR_Z_SCALARS)) {
273 return false;
274 }
275 // Check last additional constraint (60->70)
276 if (micro_limbs[binary_limbs.size() - 1][NUM_MICRO_LIMBS - SKIPPED_FOR_Z_SCALARS] !=
277 (SHIFT_4_TO_14 *
278 micro_limbs[binary_limbs.size() - 1][NUM_MICRO_LIMBS - SKIPPED_FOR_Z_SCALARS - 1])) {
279 return false;
280 }
281 break;
282 // Quotient also doesn't need the top 2
283 case QUOTIENT:
284 if (binary_limbs[binary_limbs.size() - 1] !=
285 accumulate_limb_from_micro_chunks(micro_limbs[binary_limbs.size() - 1], SKIPPED_FOR_QUOTIENT)) {
286 return false;
287 }
288 // Check last additional constraint (52->56)
289 if (micro_limbs[binary_limbs.size() - 1][NUM_MICRO_LIMBS - SKIPPED_FOR_QUOTIENT] !=
290 (SHIFT_10_TO_14 *
291 micro_limbs[binary_limbs.size() - 1][NUM_MICRO_LIMBS - SKIPPED_FOR_QUOTIENT - 1])) {
292 return false;
293 }
294 break;
295 default:
296 abort();
297 }
298
299 return true;
300 };
301 // Check all micro limb decompositions
302 if (!check_micro_limb_decomposition_correctness(p_x_binary_limbs, p_x_micro_chunks, STANDARD_COORDINATE)) {
303 return false;
304 }
305 if (!check_micro_limb_decomposition_correctness(p_y_binary_limbs, p_y_micro_chunks, STANDARD_COORDINATE)) {
306 return false;
307 }
308 if (!check_micro_limb_decomposition_correctness(z_1_binary_limbs, z_1_micro_chunks, Z_SCALAR)) {
309 return false;
310 }
311 if (!check_micro_limb_decomposition_correctness(z_2_binary_limbs, z_2_micro_chunks, Z_SCALAR)) {
312 return false;
313 }
314 if (!check_micro_limb_decomposition_correctness(
315 current_accumulator_binary_limbs, current_accumulator_micro_chunks, STANDARD_COORDINATE)) {
316 return false;
317 }
318 if (!check_micro_limb_decomposition_correctness(quotient_binary_limbs, quotient_micro_chunks, QUOTIENT)) {
319 return false;
320 }
321
322 // The logic we are trying to enforce is:
323 // current_accumulator = previous_accumulator ⋅ x + op_code + P.x ⋅ v + P.y ⋅ v² + z_1 ⋅ v³ + z_2 ⋅
324 // v⁴ mod Fq To ensure this we transform the relation into the form: previous_accumulator ⋅ x + op +
325 // P.x ⋅ v + P.y ⋅ v² + z_1 ⋅ v³ + z_2 ⋅ v⁴ - quotient ⋅ p - current_accumulator = 0 However, we
326 // don't have integers. Despite that, we can approximate integers for a certain range, if we know
327 // that there will not be any overflows. For now we set the range to 2²⁷² ⋅ r. We can evaluate the
328 // logic modulo 2²⁷² with range constraints and r is native.
329 //
330 // previous_accumulator ⋅ x + op + P.x ⋅ v + P.y ⋅ v² + z_1 ⋅ v³ + z_2 ⋅ v⁴ - quotient ⋅ p -
331 // current_accumulator = 0 =>
332 // 1. previous_accumulator ⋅ x + op + P.x ⋅ v + P.y ⋅ v² + z_1 ⋅ v³ + z_2 ⋅ v⁴ + quotient ⋅ (-p mod
333 // 2²⁷²) - current_accumulator = 0 mod 2²⁷²
334 // 2. previous_accumulator ⋅ x + op + P.x ⋅ v + P.y ⋅ v² + z_1 ⋅ v³ + z_2 ⋅ v⁴ - quotient ⋅ p -
335 // current_accumulator = 0 mod r
336 //
337 // The second relation is straightforward and easy to check. The first, not so much. We have to
338 // evaluate certain bit chunks of the equation and ensure that they are zero. For example, for the
339 // lowest limb it would be (inclusive ranges):
340 //
341 // previous_accumulator[0:67] ⋅ x[0:67] + op + P.x[0:67] ⋅ v[0:67] + P.y[0:67] ⋅ v²[0:67] +
342 // z_1[0:67] ⋅ v³[0:67] + z_2[0:67] ⋅ v⁴[0:67] + quotient[0:67] ⋅ (-p mod 2²⁷²)[0:67] -
343 // current_accumulator[0:67] = intermediate_value; (we don't take parts of op, because it's supposed
344 // to be between 0 and 3)
345 //
346 // We could check that this intermediate_value is equal to 0 mod 2⁶⁸ by dividing it by 2⁶⁸ and
347 // constraining it. For efficiency, we actually compute wider evaluations for 136 bits, which
348 // require us to also obtain and shift products of [68:135] by [0:67] and [0:67] by [68:135] bits.
349 // The result of division goes into the next evaluation (the same as a carry flag would)
350 // So the lowest wide limb is : (∑everything[0:67]⋅everything[0:67] +
351 // 2⁶⁸⋅(∑everything[0:67]⋅everything[68:135]))/ 2¹³⁶
352 //
353 // The high is:
354 // (low_limb + ∑everything[0:67]⋅everything[136:203] + ∑everything[68:135]⋅everything[68:135] +
355 // 2⁶⁸(∑everything[0:67]⋅everything[204:271] + ∑everything[68:135]⋅everything[136:203])) / 2¹³⁶
356 //
357 // We also limit computation on limbs of op, z_1 and z_2, since we know that op has only the lowest
358 // limb and z_1 and z_2 have only the two lowest limbs
359 constexpr std::array<Fr, 5> NEGATIVE_MODULUS_LIMBS = Builder::NEGATIVE_MODULUS_LIMBS;
360 const uint256_t SHIFT_1 = Builder::SHIFT_1;
361 const uint256_t SHIFT_2 = Builder::SHIFT_2;
362 const uint256_t SHIFT_3 = Builder::SHIFT_3;
363 Fr low_wide_limb_relation_check =
364
365 (previous_accumulator_binary_limbs[0] * relation_inputs.x_limbs[0] + op_code +
366 relation_inputs.v_limbs[0] * p_x_0 + relation_inputs.v_squared_limbs[0] * p_y_0 +
367 relation_inputs.v_cubed_limbs[0] * z_1_lo + relation_inputs.v_quarted_limbs[0] * z_2_lo +
368 quotient_binary_limbs[0] * NEGATIVE_MODULUS_LIMBS[0] - current_accumulator_binary_limbs[0]) +
369 (previous_accumulator_binary_limbs[1] * relation_inputs.x_limbs[0] +
370 relation_inputs.v_limbs[1] * p_x_0 + relation_inputs.v_squared_limbs[1] * p_y_0 +
371 relation_inputs.v_cubed_limbs[1] * z_1_lo + relation_inputs.v_quarted_limbs[1] * z_2_lo +
372 quotient_binary_limbs[1] * NEGATIVE_MODULUS_LIMBS[0] +
373 previous_accumulator_binary_limbs[0] * relation_inputs.x_limbs[1] +
374 relation_inputs.v_limbs[0] * p_x_1 + relation_inputs.v_squared_limbs[0] * p_y_1 +
375 relation_inputs.v_cubed_limbs[0] * z_1_hi + relation_inputs.v_quarted_limbs[0] * z_2_hi +
376 quotient_binary_limbs[0] * NEGATIVE_MODULUS_LIMBS[1] - current_accumulator_binary_limbs[1]) *
377 Fr(SHIFT_1);
378 if (low_wide_limb_relation_check != (low_wide_relation_limb * SHIFT_2)) {
379 return false;
380 }
381 Fr high_wide_relation_limb_check =
382 low_wide_relation_limb + previous_accumulator_binary_limbs[2] * relation_inputs.x_limbs[0] +
383 previous_accumulator_binary_limbs[1] * relation_inputs.x_limbs[1] +
384 previous_accumulator_binary_limbs[0] * relation_inputs.x_limbs[2] + relation_inputs.v_limbs[2] * p_x_0 +
385 relation_inputs.v_limbs[1] * p_x_1 + relation_inputs.v_limbs[0] * p_x_2 +
386 relation_inputs.v_squared_limbs[2] * p_y_0 + relation_inputs.v_squared_limbs[1] * p_y_1 +
387 relation_inputs.v_squared_limbs[0] * p_y_2 + relation_inputs.v_cubed_limbs[2] * z_1_lo +
388 relation_inputs.v_cubed_limbs[1] * z_1_hi + relation_inputs.v_quarted_limbs[2] * z_2_lo +
389 relation_inputs.v_quarted_limbs[1] * z_2_hi + quotient_binary_limbs[2] * NEGATIVE_MODULUS_LIMBS[0] +
390 quotient_binary_limbs[1] * NEGATIVE_MODULUS_LIMBS[1] +
391 quotient_binary_limbs[0] * NEGATIVE_MODULUS_LIMBS[2] - current_accumulator_binary_limbs[2] +
392 (previous_accumulator_binary_limbs[3] * relation_inputs.x_limbs[0] +
393 previous_accumulator_binary_limbs[2] * relation_inputs.x_limbs[1] +
394 previous_accumulator_binary_limbs[1] * relation_inputs.x_limbs[2] +
395 previous_accumulator_binary_limbs[0] * relation_inputs.x_limbs[3] +
396 relation_inputs.v_limbs[3] * p_x_0 + relation_inputs.v_limbs[2] * p_x_1 +
397 relation_inputs.v_limbs[1] * p_x_2 + relation_inputs.v_limbs[0] * p_x_3 +
398 relation_inputs.v_squared_limbs[3] * p_y_0 + relation_inputs.v_squared_limbs[2] * p_y_1 +
399 relation_inputs.v_squared_limbs[1] * p_y_2 + relation_inputs.v_squared_limbs[0] * p_y_3 +
400 relation_inputs.v_cubed_limbs[3] * z_1_lo + relation_inputs.v_cubed_limbs[2] * z_1_hi +
401 relation_inputs.v_quarted_limbs[3] * z_2_lo + relation_inputs.v_quarted_limbs[2] * z_2_hi +
402 quotient_binary_limbs[3] * NEGATIVE_MODULUS_LIMBS[0] +
403 quotient_binary_limbs[2] * NEGATIVE_MODULUS_LIMBS[1] +
404 quotient_binary_limbs[1] * NEGATIVE_MODULUS_LIMBS[2] +
405 quotient_binary_limbs[0] * NEGATIVE_MODULUS_LIMBS[3] - current_accumulator_binary_limbs[3]) *
406 SHIFT_1;
407 if (high_wide_relation_limb_check != (high_wide_relation_limb * SHIFT_2)) {
408 return false;
409 }
410 // Apart from checking the correctness of the evaluation modulo 2²⁷² we also need to ensure that the
411 // logic works in our scalar field. For this we reconstruct the scalar field values from individual
412 // limbs
413 auto reconstructed_p_x = (p_x_0 + p_x_1 * SHIFT_1 + p_x_2 * SHIFT_2 + p_x_3 * SHIFT_3);
414 auto reconstructed_p_y = (p_y_0 + p_y_1 * SHIFT_1 + p_y_2 * SHIFT_2 + p_y_3 * SHIFT_3);
415 auto reconstructed_current_accumulator =
416 (current_accumulator_binary_limbs[0] + current_accumulator_binary_limbs[1] * SHIFT_1 +
417 current_accumulator_binary_limbs[2] * SHIFT_2 + current_accumulator_binary_limbs[3] * SHIFT_3);
418 auto reconstructed_previous_accumulator =
419 (previous_accumulator_binary_limbs[0] + previous_accumulator_binary_limbs[1] * SHIFT_1 +
420 previous_accumulator_binary_limbs[2] * SHIFT_2 + previous_accumulator_binary_limbs[3] * SHIFT_3);
421
422 auto reconstructed_z1 = (z_1_lo + z_1_hi * SHIFT_1);
423 auto reconstructed_z2 = (z_2_lo + z_2_hi * SHIFT_1);
424 auto reconstructed_quotient = (quotient_binary_limbs[0] + quotient_binary_limbs[1] * SHIFT_1 +
425 quotient_binary_limbs[2] * SHIFT_2 + quotient_binary_limbs[3] * SHIFT_3);
426
427 // Check the relation
428 if (!(reconstructed_previous_accumulator * reconstructed_evaluation_input_x + op_code +
429 reconstructed_p_x * reconstructed_batching_evaluation_v +
430 reconstructed_p_y * reconstructed_batching_evaluation_v2 +
431 reconstructed_z1 * reconstructed_batching_evaluation_v3 +
432 reconstructed_z2 * reconstructed_batching_evaluation_v4 +
433 reconstructed_quotient * NEGATIVE_MODULUS_LIMBS[4] - reconstructed_current_accumulator)
434 .is_zero()) {
435 return false;
436 };
437 }
438 {
439 size_t odd_gate_index = i + 1;
440 // Check the accumulator is copied correctly
441 const std::vector current_accumulator_binary_limbs_copy = {
442 circuit.get_variable(accumulators_binary_limbs_0_wire[odd_gate_index]),
443 circuit.get_variable(accumulators_binary_limbs_1_wire[odd_gate_index]),
444 circuit.get_variable(accumulators_binary_limbs_2_wire[odd_gate_index]),
445 circuit.get_variable(accumulators_binary_limbs_3_wire[odd_gate_index]),
446 };
447 if (odd_gate_index < circuit.num_gates - 1) {
448 size_t next_even_gate_index = i + 2;
449 const std::vector current_accumulator_binary_limbs = {
450 circuit.get_variable(accumulators_binary_limbs_0_wire[next_even_gate_index]),
451 circuit.get_variable(accumulators_binary_limbs_1_wire[next_even_gate_index]),
452 circuit.get_variable(accumulators_binary_limbs_2_wire[next_even_gate_index]),
453 circuit.get_variable(accumulators_binary_limbs_3_wire[next_even_gate_index]),
454 };
455
456 for (size_t j = 0; j < current_accumulator_binary_limbs.size(); j++) {
457 if (current_accumulator_binary_limbs_copy[j] != current_accumulator_binary_limbs[j]) {
458 return report_fail("accumulator copy failed at row = ", odd_gate_index);
459 }
460 }
461 } else {
462 // Check accumulator starts at zero
463 for (const auto& limb : current_accumulator_binary_limbs_copy) {
464 if (limb != Fr(0)) {
465 return report_fail("accumulator doesn't start with 0 = ", odd_gate_index);
466 }
467 }
468 }
469 }
470 }
471 return true;
472};
473}; // namespace bb
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:59
FF get_variable(const uint32_t index) const
Get the value of the variable v_{index}.
TranslatorCircuitBuilder creates a circuit that evaluates the correctness of the evaluation of EccOpQ...
static constexpr std::array< Fr, 5 > NEGATIVE_MODULUS_LIMBS
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.
std::array< SlabVector< uint32_t >, NUM_WIRES > wires
WireIds
There are so many wires that naming them has no sense, it is easier to access them with enums.
static RelationInputs compute_relation_inputs_limbs(const Fq &batching_challenge_v, const Fq &evaluation_input_x)
Create limb representations of x and powers of v that are needed to compute the witness or check circ...
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
void info(Args... args)
Definition log.hpp:70
Entry point for Barretenberg command-line interface.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept