Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecc_msm_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 "ecc_msm_relation.hpp"
11
12namespace bb {
13
46template <typename FF>
47template <typename ContainerOverSubrelations, typename AllEntities, typename Parameters>
48void ECCVMMSMRelationImpl<FF>::accumulate(ContainerOverSubrelations& accumulator,
49 const AllEntities& in,
50 const Parameters& /*unused*/,
51 const FF& scaling_factor)
52{
53 using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>;
54 using View = typename Accumulator::View;
55
56 const auto& x1 = View(in.msm_x1);
57 const auto& y1 = View(in.msm_y1);
58 const auto& x2 = View(in.msm_x2);
59 const auto& y2 = View(in.msm_y2);
60 const auto& x3 = View(in.msm_x3);
61 const auto& y3 = View(in.msm_y3);
62 const auto& x4 = View(in.msm_x4);
63 const auto& y4 = View(in.msm_y4);
64 const auto& collision_inverse1 = View(in.msm_collision_x1);
65 const auto& collision_inverse2 = View(in.msm_collision_x2);
66 const auto& collision_inverse3 = View(in.msm_collision_x3);
67 const auto& collision_inverse4 = View(in.msm_collision_x4);
68 const auto& lambda1 = View(in.msm_lambda1);
69 const auto& lambda2 = View(in.msm_lambda2);
70 const auto& lambda3 = View(in.msm_lambda3);
71 const auto& lambda4 = View(in.msm_lambda4);
72 const auto& lagrange_first = View(in.lagrange_first);
73 const auto& add1 = View(in.msm_add1);
74 const auto& add1_shift = View(in.msm_add1_shift);
75 const auto& add2 = View(in.msm_add2);
76 const auto& add3 = View(in.msm_add3);
77 const auto& add4 = View(in.msm_add4);
78 const auto& acc_x = View(in.msm_accumulator_x);
79 const auto& acc_y = View(in.msm_accumulator_y);
80 const auto& acc_x_shift = View(in.msm_accumulator_x_shift);
81 const auto& acc_y_shift = View(in.msm_accumulator_y_shift);
82 const auto& slice1 = View(in.msm_slice1);
83 const auto& slice2 = View(in.msm_slice2);
84 const auto& slice3 = View(in.msm_slice3);
85 const auto& slice4 = View(in.msm_slice4);
86 const auto& msm_transition = View(in.msm_transition);
87 const auto& msm_transition_shift = View(in.msm_transition_shift);
88 const auto& round = View(in.msm_round);
89 const auto& round_shift = View(in.msm_round_shift);
90 const auto& q_add = View(in.msm_add); // is 1 iff we are at an ADD row in Straus algorithm
91 const auto& q_add_shift = View(in.msm_add_shift);
92 const auto& q_skew = View(in.msm_skew);
93 const auto& q_skew_shift = View(in.msm_skew_shift);
94 const auto& q_double = View(in.msm_double); // is 1 iff we are at an DOUBLE row in Straus algorithm
95 const auto& q_double_shift = View(in.msm_double_shift);
96 const auto& msm_size = View(in.msm_size_of_msm);
97 const auto& pc = View(in.msm_pc); // pc stands for `point-counter`.
98 const auto& pc_shift = View(in.msm_pc_shift);
99 const auto& count = View(in.msm_count);
100 const auto& count_shift = View(in.msm_count_shift);
101 auto is_not_first_row = (-lagrange_first + 1);
102
277 auto add = [&](auto& xb, auto& yb, auto& xa, auto& ya, auto& lambda, auto& selector, auto& collision_relation) {
278 // computation of lambda is valid: if q == 1, then L == (yb - ya) / (xb - xa)
279 // if q == 0, then L == 0. combining these into a single constraint yields:
280 // q * (L * (xb - xa - 1) - (yb - ya)) + L = 0
281 auto slope_relation = selector * (lambda * (xb - xa - 1) - (yb - ya)) + lambda;
282 collision_relation += selector * (xb - xa);
283 // x_out = L.L + (-xb - xa) * q + (1 - q) xa
284 // deg L = 1, deg q = 1, min(deg(xa), deg(xb))≥ 1.
285 // hence deg(x_out) = 1 + max(deg(xa), deg(xb))
286 auto x_out = lambda.sqr() + (-xb - xa - xa) * selector + xa;
287
288 // y_out = L . (xa - x_out) - ya * q + (1 - q) ya
289 // hence deg(y_out) = max(1 + deg(x_out), 1 + deg(ya))
290 auto y_out = lambda * (xa - x_out) + (-ya - ya) * selector + ya;
291 return std::array<Accumulator, 3>{ x_out, y_out, slope_relation };
292 };
293
326 auto first_add =
327 [&](auto& xb, auto& yb, auto& xa, auto& ya, auto& lambda, auto& selector, auto& collision_relation) {
328 // N.B. this is brittle - should be curve agnostic but we don't propagate the curve parameter into
329 // relations!
330 constexpr auto offset_generator = get_precomputed_generators<g1, "ECCVM_OFFSET_GENERATOR", 1>()[0];
331 constexpr uint256_t oxu = offset_generator.x;
332 constexpr uint256_t oyu = offset_generator.y;
333 const Accumulator xo(oxu);
334 const Accumulator yo(oyu);
335 // set (x, y) to be either accumulator if `selector == 0` or OFFSET if `selector == 1`.
336 auto x = xo * selector + xb * (-selector + 1);
337 auto y = yo * selector + yb * (-selector + 1);
338 auto slope_relation = lambda * (x - xa) - (y - ya); // degree 3
339 collision_relation += (xa - x);
340 auto x_out = lambda * lambda + (-x - xa);
341 auto y_out = lambda * (xa - x_out) - ya;
342 return std::array<Accumulator, 3>{ x_out, y_out, slope_relation };
343 };
344
345 // ADD operations (if row represents ADD round, not SKEW or DOUBLE)
346 Accumulator x1_collision_relation(0);
347 Accumulator x2_collision_relation(0);
348 Accumulator x3_collision_relation(0);
349 Accumulator x4_collision_relation(0);
350 // If `msm_transition == 1`, we have started a new MSM. We need to treat the current value of [Acc] as the point at
351 // infinity!
352 auto [x_t1, y_t1, add_slope_relation1] =
353 first_add(acc_x, acc_y, x1, y1, lambda1, msm_transition, x1_collision_relation); // [deg 2, deg 3]
354 auto [x_t2, y_t2, add_slope_relation2] =
355 add(x2, y2, x_t1, y_t1, lambda2, add2, x2_collision_relation); // [deg 3, deg 4]
356 auto [x_t3, y_t3, add_slope_relation3] =
357 add(x3, y3, x_t2, y_t2, lambda3, add3, x3_collision_relation); // [deg 4, deg 5]
358 auto [x_t4, y_t4, add_slope_relation4] =
359 add(x4, y4, x_t3, y_t3, lambda4, add4, x4_collision_relation); // [deg 5, deg 6]
360
361 // Validate accumulator output matches ADD output if q_add = 1
362 std::get<ADD_ACC_X>(accumulator) += q_add * (acc_x_shift - x_t4) * scaling_factor;
363 std::get<ADD_ACC_Y>(accumulator) += q_add * (acc_y_shift - y_t4) * scaling_factor;
364 // Validate slope relations for each addition separately to prevent cancellation attacks
365 std::get<ADD_SLOPE_1>(accumulator) += q_add * add_slope_relation1 * scaling_factor;
366 std::get<ADD_SLOPE_2>(accumulator) += q_add * add_slope_relation2 * scaling_factor;
367 std::get<ADD_SLOPE_3>(accumulator) += q_add * add_slope_relation3 * scaling_factor;
368 std::get<ADD_SLOPE_4>(accumulator) += q_add * add_slope_relation4 * scaling_factor;
369
377 auto dbl = [&](auto& x, auto& y, auto& lambda) {
378 auto two_x = x + x;
379 auto slope_relation = lambda * (y + y) - (two_x + x) * x;
380 auto x_out = lambda.sqr() - two_x;
381 auto y_out = lambda * (x - x_out) - y;
382 return std::array<Accumulator, 3>{ x_out, y_out, slope_relation };
383 };
384
400 auto [x_d1, y_d1, double_slope_relation1] = dbl(acc_x, acc_y, lambda1);
401 auto [x_d2, y_d2, double_slope_relation2] = dbl(x_d1, y_d1, lambda2);
402 auto [x_d3, y_d3, double_slope_relation3] = dbl(x_d2, y_d2, lambda3);
403 auto [x_d4, y_d4, double_slope_relation4] = dbl(x_d3, y_d3, lambda4);
404 std::get<DOUBLE_ACC_X>(accumulator) += q_double * (acc_x_shift - x_d4) * scaling_factor;
405 std::get<DOUBLE_ACC_Y>(accumulator) += q_double * (acc_y_shift - y_d4) * scaling_factor;
406 // Validate slope relations for each doubling separately to prevent cancellation attacks
407 std::get<DOUBLE_SLOPE_1>(accumulator) += q_double * double_slope_relation1 * scaling_factor;
408 std::get<DOUBLE_SLOPE_2>(accumulator) += q_double * double_slope_relation2 * scaling_factor;
409 std::get<DOUBLE_SLOPE_3>(accumulator) += q_double * double_slope_relation3 * scaling_factor;
410 std::get<DOUBLE_SLOPE_4>(accumulator) += q_double * double_slope_relation4 * scaling_factor;
411
425 static FF inverse_seven = FF(7).invert();
426 auto skew1_select = slice1 * inverse_seven;
427 auto skew2_select = slice2 * inverse_seven;
428 auto skew3_select = slice3 * inverse_seven;
429 auto skew4_select = slice4 * inverse_seven;
430 Accumulator x1_skew_collision_relation(0);
431 Accumulator x2_skew_collision_relation(0);
432 Accumulator x3_skew_collision_relation(0);
433 Accumulator x4_skew_collision_relation(0);
434 // add skew points iff row is a SKEW row AND slice = 7 (point_table[7] maps to -[P])
435 // N.B. while it would be nice to have one `add` relation for both ADD and SKEW rounds,
436 // this would increase degree of sumcheck identity vs evaluating them separately.
437 // This is because, for add rounds, the result of adding [P1], [Acc] is [P1 + Acc] or [P1]
438 // but for skew rounds, the result of adding [P1], [Acc] is [P1 + Acc] or [Acc]
439 auto [x_s1, y_s1, skew_slope_relation1] =
440 add(x1, y1, acc_x, acc_y, lambda1, skew1_select, x1_skew_collision_relation);
441 auto [x_s2, y_s2, skew_slope_relation2] =
442 add(x2, y2, x_s1, y_s1, lambda2, skew2_select, x2_skew_collision_relation);
443 auto [x_s3, y_s3, skew_slope_relation3] =
444 add(x3, y3, x_s2, y_s2, lambda3, skew3_select, x3_skew_collision_relation);
445 auto [x_s4, y_s4, skew_slope_relation4] =
446 add(x4, y4, x_s3, y_s3, lambda4, skew4_select, x4_skew_collision_relation);
447
448 // Validate accumulator output matches SKEW output if q_skew = 1
449 std::get<SKEW_ACC_X>(accumulator) += q_skew * (acc_x_shift - x_s4) * scaling_factor;
450 std::get<SKEW_ACC_Y>(accumulator) += q_skew * (acc_y_shift - y_s4) * scaling_factor;
451 // Validate slope relations for each skew addition separately to prevent cancellation attacks
452 std::get<SKEW_SLOPE_1>(accumulator) += q_skew * skew_slope_relation1 * scaling_factor;
453 std::get<SKEW_SLOPE_2>(accumulator) += q_skew * skew_slope_relation2 * scaling_factor;
454 std::get<SKEW_SLOPE_3>(accumulator) += q_skew * skew_slope_relation3 * scaling_factor;
455 std::get<SKEW_SLOPE_4>(accumulator) += q_skew * skew_slope_relation4 * scaling_factor;
456
457 // Check x-coordinates do not collide if row is an ADD row or a SKEW row
458 // if either q_add or q_skew = 1, an inverse should exist for each computed relation
459 // Step 1: construct boolean selectors that describe whether we added a point at the current row
460 const auto add_first_point = add1 * q_add + q_skew * skew1_select;
461 const auto add_second_point = add2 * q_add + q_skew * skew2_select;
462 const auto add_third_point = add3 * q_add + q_skew * skew3_select;
463 const auto add_fourth_point = add4 * q_add + q_skew * skew4_select;
464 // Step 2: construct the difference a.k.a. delta between x-coordinates for each point add (depending on if row is
465 // ADD or SKEW)
466 const auto x1_delta = x1_skew_collision_relation * q_skew + x1_collision_relation * q_add;
467 const auto x2_delta = x2_skew_collision_relation * q_skew + x2_collision_relation * q_add;
468 const auto x3_delta = x3_skew_collision_relation * q_skew + x3_collision_relation * q_add;
469 const auto x4_delta = x4_skew_collision_relation * q_skew + x4_collision_relation * q_add;
470 // Step 3: x_delta * inverse - 1 = 0 if we performed a point addition (else x_delta * inverse = 0)
471 std::get<COLLISION_CHECK_1>(accumulator) += (x1_delta * collision_inverse1 - add_first_point) * scaling_factor;
472 std::get<COLLISION_CHECK_2>(accumulator) += (x2_delta * collision_inverse2 - add_second_point) * scaling_factor;
473 std::get<COLLISION_CHECK_3>(accumulator) += (x3_delta * collision_inverse3 - add_third_point) * scaling_factor;
474 std::get<COLLISION_CHECK_4>(accumulator) += (x4_delta * collision_inverse4 - add_fourth_point) * scaling_factor;
475
476 // When add_i = 0, force slice_i to ALSO be 0
477 std::get<INACTIVE_SLICE_1>(accumulator) += (-add1 + 1) * slice1 * scaling_factor;
478 std::get<INACTIVE_SLICE_2>(accumulator) += (-add2 + 1) * slice2 * scaling_factor;
479 std::get<INACTIVE_SLICE_3>(accumulator) += (-add3 + 1) * slice3 * scaling_factor;
480 std::get<INACTIVE_SLICE_4>(accumulator) += (-add4 + 1) * slice4 * scaling_factor;
481
482 // SELECTORS ARE MUTUALLY EXCLUSIVE
483 // at most one of q_skew, q_double, q_add can be nonzero.
484 // note that as we can expect our table to be zero padded, we _do not_ insist that q_add + q_double + q_skew == 1.
486 (q_add * q_double + q_add * q_skew + q_double * q_skew) * scaling_factor;
487
488 // ACCUMULATOR PRESERVATION ON NO-OP ROWS
489 // If no phase selector is active (q_add = q_double = q_skew = 0), the accumulator must not change.
490 // Without this constraint, a malicious prover could insert no-op rows between active rows and
491 // set arbitrary accumulator values on the next row, because the accumulator-update constraints
492 // are all gated by their respective phase selectors.
493 // We exclude two boundary cases:
494 // - msm_transition = 1 on the current row: msm_transition marks the first row of a new MSM
495 // (where q_add is also 1), but the final row of the entire MSM trace ALSO has msm_transition = 1
496 // with ALL phase selectors off. On that row, acc holds the MSM output and acc_shift need not
497 // be preserved. This is safe because the set relation constrains (pc, acc_x, acc_y, msm_size)
498 // at transitions. (NOTE: this is a design choice specified by the builder; we could equivalently propagate the
499 // accumulator one past the last MSM row and then not turn off the constraint when `msm_transition == 1`.)
500 // - lagrange_first = 1 (row TRACE_OFFSET): the first active row of the trace is zero-padded and the next row
501 // starts a fresh MSM whose accumulator is initialized via first_add, not by continuity.
502 auto no_op_selector =
503 (-q_add + 1) * (-q_double + 1) * (-q_skew + 1) * (-msm_transition + 1) * (-lagrange_first + 1); // degree 5
505 no_op_selector * (acc_x_shift - acc_x) * scaling_factor; // degree 6
507 no_op_selector * (acc_y_shift - acc_y) * scaling_factor; // degree 6
508
509 // Validate that if q_add = 1 or q_skew = 1, add1 also is 1
510 // NOTE(#2222): could just get rid of add1 as a column, as it is a linear combination.
511 std::get<ADD1_DECOMPOSITION>(accumulator) += (add1 - q_add - q_skew) * scaling_factor;
512
513 // ROUND TRANSITION LOGIC
514 // `round_transition` describes whether we are transitioning between "rounds" of the MSM according to the Straus
515 // algorithm. In particular, the `round` corresponds to the wNAF digit we are currently processing.
516
517 const auto round_delta = round_shift - round;
518 // If `msm_transition == 0` (next row) then `round_delta` is boolean; the round is internal to a given MSM and
519 // represents the wNAF digit currently being processed. `round_delta == 0` means that the current and next steps of
520 // the Straus algorithm are processing the same wNAF digit place.
521
522 // `round_transition == 0` if `round_delta == 0` or the next row is an MSM transition.
523 // if `round_transition != 0`, then `round_transition == round_delta == 1` by the following constraint.
524 // in particular, `round_transition` is boolean. (`round_delta` is not boolean precisely one step before an MSM
525 // transition, but that does not concern us here.)
526 const auto round_transition = round_delta * (-msm_transition_shift + 1);
527 std::get<ROUND_TRANSITION_FORCES_DELTA_ONE>(accumulator) += round_transition * (round_delta - 1) * scaling_factor;
528
529 // If `round_transition == 1`, then `round_delta == 1` and `msm_transition_shift == 0`. Therefore, we wish to
530 // constrain next row in the VM to either be a double (if `round != 31`) or skew (if `round == 31`). In either case,
531 // the point is that we have finished processing a wNAF digit place and need to either perform the doublings to move
532 // on to the next place _or_ we are at the last place and need to perform the skew computation to finish. These are
533 // equationally represented as:
534 // round_transition * skew_shift * (round - 31) = 0 (if round tx and skew, then round == 31);
535 // round_transition * (skew_shift + double_shift - 1) = 0 (if round tx, then skew XOR double = 1).
536 // (-round_delta + 1) * q_double_shift = 1 (if q_double_shift == 1, then round_transition = 1)
537 // together, these have the following implications: if round tx and round != 31, then double_shift = 1.
538 // conversely, if round tx and double_shift == 0, then `q_skew_shift == 1` (which then forces `round == 31`).
539 // similarly, if q_double_shift == 1, then round_transition == 0,
540 // the fact that a round_transition occurs at the first time skew_shift == 1 follows from the fact that skew == 1
541 // implies round == 32 and the above three relations, together with the _definition_ of round_transition.
543 round_transition * q_skew_shift * (round - 31) * scaling_factor;
545 round_transition * (q_skew_shift + q_double_shift - 1) * scaling_factor;
546 std::get<DOUBLE_REQUIRES_ROUND_CHANGE>(accumulator) += (-round_delta + 1) * q_double_shift * scaling_factor;
547 // if the next is neither double nor skew, and we are not at an msm_transition, then round_delta = 0 and the next
548 // "row" of our VM is processing the same wNAF digit place.
550 round_transition * (-q_double_shift + 1) * (-q_skew_shift + 1) * scaling_factor;
551
552 // CONSTRAINING Q_DOUBLE AND Q_SKEW
553 // NOTE: we have already constrained q_add, q_skew, and q_double to be mutually exclusive.
554
555 // if double, next add = 1. As q_double, q_add, and q_skew are mutually exclusive, this suffices to force
556 // q_double_shift == q_skew_shift == 0.
557 std::get<DOUBLE_IMPLIES_NEXT_IS_ADD>(accumulator) += q_double * (-q_add_shift + 1) * scaling_factor;
558 // if the current row has q_skew == 1 and the next row is _not_ an MSM transition, then q_skew_shift = 1.
559 // this forces q_skew to precisely correspond to the rows where `round == 32`. Indeed, note that the first q_skew
560 // bit is set correctly:
561 // round == 31, round_transition == 1 ==> q_skew_shift == 1. (if, to the contrary, q_double_shift == 1, then
562 // the q_add_shift_shift == 1, but we assume that we have correctly constrained the q_adds via the multiset
563 // argument. this means that q_double_shift == 0, which forces q_skew_shift == 1 because round_transition
564 // == 1.)
565 // this means that the first row with `round == 32` has q_skew == 1. then all subsequent q_skew entries must be 1,
566 // _until_ we start our new MSM.
568 (-msm_transition_shift + 1) * q_skew * (-q_skew_shift + 1) * scaling_factor;
569 // if q_skew == 1, then round == 32. This is almost certainly redundant but psychologically useful to "constrain
570 // both ends".
571 std::get<SKEW_IMPLIES_ROUND_32>(accumulator) += q_skew * (-round + 32) * scaling_factor;
572
573 // UPDATING THE COUNT
574
575 // if we are changing the `round` (i.e., starting to process a new wNAF digit or at an msm transition), the
576 // count_shift must be 0.
577 std::get<COUNT_SHIFT_ZERO_ON_ROUND_CHANGE>(accumulator) += round_delta * count_shift * scaling_factor;
578 // if msm_transition_shift = 0 and round_delta = 0, then the next "row" of the VM is processing the same wNAF digit.
579 // this means that the count must increase: count_shift = count + add1 + add2 + add3 + add4
580 std::get<COUNT_INCREMENT_WITHIN_ROUND>(accumulator) += (-msm_transition_shift + 1) * (-round_delta + 1) *
581 (count_shift - count - add1 - add2 - add3 - add4) *
582 scaling_factor;
583
584 // at least one of the following must be true:
585 // the next step is an MSM transition;
586 // the next count is zero (meaning we are starting the processing of a new wNAF digit)
587 // the next step is processing the same wNAF digit (i.e., round_delta == 0)
588 // (note that at the start of a new MSM, the count is also zero, so the above are not mutually exclusive.)
590 is_not_first_row * (-msm_transition_shift + 1) * round_delta * count_shift * scaling_factor;
591
592 // if msm_transition = 1, then round = 0.
593 std::get<MSM_TRANSITION_ROUND_ZERO>(accumulator) += msm_transition * round * scaling_factor;
594
595 // if msm_transition_shift = 1, pc = pc_shift + msm_size
596 // NB: `ecc_set_relation` ensures `msm_size` maps to `transcript.msm_count` for the current value of `pc`
597 std::get<MSM_TRANSITION_PC>(accumulator) +=
598 is_not_first_row * msm_transition_shift * (msm_size + pc_shift - pc) * scaling_factor;
599
600 // Addition continuity checks
601 // We want to RULE OUT the following scenarios:
602 // Case 1: add2 = 1, add1 = 0
603 // Case 2: add3 = 1, add2 = 0
604 // Case 3: add4 = 1, add3 = 0
605 // These checks ensure that the current row does not skip points (for both ADD and SKEW ops)
606 // This is part of a wider set of checks we use to ensure that all point data is used in the assigned
607 // multiscalar multiplication operation (and not in a different MSM operation).
608 std::get<ADD_CONTINUITY_2>(accumulator) += add2 * (-add1 + 1) * scaling_factor;
609 std::get<ADD_CONTINUITY_3>(accumulator) += add3 * (-add2 + 1) * scaling_factor;
610 std::get<ADD_CONTINUITY_4>(accumulator) += add4 * (-add3 + 1) * scaling_factor;
611
612 // Final continuity check.
613 // If an addition spans two rows, we need to make sure that the following scenario is RULED OUT:
614 // add4 = 0 on the CURRENT row, add1 = 1 on the NEXT row
615 // We must apply the above for the two cases:
616 // Case 1: q_add = 1 on the CURRENT row, q_add = 1 on the NEXT row
617 // Case 2: q_skew = 1 on the CURRENT row, q_skew = 1 on the NEXT row
618 // (i.e. if q_skew = 1, q_add_shift = 1 this implies an MSM transition so we skip this continuity check)
620 (q_add * q_add_shift + q_skew * q_skew_shift) * (-add4 + 1) * add1_shift * scaling_factor;
621
622 // remaining checks (done in ecc_set_relation.hpp, ecc_lookup_relation.hpp)
623 // when transition occurs, perform set membership lookup on (accumulator / pc / msm_size)
624 // perform set membership lookups on add_i * (pc / round / slice_i)
625 // perform lookups on (pc / slice_i / x / y)
626
627 // We look up wnaf slices by mapping round + pc -> slice
628 // We use an exact set membership check to validate that
629 // wnafs written in wnaf_relation == wnafs read in msm relation
630 // We use `add1/add2/add3/add4` to flag whether we are performing a wnaf read op
631 // We can set these to be Prover-defined as the set membership check implicitly ensures that the correct reads
632 // have occurred.
633}
634
635} // namespace bb
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
static void accumulate(ContainerOverSubrelations &accumulator, const AllEntities &in, const Parameters &, const FF &scaling_factor)
MSM relations that evaluate the Strauss multiscalar multiplication algorithm.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
group< fq, fr, Bn254G1Params > g1
Definition g1.hpp:34
constexpr std::span< const typename Group::affine_element > get_precomputed_generators()
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
constexpr field invert() const noexcept