Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecc_set_relation_impl.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Raju], commit: 2a49eb6 }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
10#include <type_traits>
11
12namespace bb {
13
69template <typename FF>
70template <typename Accumulator, typename AllEntities, typename Parameters>
71Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_numerator(const AllEntities& in, const Parameters& params)
72{
73 using View = typename Accumulator::View;
74
75 const auto& precompute_round = View(in.precompute_round);
76 const auto precompute_round2 = precompute_round + precompute_round;
77 const auto precompute_round4 = precompute_round2 + precompute_round2;
78
79 const auto& gamma = params.gamma;
80 const auto& beta = params.beta;
81 const auto& beta_sqr = params.beta_sqr;
82 const auto& beta_cube = params.beta_cube;
83 const auto& beta_quartic = params.beta_quartic;
84 const auto& precompute_pc = View(in.precompute_pc);
85 const auto& precompute_select = View(in.precompute_select);
86
87 // Domain separation: each tuple family includes a distinct tag * beta^4 term to prevent cross-family collisions.
88 const auto first_term_tag = beta_quartic * FIRST_TERM_TAG;
89 const auto second_term_tag = beta_quartic * SECOND_TERM_TAG;
90 const auto third_term_tag = beta_quartic * THIRD_TERM_TAG;
91
104 // OPTIMIZE(@zac-williamson #2226) optimize degrees
105
106 Accumulator numerator(1); // degree-0
107 {
108 const auto& s0 = View(in.precompute_s1hi);
109 const auto& s1 = View(in.precompute_s1lo);
110
111 auto wnaf_slice = s0 + s0;
112 wnaf_slice += wnaf_slice;
113 wnaf_slice += s1;
114
115 const auto wnaf_slice_input0 =
116 wnaf_slice + gamma + precompute_pc * beta + precompute_round4 * beta_sqr + first_term_tag;
117 numerator *= wnaf_slice_input0; // degree-1
118 }
119 {
120 const auto& s0 = View(in.precompute_s2hi);
121 const auto& s1 = View(in.precompute_s2lo);
122
123 auto wnaf_slice = s0 + s0;
124 wnaf_slice += wnaf_slice;
125 wnaf_slice += s1;
126
127 const auto wnaf_slice_input1 =
128 wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 1) * beta_sqr + first_term_tag;
129 numerator *= wnaf_slice_input1; // degree-2
130 }
131 {
132 const auto& s0 = View(in.precompute_s3hi);
133 const auto& s1 = View(in.precompute_s3lo);
134
135 auto wnaf_slice = s0 + s0;
136 wnaf_slice += wnaf_slice;
137 wnaf_slice += s1;
138
139 const auto wnaf_slice_input2 =
140 wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 2) * beta_sqr + first_term_tag;
141 numerator *= wnaf_slice_input2; // degree-3
142 }
143 {
144 const auto& s0 = View(in.precompute_s4hi);
145 const auto& s1 = View(in.precompute_s4lo);
146
147 auto wnaf_slice = s0 + s0;
148 wnaf_slice += wnaf_slice;
149 wnaf_slice += s1;
150 const auto wnaf_slice_input3 =
151 wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 3) * beta_sqr + first_term_tag;
152 numerator *= wnaf_slice_input3; // degree-4
153 }
154 {
155 // skew product if relevant
156 const auto& skew = View(in.precompute_skew);
157 const auto& precompute_point_transition = View(in.precompute_point_transition);
158 const auto skew_input = precompute_point_transition * (skew + gamma + precompute_pc * beta +
159 (precompute_round4 + 4) * beta_sqr + first_term_tag) +
160 (-precompute_point_transition + 1);
161 numerator *= skew_input; // degree-5
162 }
163 {
164 // in `EccvmProver` and `ECCVMVerifier`, we see that `eccvm_set_permutation_delta` is initially computed as
165 // (γ+t·β⁴)·(γ+β²+t·β⁴)·(γ+2β²+t·β⁴)·(γ+3β²+t·β⁴) (where t = FIRST_TERM_TAG) and _then_ inverted.
166 const auto& eccvm_set_permutation_delta = params.eccvm_set_permutation_delta;
167 // if `precompute_select == 1`, don't change the numerator. if it is 0, then to get the grand product argument
168 // to work (as we have zero-padded the rows of the MSM table), we must multiply by the inverse of the
169 // fingerprint of (0, 0, 0).
170 numerator *= precompute_select * (-eccvm_set_permutation_delta + 1) + eccvm_set_permutation_delta; // degree-7
171 }
172
186 {
187 const auto& table_x = View(in.precompute_tx);
188 const auto& table_y = View(in.precompute_ty);
189
190 const auto& precompute_skew = View(in.precompute_skew);
191 const auto negative_inverse_seven = []() {
192 if constexpr (std::same_as<FF, grumpkin::fr>) {
193 static constexpr FF negative_inverse_seven = FF(-7).invert();
194 return negative_inverse_seven;
195 } else {
196 FF negative_inverse_seven = FF(-7).invert();
197 return negative_inverse_seven;
198 }
199 };
200 auto adjusted_skew =
201 precompute_skew * negative_inverse_seven(); // `precompute_skew` ∈ {0, 7}, `adjusted_skew`∈ {0, -1}
202
203 const auto& wnaf_scalar_sum = View(in.precompute_scalar_sum);
204 const auto w0 = convert_to_wnaf<Accumulator>(View(in.precompute_s1hi), View(in.precompute_s1lo));
205 const auto w1 = convert_to_wnaf<Accumulator>(View(in.precompute_s2hi), View(in.precompute_s2lo));
206 const auto w2 = convert_to_wnaf<Accumulator>(View(in.precompute_s3hi), View(in.precompute_s3lo));
207 const auto w3 = convert_to_wnaf<Accumulator>(View(in.precompute_s4hi), View(in.precompute_s4lo));
208
209 auto row_slice = w0;
210 row_slice += row_slice;
211 row_slice += row_slice;
212 row_slice += row_slice;
213 row_slice += row_slice;
214 row_slice += w1;
215 row_slice += row_slice;
216 row_slice += row_slice;
217 row_slice += row_slice;
218 row_slice += row_slice;
219 row_slice += w2;
220 row_slice += row_slice;
221 row_slice += row_slice;
222 row_slice += row_slice;
223 row_slice += row_slice;
224 row_slice += w3; // row_slice = 2^12 w_0 + 2^8 w_1 + 2^4 w_2 + 2^0 w_3
225
226 auto scalar_sum_full = wnaf_scalar_sum + wnaf_scalar_sum;
227 scalar_sum_full += scalar_sum_full;
228 scalar_sum_full += scalar_sum_full;
229 scalar_sum_full += scalar_sum_full;
230 scalar_sum_full += scalar_sum_full;
231 scalar_sum_full += scalar_sum_full;
232 scalar_sum_full += scalar_sum_full;
233 scalar_sum_full += scalar_sum_full;
234 scalar_sum_full += scalar_sum_full;
235 scalar_sum_full += scalar_sum_full;
236 scalar_sum_full += scalar_sum_full;
237 scalar_sum_full += scalar_sum_full;
238 scalar_sum_full += scalar_sum_full;
239 scalar_sum_full += scalar_sum_full;
240 scalar_sum_full += scalar_sum_full;
241 scalar_sum_full += scalar_sum_full;
242 scalar_sum_full +=
243 row_slice + adjusted_skew; // scalar_sum_full = 2^16 * wnaf_scalar_sum + row_slice + adjusted_skew
244
245 auto precompute_point_transition = View(in.precompute_point_transition);
246
247 auto point_table_init_read =
248 (precompute_pc + table_x * beta + table_y * beta_sqr + scalar_sum_full * beta_cube + second_term_tag);
249 point_table_init_read =
250 precompute_point_transition * (point_table_init_read + gamma) + (-precompute_point_transition + 1);
251
252 numerator *= point_table_init_read; // degree-9
253 }
271 {
272 const auto& lagrange_first = View(in.lagrange_first);
273 const auto& partial_msm_transition_shift = View(in.msm_transition_shift);
274 const auto msm_transition_shift = (-lagrange_first + 1) * partial_msm_transition_shift;
275 const auto& msm_pc_shift = View(in.msm_pc_shift);
276
277 const auto& msm_x_shift = View(in.msm_accumulator_x_shift);
278 const auto& msm_y_shift = View(in.msm_accumulator_y_shift);
279 const auto& msm_size = View(in.msm_size_of_msm);
280
281 // msm_transition = 1 when a row BEGINS a new msm
282 //
283 // row msm tx acc.x acc.y pc msm_size
284 // i 0 no no no yes
285 // i+1 1 yes yes yes no
286 //
287 // at row i we are at the final row of the current msm
288 // at row i the value of `msm_size` = size of current msm
289 // at row i + 1 we have the final accumulated value of the msm computation
290 // at row i + 1 we have updated `pc` to be `(pc at start of msm) + msm_count`
291 // at row i + 1 q_msm_transtiion = 1
292
293 auto msm_result_write =
294 msm_pc_shift + msm_x_shift * beta + msm_y_shift * beta_sqr + msm_size * beta_cube + third_term_tag;
295
296 // msm_result_write = degree 2
297 msm_result_write = msm_transition_shift * (msm_result_write + gamma) + (-msm_transition_shift + 1);
298 numerator *= msm_result_write; // degree-11
299 }
300 return numerator;
301}
302
303template <typename FF>
304template <typename Accumulator, typename AllEntities, typename Parameters>
305Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_denominator(const AllEntities& in, const Parameters& params)
306{
307 using View = typename Accumulator::View;
308
309 // OPTIMIZE(@zac-williamson). The degree of this contribution is 17! makes overall relation degree 19.
310 // Can potentially optimize by refining the algebra.
311 const auto& gamma = params.gamma;
312 const auto& beta = params.beta;
313 const auto& beta_sqr = params.beta_sqr;
314 const auto& beta_cube = params.beta_cube;
315 const auto& beta_quartic = params.beta_quartic;
316 const auto& msm_pc = View(in.msm_pc);
317 const auto& msm_count = View(in.msm_count);
318 const auto& msm_round = View(in.msm_round);
319
320 // Domain separation: must match the tags used in the numerator.
321 const auto first_term_tag = beta_quartic * FIRST_TERM_TAG;
322 const auto second_term_tag = beta_quartic * SECOND_TERM_TAG;
323 const auto third_term_tag = beta_quartic * THIRD_TERM_TAG;
324
330 Accumulator denominator(1); // degree-0
331 {
332 const auto& add1 = View(in.msm_add1);
333 const auto& msm_slice1 = View(in.msm_slice1);
334
335 auto wnaf_slice_output1 =
336 add1 * (msm_slice1 + gamma + (msm_pc - msm_count) * beta + msm_round * beta_sqr + first_term_tag) +
337 (-add1 + 1);
338 denominator *= wnaf_slice_output1; // degree-2
339 }
340 {
341 const auto& add2 = View(in.msm_add2);
342 const auto& msm_slice2 = View(in.msm_slice2);
343
344 auto wnaf_slice_output2 =
345 add2 * (msm_slice2 + gamma + (msm_pc - msm_count - 1) * beta + msm_round * beta_sqr + first_term_tag) +
346 (-add2 + 1);
347 denominator *= wnaf_slice_output2; // degree-4
348 }
349 {
350 const auto& add3 = View(in.msm_add3);
351 const auto& msm_slice3 = View(in.msm_slice3);
352
353 auto wnaf_slice_output3 =
354 add3 * (msm_slice3 + gamma + (msm_pc - msm_count - 2) * beta + msm_round * beta_sqr + first_term_tag) +
355 (-add3 + 1);
356 denominator *= wnaf_slice_output3; // degree-6
357 }
358 {
359 const auto& add4 = View(in.msm_add4);
360 const auto& msm_slice4 = View(in.msm_slice4);
361 auto wnaf_slice_output4 =
362 add4 * (msm_slice4 + gamma + (msm_pc - msm_count - 3) * beta + msm_round * beta_sqr + first_term_tag) +
363 (-add4 + 1);
364 denominator *= wnaf_slice_output4; // degree-8
365 }
366
377 {
378 const auto& transcript_pc = View(in.transcript_pc);
379
380 const auto& transcript_Px = View(in.transcript_Px);
381 const auto& transcript_Py = View(in.transcript_Py);
382 const auto& z1 = View(in.transcript_z1);
383 const auto& z2 = View(in.transcript_z2);
384 const auto& z1_zero = View(in.transcript_z1zero);
385 const auto& z2_zero = View(in.transcript_z2zero);
386 const auto& base_infinity = View(in.transcript_base_infinity);
387 const auto& transcript_mul = View(in.transcript_mul);
388
389 const auto& lookup_first = (-z1_zero + 1);
390 const auto& lookup_second = (-z2_zero + 1);
391 FF cube_root_unity = FF(bb::fq::cube_root_of_unity());
392
393 auto transcript_input1 = transcript_pc + transcript_Px * beta + transcript_Py * beta_sqr + z1 * beta_cube +
394 second_term_tag; // degree = 1
395 auto transcript_input2 = (transcript_pc - lookup_first) + transcript_Px * cube_root_unity * beta -
396 transcript_Py * beta_sqr + z2 * beta_cube + second_term_tag; // degree = 2
397
398 // The following diagram expresses a fingerprint of part of the tuple. It does not include `transcript_pc` and
399 // has not weighted the X and Y with beta and beta_sqr respectively. The point is nonetheless to show exactly
400 // when a tuple is added to the multiset: iff it corresponds to a non-trivial (128-bit) scalar mul. If neither
401 // z1 nor z2 are zero, then we implicitly add _two_ tuples to the multiset.
402 //
403 // | q_mul | z2_zero | z1_zero | base_infinity | partial lookup |
404 // | ----- | ------- | ------- | ------------- |----------------------- |
405 // | 0 | - | - | - | 1 |
406 // | 1 | 0 | 0 | 0 | 1 |
407 // | 1 | 0 | 1 | 0 | X + gamma |
408 // | 1 | 1 | 0 | 0 | Y + gamma |
409 // | 1 | 1 | 1 | 0 | (X + gamma)(Y + gamma) |
410 // | 1 | 0 | 0 | 1 | 1 |
411 // | 1 | 0 | 1 | 1 | 1 |
412 // | 1 | 1 | 0 | 1 | 1 |
413 // | 1 | 1 | 1 | 1 | 1 |
414 transcript_input1 = (transcript_input1 + gamma) * lookup_first + (-lookup_first + 1); // degree 2
415 transcript_input2 = (transcript_input2 + gamma) * lookup_second + (-lookup_second + 1); // degree 3
416
417 // transcript_product = degree 6
418 auto transcript_product = (transcript_input1 * transcript_input2) * (-base_infinity + 1) + base_infinity;
419
420 // point_table_init_write = degree 7
421 auto point_table_init_write = transcript_mul * transcript_product + (-transcript_mul + 1);
422 denominator *= point_table_init_write; // degree 17
423 }
439 {
440 const auto& transcript_pc_shift = View(in.transcript_pc_shift);
441 const auto& transcript_msm_x = View(in.transcript_msm_x);
442 const auto& transcript_msm_y = View(in.transcript_msm_y);
443 const auto& transcript_msm_transition = View(in.transcript_msm_transition);
444 const auto& transcript_msm_count = View(in.transcript_msm_count);
445 const auto& z1_zero = View(in.transcript_z1zero);
446 const auto& z2_zero = View(in.transcript_z2zero);
447 const auto& transcript_mul = View(in.transcript_mul);
448 const auto& base_infinity = View(in.transcript_base_infinity);
449
450 // do not add to count if point at infinity!
451 auto full_msm_count =
452 transcript_msm_count + transcript_mul * ((-z1_zero + 1) + (-z2_zero + 1)) * (-base_infinity + 1);
453 // msm_result_read = degree 2
454 auto msm_result_read = transcript_pc_shift + transcript_msm_x * beta + transcript_msm_y * beta_sqr +
455 full_msm_count * beta_cube + third_term_tag;
456 msm_result_read = transcript_msm_transition * (msm_result_read + gamma) + (-transcript_msm_transition + 1);
457 denominator *= msm_result_read; // degree-20
458 }
459 return denominator;
460}
461
472template <typename FF>
473template <typename ContainerOverSubrelations, typename AllEntities, typename Parameters>
474void ECCVMSetRelationImpl<FF>::accumulate(ContainerOverSubrelations& accumulator,
475 const AllEntities& in,
476 const Parameters& params,
477 const FF& scaling_factor)
478{
479 using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>;
480 using View = typename Accumulator::View;
481 using ShortView = typename std::tuple_element_t<1, ContainerOverSubrelations>::View;
482
483 // degree-11
484 Accumulator numerator_evaluation = compute_grand_product_numerator<Accumulator>(in, params);
485
486 // degree-20
487 Accumulator denominator_evaluation = compute_grand_product_denominator<Accumulator>(in, params);
488
489 const auto& lagrange_first = View(in.lagrange_first);
490 const auto& lagrange_last = View(in.lagrange_last);
491 const auto& lagrange_last_short = ShortView(in.lagrange_last);
492
493 const auto& z_perm = View(in.z_perm);
494 const auto& z_perm_shift = View(in.z_perm_shift);
495 const auto& z_perm_shift_short = ShortView(in.z_perm_shift);
496
497 // degree-21
498 std::get<0>(accumulator) +=
499 ((z_perm + lagrange_first) * numerator_evaluation - (z_perm_shift + lagrange_last) * denominator_evaluation) *
500 scaling_factor;
501
502 // Contribution (2)
503 std::get<1>(accumulator) += lagrange_last_short * z_perm_shift_short * scaling_factor;
504}
505} // namespace bb
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
static Accumulator compute_grand_product_denominator(const AllEntities &in, const Parameters &params)
static Accumulator compute_grand_product_numerator(const AllEntities &in, const Parameters &params)
Performs multiset equality checks for the ECCVM. This faciliates "communication" between disjoint set...
static void accumulate(ContainerOverSubrelations &accumulator, const AllEntities &in, const Parameters &params, const FF &scaling_factor)
Expression for the standard arithmetic gate. @dbetails The relation is defined as C(in(X)....
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr field cube_root_of_unity()
constexpr field invert() const noexcept