1212namespace bb {
1313
1414/* *
15- * @brief Performs list-equivalence checks for the ECCVM
15+ * @brief Performs multiset equality checks for the ECCVM. This faciliates "communication" between disjoint sets of
16+ * columns, which we view as tables: the Precomputed table, the MSM table, and the Transcript table. This used to be
17+ * called a strict lookup argument (where every element written was read _exactly_ once.)
1618 *
17- * @details ECCVMSetRelationImpl validates the correctness of the inputs/outputs of the three main algorithms evaluated
18- * by the ECCVM.
19+ * @details ECCVMSetRelationImpl validates the correctness of the "inputs"/"outputs" of the three main algorithms
20+ * evaluated by the ECCVM. Note that the terminology of "inputs" and "outputs" is _purely psychological_; they each just
21+ * name the multiset we are adding to.
22+ *
23+ * It will be helpful to recall that `pc` always stands for point-counter. We use the terms interchangably.
24+ *
25+ * FIRST TERM: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices, as part of
26+ * ECCVMWnafRelation.
1927 *
20- * First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices,
21- * as part of ECCVMWnafRelation
2228 * Input source: ECCVMWnafRelation
2329 * Output source: ECCVMMSMRelation
2430 *
2531 *
26- * Second term: tuple of (point-counter, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and
27- * ECCVMPointTableRelation
32+ *
33+ * SECOND TERM: tuple of (pc, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation.
34+ *
2835 * Input source: ECCVMPointTableRelation
29- * Output source: ECCVMMSMRelation
36+ * Output source: ECCVMTranscriptRelation
37+ *
38+ * Note that, from the latter table, this is only turned on when we are at an MSM transition, so we don't record the
39+ * "intermediate" `transcript_pc` values from the Transcript columns. This is compatible with the fact that the `msm_pc`
40+ * values are _constant_ on a fixed MSM.
41+ *
42+ * THIRD TERM: tuple of (pc, P.x, P.y, msm-size) from ECCVMMSMRelation, to link the output of the MSM computation from
43+ * the MSM table to the values in the Transcript tables.
3044 *
31- * Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMMSMRelation
3245 * Input source: ECCVMMSMRelation
3346 * Output source: ECCVMTranscriptRelation
3447 *
@@ -57,11 +70,19 @@ Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_numerator(const AllE
5770 const auto & precompute_select = View (in.precompute_select );
5871
5972 /* *
60- * @brief First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices,
61- * as part of ECCVMWnafRelation.
62- * If precompute_select = 1, tuple entry = (wnaf-slice + point-counter * beta + msm-round * beta_sqr).
63- * There are 4 tuple entries per row.
73+ * @brief First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices, as
74+ * part of ECCVMWnafRelation.
75+ *
76+ * @details
77+ * There are 4 tuple entries per row of the Precompute table. Moreover, the element that "increments" is
78+ * 4 * `precompute_round`, due to the fact that the Precompute columns contain four "digits"/slices per row.
79+ *
80+ * @note
81+ * We only add this tuple if `precompute_select == 1`. Otherwise, we add a the tuple (0, 0, 0).
6482 */
83+
84+ // OPTIMIZE(@zac-williamson #2226) optimize degrees
85+
6586 Accumulator numerator (1 ); // degree-0
6687 {
6788 const auto & s0 = View (in.precompute_s1hi );
@@ -71,7 +92,6 @@ Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_numerator(const AllE
7192 wnaf_slice += wnaf_slice;
7293 wnaf_slice += s1;
7394
74- // TODO(@zac-williamson #2226) optimize
7595 const auto wnaf_slice_input0 = wnaf_slice + gamma + precompute_pc * beta + precompute_round4 * beta_sqr;
7696 numerator *= wnaf_slice_input0; // degree-1
7797 }
@@ -83,7 +103,6 @@ Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_numerator(const AllE
83103 wnaf_slice += wnaf_slice;
84104 wnaf_slice += s1;
85105
86- // TODO(@zac-williamson #2226) optimize
87106 const auto wnaf_slice_input1 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 1 ) * beta_sqr;
88107 numerator *= wnaf_slice_input1; // degree-2
89108 }
@@ -95,7 +114,6 @@ Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_numerator(const AllE
95114 wnaf_slice += wnaf_slice;
96115 wnaf_slice += s1;
97116
98- // TODO(@zac-williamson #2226) optimize
99117 const auto wnaf_slice_input2 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 2 ) * beta_sqr;
100118 numerator *= wnaf_slice_input2; // degree-3
101119 }
@@ -106,7 +124,6 @@ Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_numerator(const AllE
106124 auto wnaf_slice = s0 + s0;
107125 wnaf_slice += wnaf_slice;
108126 wnaf_slice += s1;
109- // TODO(@zac-williamson #2226) optimize
110127 const auto wnaf_slice_input3 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 3 ) * beta_sqr;
111128 numerator *= wnaf_slice_input3; // degree-4
112129 }
@@ -121,15 +138,24 @@ Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_numerator(const AllE
121138 }
122139 {
123140 const auto & eccvm_set_permutation_delta = params.eccvm_set_permutation_delta ;
141+ // if `precompute_select == 1`, don't change the numerator. if it is 0, then to get the grand product argument
142+ // to work (as we have zero-padded the rows of the MSM table), we must multiply by
143+ // `eccvm_set_permutation_delta` == (γ)·(γ + β²)·(γ + 2β²)·(γ + 3β²)
124144 numerator *= precompute_select * (-eccvm_set_permutation_delta + 1 ) + eccvm_set_permutation_delta; // degree-7
125145 }
126146
127147 /* *
128- * @brief Second term: tuple of (point-counter, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and
129- * ECCVMPointTableRelation. ECCVMWnafRelation validates the sum of the wnaf slices associated with point-counter
148+ * @brief Second term: tuple of (pc, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and
149+ * ECCVMPointTableRelation.
150+ *
151+ * @details
152+ * ECCVMWnafRelation validates the sum of the wnaf slices associated with point-counter
130153 * equals scalar-multiplier. ECCVMPointTableRelation computes a table of muliples of [P]: { -15[P], -13[P], ...,
131- * 15[P] }. We need to validate that scalar-multiplier and [P] = (P.x, P.y) come from MUL opcodes in the transcript
132- * columns.
154+ * 15[P] }. We need to validate that the scalar-multiplier and [P] = (P.x, P.y) come from MUL opcodes in the
155+ * transcript columns; in other words, that the wNAF expansion of the scalar-multiplier is correct.
156+ *
157+ * @note
158+ * We only add the tuple to the multiset if `precompute_point_transition == 1`.
133159 */
134160 {
135161 const auto & table_x = View (in.precompute_tx );
@@ -200,16 +226,19 @@ Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_numerator(const AllE
200226 numerator *= point_table_init_read; // degree-9
201227 }
202228 /* *
203- * @brief Third term: tuple of (point-counter , P.x, P.y, msm-size) from ECCVMMSMRelation.
229+ * @brief Third term: tuple of (pc , P.x, P.y, msm-size) from ECCVMMSMRelation.
204230 * (P.x, P.y) is the output of a multi-scalar-multiplication evaluated in ECCVMMSMRelation.
205231 * We need to validate that the same values (P.x, P.y) are present in the Transcript columns and describe a
206- * multi-scalar multiplication of size `msm-size`, starting at `point-counter `.
232+ * multi-scalar multiplication of size `msm-size`, starting at `pc `.
207233 *
208- * If msm_transition_shift = 1, this indicates the current row is the last row of a multiscalar
209- * multiplication evaluation. The output of the MSM will be present on `(msm_accumulator_x_shift,
210- * msm_accumulator_y_shift)`. The values of `msm_accumulator_x_shift, msm_accumulator_y_shift, msm_pc,
211- * msm_size_of_msm` must match up with equivalent values `transcript_msm_output_x, transcript_msm_output_y,
212- * transcript_pc, transcript_msm_count` present in the Transcript columns
234+ * If `msm_transition_shift == 1`, this indicates the current row is the last row of a multiscalar
235+ * multiplication evaluation. The output of the MSM will be present on `(msm_accumulator_x_shift,
236+ * msm_accumulator_y_shift)`. The values of `msm_accumulator_x_shift, msm_accumulator_y_shift, msm_pc,
237+ * msm_size_of_msm` must match up with equivalent values `transcript_msm_output_x, transcript_msm_output_y,
238+ * transcript_pc, transcript_msm_count` present in the Transcript columns.
239+ *
240+ * Checking `msm_size` is correct (it is tied to the `pc`) is necessary to make sure the `msm_pc` increments
241+ * correctly after it completes an MSM.
213242 */
214243 {
215244 const auto & lagrange_first = View (in.lagrange_first );
@@ -248,8 +277,8 @@ Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_denominator(const Al
248277{
249278 using View = typename Accumulator::View;
250279
251- // TODO (@zac-williamson). The degree of this contribution is 17! makes overall relation degree 19.
252- // Can optimize by refining the algebra, once we have a stable base to iterate off of .
280+ // OPTIMIZE (@zac-williamson). The degree of this contribution is 17! makes overall relation degree 19.
281+ // Can potentially optimize by refining the algebra.
253282 const auto & gamma = params.gamma ;
254283 const auto & beta = params.beta ;
255284 const auto & beta_sqr = params.beta_sqr ;
@@ -297,43 +326,52 @@ Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_denominator(const Al
297326 }
298327
299328 /* *
300- * @brief Second term: tuple of (transcript_pc, transcript_Px, transcript_Py, z1) OR (transcript_pc, \lambda *
301- * transcript_Px, -transcript_Py, z2) for each scalar multiplication in ECCVMTranscriptRelation columns. (the latter
302- * term uses the curve endomorphism: \lambda = cube root of unity) . These values must be equivalent to the second
329+ * @brief Second term: tuple of the form ` (transcript_pc, transcript_Px, transcript_Py, z1)` OR ` (transcript_pc,
330+ * \beta * transcript_Px, -transcript_Py, z2)` for each scalar multiplication in ECCVMTranscriptRelation columns.
331+ * Here \f$\beta\f$ is a cube root of unity in \f$\mathbb f_q\f$ . These values must be equivalent to the second
303332 * term values in `compute_grand_product_numerator`
333+ *
334+ * @details
335+ * Recall that every element of \f$\mathbb F_r\f$ may be written as \f$z_1 + \zeta z_2 = z_1 - \beta z_2\f$, where
336+ * the \f$z_i\f$ are 128 bit numbers and \f$\zeta = -\beta\f$ is a sixth root of unity.
304337 */
305338 {
306339 const auto & transcript_pc = View (in.transcript_pc );
307340
308- auto transcript_Px = View (in.transcript_Px );
309- auto transcript_Py = View (in.transcript_Py );
310- auto z1 = View (in.transcript_z1 );
311- auto z2 = View (in.transcript_z2 );
312- auto z1_zero = View (in.transcript_z1zero );
313- auto z2_zero = View (in.transcript_z2zero );
314- auto base_infinity = View (in.transcript_base_infinity );
315- auto transcript_mul = View (in.transcript_mul );
341+ const auto & transcript_Px = View (in.transcript_Px );
342+ const auto & transcript_Py = View (in.transcript_Py );
343+ const auto & z1 = View (in.transcript_z1 );
344+ const auto & z2 = View (in.transcript_z2 );
345+ const auto & z1_zero = View (in.transcript_z1zero );
346+ const auto & z2_zero = View (in.transcript_z2zero );
347+ const auto & base_infinity = View (in.transcript_base_infinity );
348+ const auto & transcript_mul = View (in.transcript_mul );
316349
317- auto lookup_first = (-z1_zero + 1 );
318- auto lookup_second = (-z2_zero + 1 );
319- FF endomorphism_base_field_shift = FF (bb::fq::cube_root_of_unity ());
350+ const auto & lookup_first = (-z1_zero + 1 );
351+ const auto & lookup_second = (-z2_zero + 1 );
352+ FF cube_root_unity = FF (bb::fq::cube_root_of_unity ());
320353
321354 auto transcript_input1 =
322355 transcript_pc + transcript_Px * beta + transcript_Py * beta_sqr + z1 * beta_cube; // degree = 1
323- auto transcript_input2 = (transcript_pc - 1 ) + transcript_Px * endomorphism_base_field_shift * beta -
356+ auto transcript_input2 = (transcript_pc - 1 ) + transcript_Px * cube_root_unity * beta -
324357 transcript_Py * beta_sqr + z2 * beta_cube; // degree = 2
325358
326- // | q_mul | z2_zero | z1_zero | base_infinity | lookup |
327- // | ----- | ------- | ------- | ------------- |----------------------- |
328- // | 0 | - | - | - | 1 |
329- // | 1 | 0 | 0 | 0 | 1 |
330- // | 1 | 0 | 1 | 0 | X + gamma |
331- // | 1 | 1 | 0 | 0 | Y + gamma |
332- // | 1 | 1 | 1 | 0 | (X + gamma)(Y + gamma) |
333- // | 1 | 0 | 0 | 1 | 1 |
334- // | 1 | 0 | 1 | 1 | 1 |
335- // | 1 | 1 | 0 | 1 | 1 |
336- // | 1 | 1 | 1 | 1 | 1 |
359+ // The following diagram expresses a fingerprint of part of the tuple. It does not include `transcript_pc` and
360+ // has not weighted the X and Y with beta and beta_sqr respectively. The point is nonetheless to show exactly
361+ // when a tuple is added to the multiset: iff it corresponds to a non-trivial (128-bit) scalar mul. If neither
362+ // z1 nor z2 are zero, then we implicitly add _two_ tuples to the multiset.
363+ //
364+ // | q_mul | z2_zero | z1_zero | base_infinity | partial lookup |
365+ // | ----- | ------- | ------- | ------------- |----------------------- |
366+ // | 0 | - | - | - | 1 |
367+ // | 1 | 0 | 0 | 0 | 1 |
368+ // | 1 | 0 | 1 | 0 | X + gamma |
369+ // | 1 | 1 | 0 | 0 | Y + gamma |
370+ // | 1 | 1 | 1 | 0 | (X + gamma)(Y + gamma) |
371+ // | 1 | 0 | 0 | 1 | 1 |
372+ // | 1 | 0 | 1 | 1 | 1 |
373+ // | 1 | 1 | 0 | 1 | 1 |
374+ // | 1 | 1 | 1 | 1 | 1 |
337375 transcript_input1 = (transcript_input1 + gamma) * lookup_first + (-lookup_first + 1 ); // degree 2
338376 transcript_input2 = (transcript_input2 + gamma) * lookup_second + (-lookup_second + 1 ); // degree 3
339377
@@ -348,19 +386,22 @@ Accumulator ECCVMSetRelationImpl<FF>::compute_grand_product_denominator(const Al
348386 * @brief Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMTranscriptRelation.
349387 * (P.x, P.y) is the *claimed* output of a multi-scalar-multiplication evaluated in ECCVMMSMRelation.
350388 * We need to validate that the msm output produced in ECCVMMSMRelation is equivalent to the output present
351- * in `transcript_msm_output_x, transcript_msm_output_y`, for a given multi-scalar multiplication starting at
352- * `transcript_pc` and has size `transcript_msm_count`
389+ * in `transcript_msm_output_x, transcript_msm_output_y`, for a given multi-scalar multiplication starting at
390+ * `transcript_pc` and has size `transcript_msm_count`.
391+ * @note In the case of an honest prover, `(transcript_msm_output_x, transcript_msm_output_y)` is the value of the
392+ * just-completed MSM + `OFFSET` (as this is what the MSM table computes with to avoid branch logic.)
393+ *
353394 */
354395 {
355- auto transcript_pc_shift = View (in.transcript_pc_shift );
356- auto transcript_msm_x = View (in.transcript_msm_x );
357- auto transcript_msm_y = View (in.transcript_msm_y );
358- auto transcript_msm_transition = View (in.transcript_msm_transition );
359- auto transcript_msm_count = View (in.transcript_msm_count );
360- auto z1_zero = View (in.transcript_z1zero );
361- auto z2_zero = View (in.transcript_z2zero );
362- auto transcript_mul = View (in.transcript_mul );
363- auto base_infinity = View (in.transcript_base_infinity );
396+ const auto & transcript_pc_shift = View (in.transcript_pc_shift );
397+ const auto & transcript_msm_x = View (in.transcript_msm_x );
398+ const auto & transcript_msm_y = View (in.transcript_msm_y );
399+ const auto & transcript_msm_transition = View (in.transcript_msm_transition );
400+ const auto & transcript_msm_count = View (in.transcript_msm_count );
401+ const auto & z1_zero = View (in.transcript_z1zero );
402+ const auto & z2_zero = View (in.transcript_z2zero );
403+ const auto & transcript_mul = View (in.transcript_mul );
404+ const auto & base_infinity = View (in.transcript_base_infinity );
364405
365406 // do not add to count if point at infinity!
366407 auto full_msm_count =
0 commit comments