barretenberg
Loading...
Searching...
No Matches
zeromorph.hpp
1#pragma once
2#include "barretenberg/commitment_schemes/commitment_key.hpp"
3#include "barretenberg/common/ref_vector.hpp"
4#include "barretenberg/common/zip_view.hpp"
5#include "barretenberg/polynomials/polynomial.hpp"
6#include "barretenberg/transcript/transcript.hpp"
7
8namespace proof_system::honk::pcs::zeromorph {
9
18template <class FF> inline std::vector<FF> powers_of_challenge(const FF challenge, const size_t num_powers)
19{
20 std::vector<FF> challenge_powers = { FF(1), challenge };
21 challenge_powers.reserve(num_powers);
22 for (size_t j = 2; j < num_powers; j++) {
23 challenge_powers.emplace_back(challenge_powers[j - 1] * challenge);
24 }
25 return challenge_powers;
26};
27
33template <typename Curve> class ZeroMorphProver_ {
34 using FF = typename Curve::ScalarField;
35 using Commitment = typename Curve::AffineElement;
37
38 // TODO(#742): Set this N_max to be the number of G1 elements in the mocked zeromorph SRS once it's in place.
39 // (Then, eventually, set it based on the real SRS). For now we set it to be large but more or less arbitrary.
40 static const size_t N_max = 1 << 22;
41
42 public:
63 static std::vector<Polynomial> compute_multilinear_quotients(Polynomial polynomial, std::span<const FF> u_challenge)
64 {
65 size_t log_N = numeric::get_msb(polynomial.size());
66 // The size of the multilinear challenge must equal the log of the polynomial size
67 ASSERT(log_N == u_challenge.size());
68
69 // Define the vector of quotients q_k, k = 0, ..., log_N-1
70 std::vector<Polynomial> quotients;
71 for (size_t k = 0; k < log_N; ++k) {
72 size_t size = 1 << k;
73 quotients.emplace_back(Polynomial(size)); // degree 2^k - 1
74 }
75
76 // Compute the coefficients of q_{n-1}
77 size_t size_q = 1 << (log_N - 1);
78 Polynomial q{ size_q };
79 for (size_t l = 0; l < size_q; ++l) {
80 q[l] = polynomial[size_q + l] - polynomial[l];
81 }
82
83 quotients[log_N - 1] = q.share();
84
85 std::vector<FF> f_k;
86 f_k.resize(size_q);
87
88 std::vector<FF> g(polynomial.data().get(), polynomial.data().get() + size_q);
89
90 // Compute q_k in reverse order from k= n-2, i.e. q_{n-2}, ..., q_0
91 for (size_t k = 1; k < log_N; ++k) {
92 // Compute f_k
93 for (size_t l = 0; l < size_q; ++l) {
94 f_k[l] = g[l] + u_challenge[log_N - k] * q[l];
95 }
96
97 size_q = size_q / 2;
98 q = Polynomial{ size_q };
99
100 for (size_t l = 0; l < size_q; ++l) {
101 q[l] = f_k[size_q + l] - f_k[l];
102 }
103
104 quotients[log_N - k - 1] = q.share();
105 g = f_k;
106 }
107
108 return quotients;
109 }
110
123 static Polynomial compute_batched_lifted_degree_quotient(std::vector<Polynomial>& quotients,
124 FF y_challenge,
125 size_t N)
126 {
127 // Batched lifted degree quotient polynomial
128 auto result = Polynomial(N);
129
130 // Compute \hat{q} = \sum_k y^k * X^{N - d_k - 1} * q_k
131 size_t k = 0;
132 auto scalar = FF(1); // y^k
133 for (auto& quotient : quotients) {
134 // Rather than explicitly computing the shifts of q_k by N - d_k - 1 (i.e. multiplying q_k by X^{N - d_k -
135 // 1}) then accumulating them, we simply accumulate y^k*q_k into \hat{q} at the index offset N - d_k - 1
136 auto deg_k = static_cast<size_t>((1 << k) - 1);
137 size_t offset = N - deg_k - 1;
138 for (size_t idx = 0; idx < deg_k + 1; ++idx) {
139 result[offset + idx] += scalar * quotient[idx];
140 }
141 scalar *= y_challenge; // update batching scalar y^k
142 k++;
143 }
144
145 return result;
146 }
147
161 std::vector<Polynomial>& quotients,
162 FF y_challenge,
163 FF x_challenge)
164 {
165 size_t N = batched_quotient.size();
166 size_t log_N = quotients.size();
167
168 // Initialize partially evaluated degree check polynomial \zeta_x to \hat{q}
169 auto result = batched_quotient;
170
171 auto y_power = FF(1); // y^k
172 for (size_t k = 0; k < log_N; ++k) {
173 // Accumulate y^k * x^{N - d_k - 1} * q_k into \hat{q}
174 auto deg_k = static_cast<size_t>((1 << k) - 1);
175 auto x_power = x_challenge.pow(N - deg_k - 1); // x^{N - d_k - 1}
176
177 result.add_scaled(quotients[k], -y_power * x_power);
178
179 y_power *= y_challenge; // update batching scalar y^k
180 }
181
182 return result;
183 }
184
206 Polynomial& f_batched,
207 Polynomial& g_batched,
208 std::vector<Polynomial>& quotients,
209 FF v_evaluation,
210 std::span<const FF> u_challenge,
211 FF x_challenge,
212 std::vector<Polynomial> concatenation_groups_batched = {})
213 {
214 size_t N = f_batched.size();
215 size_t log_N = quotients.size();
216
217 // Initialize Z_x with x * \sum_{i=0}^{m-1} f_i + \sum_{i=0}^{l-1} g_i
218 auto result = g_batched;
219 result.add_scaled(f_batched, x_challenge);
220
221 // Compute Z_x -= v * x * \Phi_n(x)
222 auto phi_numerator = x_challenge.pow(N) - 1; // x^N - 1
223 auto phi_n_x = phi_numerator / (x_challenge - 1);
224 result[0] -= v_evaluation * x_challenge * phi_n_x;
225
226 // Add contribution from q_k polynomials
227 auto x_power = x_challenge; // x^{2^k}
228 for (size_t k = 0; k < log_N; ++k) {
229 x_power = x_challenge.pow(1 << k); // x^{2^k}
230
231 // \Phi_{n-k-1}(x^{2^{k + 1}})
232 auto phi_term_1 = phi_numerator / (x_challenge.pow(1 << (k + 1)) - 1);
233
234 // \Phi_{n-k}(x^{2^k})
235 auto phi_term_2 = phi_numerator / (x_challenge.pow(1 << k) - 1);
236
237 // x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k})
238 auto scalar = x_power * phi_term_1 - u_challenge[k] * phi_term_2;
239
240 scalar *= x_challenge;
241 scalar *= FF(-1);
242
243 result.add_scaled(quotients[k], scalar);
244 }
245
246 // If necessary, add to Z_x the contribution related to concatenated polynomials:
247 // \sum_{i=0}^{num_chunks_per_group}(x^{i * min_n + 1}concatenation_groups_batched_{i}).
248 // We are effectively reconstructing concatenated polynomials from their chunks now that we know x
249 // Note: this is an implementation detail related to Goblin Translator and is not part of the standard protocol.
250 if (!concatenation_groups_batched.empty()) {
251 size_t MINICIRCUIT_N = N / concatenation_groups_batched.size();
252 auto x_to_minicircuit_N =
253 x_challenge.pow(MINICIRCUIT_N); // power of x used to shift polynomials to the right
254 auto running_shift = x_challenge;
255 for (size_t i = 0; i < concatenation_groups_batched.size(); i++) {
256 result.add_scaled(concatenation_groups_batched[i], running_shift);
257 running_shift *= x_to_minicircuit_N;
258 }
259 }
260
261 return result;
262 }
263
278 Polynomial& Z_x,
279 FF x_challenge,
280 FF z_challenge)
281 {
282 // We cannot commit to polynomials with size > N_max
283 size_t N = zeta_x.size();
284 ASSERT(N <= N_max);
285
286 // Compute q_{\zeta} and q_Z in place
287 zeta_x.factor_roots(x_challenge);
288 Z_x.factor_roots(x_challenge);
289
290 // Compute batched quotient q_{\zeta} + z*q_Z
291 auto batched_quotient = zeta_x;
292 batched_quotient.add_scaled(Z_x, z_challenge);
293
294 // TODO(#742): To complete the degree check, we need to commit to (q_{\zeta} + z*q_Z)*X^{N_max - N - 1}.
295 // Verification then requires a pairing check similar to the standard KZG check but with [1]_2 replaced by
296 // [X^{N_max - N -1}]_2. Two issues: A) we do not have an SRS with these G2 elements (so need to generate a fake
297 // setup until we can do the real thing), and B) its not clear to me how to update our pairing algorithms to do
298 // this type of pairing. For now, simply construct q_{\zeta} + z*q_Z without the shift and do a standard KZG
299 // pairing check. When we're ready, all we have to do to make this fully legit is commit to the shift here and
300 // update the pairing check accordingly. Note: When this is implemented properly, it doesnt make sense to store
301 // the (massive) shifted polynomial of size N_max. Ideally would only store the unshifted version and just
302 // compute the shifted commitment directly via a new method.
303 auto batched_shifted_quotient = batched_quotient;
304
305 return batched_shifted_quotient;
306 }
307
319 static void prove(const std::vector<Polynomial>& f_polynomials,
320 const std::vector<Polynomial>& g_polynomials,
321 const std::vector<FF>& f_evaluations,
322 const std::vector<FF>& g_shift_evaluations,
323 const std::vector<FF>& multilinear_challenge,
324 const std::shared_ptr<CommitmentKey<Curve>>& commitment_key,
325 const std::shared_ptr<BaseTranscript>& transcript,
326 const std::vector<Polynomial>& concatenated_polynomials = {},
327 const std::vector<FF>& concatenated_evaluations = {},
328 const std::vector<RefVector<Polynomial>>& concatenation_groups = {})
329 {
330 // Generate batching challenge \rho and powers 1,...,\rho^{m-1}
331 const FF rho = transcript->get_challenge("rho");
332
333 // Extract multilinear challenge u and claimed multilinear evaluations from Sumcheck output
334 std::span<const FF> u_challenge = multilinear_challenge;
335 size_t log_N = u_challenge.size();
336 size_t N = 1 << log_N;
337
338 // Compute batching of unshifted polynomials f_i and to-be-shifted polynomials g_i:
339 // f_batched = sum_{i=0}^{m-1}\rho^i*f_i and g_batched = sum_{i=0}^{l-1}\rho^{m+i}*g_i,
340 // and also batched evaluation
341 // v = sum_{i=0}^{m-1}\rho^i*f_i(u) + sum_{i=0}^{l-1}\rho^{m+i}*h_i(u).
342 // Note: g_batched is formed from the to-be-shifted polynomials, but the batched evaluation incorporates the
343 // evaluations produced by sumcheck of h_i = g_i_shifted.
344 FF batched_evaluation{ 0 };
345 Polynomial f_batched(N); // batched unshifted polynomials
346 FF batching_scalar{ 1 };
347 for (auto [f_poly, f_eval] : zip_view(f_polynomials, f_evaluations)) {
348 f_batched.add_scaled(f_poly, batching_scalar);
349 batched_evaluation += batching_scalar * f_eval;
350 batching_scalar *= rho;
351 }
352
353 Polynomial g_batched{ N }; // batched to-be-shifted polynomials
354 for (auto [g_poly, g_shift_eval] : zip_view(g_polynomials, g_shift_evaluations)) {
355 g_batched.add_scaled(g_poly, batching_scalar);
356 batched_evaluation += batching_scalar * g_shift_eval;
357 batching_scalar *= rho;
358 };
359
360 size_t num_groups = concatenation_groups.size();
361 size_t num_chunks_per_group = concatenation_groups.empty() ? 0 : concatenation_groups[0].size();
362 // Concatenated polynomials
363 Polynomial concatenated_batched(N);
364
365 // construct concatention_groups_batched
366 std::vector<Polynomial> concatenation_groups_batched;
367 for (size_t i = 0; i < num_chunks_per_group; ++i) {
368 concatenation_groups_batched.push_back(Polynomial(N));
369 }
370 // for each group
371 for (size_t i = 0; i < num_groups; ++i) {
372 concatenated_batched.add_scaled(concatenated_polynomials[i], batching_scalar);
373 // for each element in a group
374 for (size_t j = 0; j < num_chunks_per_group; ++j) {
375 concatenation_groups_batched[j].add_scaled(concatenation_groups[i][j], batching_scalar);
376 }
377 batched_evaluation += batching_scalar * concatenated_evaluations[i];
378 batching_scalar *= rho;
379 }
380
381 // Compute the full batched polynomial f = f_batched + g_batched.shifted() = f_batched + h_batched. This is the
382 // polynomial for which we compute the quotients q_k and prove f(u) = v_batched.
383 Polynomial f_polynomial = f_batched;
384 f_polynomial += g_batched.shifted();
385 f_polynomial += concatenated_batched;
386
387 // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1})
388 auto quotients = compute_multilinear_quotients(f_polynomial, u_challenge);
389
390 // Compute and send commitments C_{q_k} = [q_k], k = 0,...,d-1
391 std::vector<Commitment> q_k_commitments;
392 q_k_commitments.reserve(log_N);
393 for (size_t idx = 0; idx < log_N; ++idx) {
394 q_k_commitments[idx] = commitment_key->commit(quotients[idx]);
395 std::string label = "ZM:C_q_" + std::to_string(idx);
396 transcript->send_to_verifier(label, q_k_commitments[idx]);
397 }
398
399 // Get challenge y
400 FF y_challenge = transcript->get_challenge("ZM:y");
401
402 // Compute the batched, lifted-degree quotient \hat{q}
403 auto batched_quotient = compute_batched_lifted_degree_quotient(quotients, y_challenge, N);
404
405 // Compute and send the commitment C_q = [\hat{q}]
406 auto q_commitment = commitment_key->commit(batched_quotient);
407 transcript->send_to_verifier("ZM:C_q", q_commitment);
408
409 // Get challenges x and z
410 auto [x_challenge, z_challenge] = challenges_to_field_elements<FF>(transcript->get_challenges("ZM:x", "ZM:z"));
411
412 // Compute degree check polynomial \zeta partially evaluated at x
413 auto zeta_x =
414 compute_partially_evaluated_degree_check_polynomial(batched_quotient, quotients, y_challenge, x_challenge);
415
416 // Compute ZeroMorph identity polynomial Z partially evaluated at x
418 g_batched,
419 quotients,
420 batched_evaluation,
421 u_challenge,
422 x_challenge,
423 concatenation_groups_batched);
424
425 // Compute batched degree-check and ZM-identity quotient polynomial pi
426 auto pi_polynomial =
427 compute_batched_evaluation_and_degree_check_quotient(zeta_x, Z_x, x_challenge, z_challenge);
428
429 // Compute and send proof commitment pi
430 auto pi_commitment = commitment_key->commit(pi_polynomial);
431 transcript->send_to_verifier("ZM:PI", pi_commitment);
432 }
433};
434
440template <typename Curve> class ZeroMorphVerifier_ {
441 using FF = typename Curve::ScalarField;
442 using Commitment = typename Curve::AffineElement;
443
444 public:
457 static Commitment compute_C_zeta_x(Commitment C_q, std::vector<Commitment>& C_q_k, FF y_challenge, FF x_challenge)
458 {
459 size_t log_N = C_q_k.size();
460 size_t N = 1 << log_N;
461
462 // Instantiate containers for input to batch mul
463 std::vector<FF> scalars;
464 std::vector<Commitment> commitments;
465
466 // Contribution from C_q
467 if constexpr (Curve::is_stdlib_type) {
468 auto builder = x_challenge.get_context();
469 scalars.emplace_back(FF(builder, 1));
470 } else {
471 scalars.emplace_back(FF(1));
472 }
473 commitments.emplace_back(C_q);
474
475 // Contribution from C_q_k, k = 0,...,log_N
476 for (size_t k = 0; k < log_N; ++k) {
477 auto deg_k = static_cast<size_t>((1 << k) - 1);
478 // Compute scalar y^k * x^{N - deg_k - 1}
479 auto scalar = y_challenge.pow(k);
480 scalar *= x_challenge.pow(N - deg_k - 1);
481 scalar *= FF(-1);
482
483 scalars.emplace_back(scalar);
484 commitments.emplace_back(C_q_k[k]);
485 }
486
487 // Compute batch mul to get the result
488 if constexpr (Curve::is_stdlib_type) {
489 return Commitment::batch_mul(commitments, scalars);
490 } else {
491 return batch_mul_native(commitments, scalars);
492 }
493 }
494
519 static Commitment compute_C_Z_x(const std::vector<Commitment>& f_commitments,
520 const std::vector<Commitment>& g_commitments,
521 std::vector<Commitment>& C_q_k,
522 FF rho,
523 FF batched_evaluation,
524 FF x_challenge,
525 std::vector<FF> u_challenge,
526 const std::vector<RefVector<Commitment>>& concatenation_groups_commitments = {})
527 {
528 size_t log_N = C_q_k.size();
529 size_t N = 1 << log_N;
530
531 std::vector<FF> scalars;
532 std::vector<Commitment> commitments;
533
534 // Phi_n(x) = (x^N - 1) / (x - 1)
535 auto phi_numerator = x_challenge.pow(N) - 1; // x^N - 1
536 auto phi_n_x = phi_numerator / (x_challenge - 1);
537
538 // Add contribution: -v * x * \Phi_n(x) * [1]_1
539 if constexpr (Curve::is_stdlib_type) {
540 auto builder = x_challenge.get_context();
541 scalars.emplace_back(FF(builder, -1) * batched_evaluation * x_challenge * phi_n_x);
542 commitments.emplace_back(Commitment::one(builder));
543 } else {
544 scalars.emplace_back(FF(-1) * batched_evaluation * x_challenge * phi_n_x);
545 commitments.emplace_back(Commitment::one());
546 }
547
548 // Add contribution: x * \sum_{i=0}^{m-1} \rho^i*[f_i]
549 auto rho_pow = FF(1);
550 for (auto& commitment : f_commitments) {
551 scalars.emplace_back(x_challenge * rho_pow);
552 commitments.emplace_back(commitment);
553 rho_pow *= rho;
554 }
555
556 // Add contribution: \sum_{i=0}^{l-1} \rho^{m+i}*[g_i]
557 for (auto& commitment : g_commitments) {
558 scalars.emplace_back(rho_pow);
559 commitments.emplace_back(commitment);
560 rho_pow *= rho;
561 }
562
563 // If applicable, add contribution from concatenated polynomial commitments
564 // Note: this is an implementation detail related to Goblin Translator and is not part of the standard protocol.
565 if (!concatenation_groups_commitments.empty()) {
566 size_t CONCATENATION_INDEX = concatenation_groups_commitments[0].size();
567 size_t MINICIRCUIT_N = N / CONCATENATION_INDEX;
568 std::vector<FF> x_shifts;
569 auto current_x_shift = x_challenge;
570 auto x_to_minicircuit_n = x_challenge.pow(MINICIRCUIT_N);
571 for (size_t i = 0; i < CONCATENATION_INDEX; ++i) {
572 x_shifts.emplace_back(current_x_shift);
573 current_x_shift *= x_to_minicircuit_n;
574 }
575 for (auto& concatenation_group_commitment : concatenation_groups_commitments) {
576 for (size_t i = 0; i < CONCATENATION_INDEX; ++i) {
577 scalars.emplace_back(rho_pow * x_shifts[i]);
578 commitments.emplace_back(concatenation_group_commitment[i]);
579 }
580 rho_pow *= rho;
581 }
582 }
583
584 // Add contributions: scalar * [q_k], k = 0,...,log_N, where
585 // scalar = -x * (x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k}))
586 auto x_pow_2k = x_challenge; // x^{2^k}
587 auto x_pow_2kp1 = x_challenge * x_challenge; // x^{2^{k + 1}}
588 for (size_t k = 0; k < log_N; ++k) {
589
590 auto phi_term_1 = phi_numerator / (x_pow_2kp1 - 1); // \Phi_{n-k-1}(x^{2^{k + 1}})
591 auto phi_term_2 = phi_numerator / (x_pow_2k - 1); // \Phi_{n-k}(x^{2^k})
592
593 auto scalar = x_pow_2k * phi_term_1;
594 scalar -= u_challenge[k] * phi_term_2;
595 scalar *= x_challenge;
596 scalar *= FF(-1);
597
598 scalars.emplace_back(scalar);
599 commitments.emplace_back(C_q_k[k]);
600
601 // Update powers of challenge x
602 x_pow_2k = x_pow_2kp1;
603 x_pow_2kp1 *= x_pow_2kp1;
604 }
605
606 if constexpr (Curve::is_stdlib_type) {
607 return Commitment::batch_mul(commitments, scalars);
608 } else {
609 return batch_mul_native(commitments, scalars);
610 }
611 }
612
617 static Commitment batch_mul_native(const std::vector<Commitment>& points, const std::vector<FF>& scalars)
618 {
619 auto result = points[0] * scalars[0];
620 for (size_t idx = 1; idx < scalars.size(); ++idx) {
621 result = result + points[idx] * scalars[idx];
622 }
623 return result;
624 }
625
636 static std::array<Commitment, 2> verify(
637 auto&& unshifted_commitments,
638 auto&& to_be_shifted_commitments,
639 auto&& unshifted_evaluations,
640 auto&& shifted_evaluations,
641 auto& multivariate_challenge,
642 auto& transcript,
643 const std::vector<RefVector<Commitment>>& concatenation_group_commitments = {},
644 const std::vector<FF>& concatenated_evaluations = {})
645 {
646 size_t log_N = multivariate_challenge.size();
647 FF rho = transcript->get_challenge("rho");
648
649 // Construct batched evaluation v = sum_{i=0}^{m-1}\rho^i*f_i(u) + sum_{i=0}^{l-1}\rho^{m+i}*h_i(u)
650 FF batched_evaluation = FF(0);
651 FF batching_scalar = FF(1);
652 for (auto& value : unshifted_evaluations) {
653 batched_evaluation += value * batching_scalar;
654 batching_scalar *= rho;
655 }
656 for (auto& value : shifted_evaluations) {
657 batched_evaluation += value * batching_scalar;
658 batching_scalar *= rho;
659 }
660 for (auto& value : concatenated_evaluations) {
661 batched_evaluation += value * batching_scalar;
662 batching_scalar *= rho;
663 }
664
665 // Receive commitments [q_k]
666 std::vector<Commitment> C_q_k;
667 C_q_k.reserve(log_N);
668 for (size_t i = 0; i < log_N; ++i) {
669 C_q_k.emplace_back(transcript->template receive_from_prover<Commitment>("ZM:C_q_" + std::to_string(i)));
670 }
671
672 // Challenge y
673 FF y_challenge = transcript->get_challenge("ZM:y");
674
675 // Receive commitment C_{q}
676 auto C_q = transcript->template receive_from_prover<Commitment>("ZM:C_q");
677
678 // Challenges x, z
679 auto [x_challenge, z_challenge] = challenges_to_field_elements<FF>(transcript->get_challenges("ZM:x", "ZM:z"));
680
681 // Compute commitment C_{\zeta_x}
682 auto C_zeta_x = compute_C_zeta_x(C_q, C_q_k, y_challenge, x_challenge);
683
684 // Compute commitment C_{Z_x}
685 Commitment C_Z_x = compute_C_Z_x(unshifted_commitments,
686 to_be_shifted_commitments,
687 C_q_k,
688 rho,
689 batched_evaluation,
690 x_challenge,
691 multivariate_challenge,
692 concatenation_group_commitments);
693
694 // Compute commitment C_{\zeta,Z}
695 Commitment C_zeta_Z;
696 if constexpr (Curve::is_stdlib_type) {
697 // Express operation as a batch_mul in order to use Goblinization if available
698 auto builder = rho.get_context();
699 std::vector<FF> scalars = { FF(builder, 1), z_challenge };
700 std::vector<Commitment> points = { C_zeta_x, C_Z_x };
701 C_zeta_Z = Commitment::batch_mul(points, scalars);
702 } else {
703 C_zeta_Z = C_zeta_x + C_Z_x * z_challenge;
704 }
705
706 // Receive proof commitment \pi
707 auto C_pi = transcript->template receive_from_prover<Commitment>("ZM:PI");
708
709 // Construct inputs and perform pairing check to verify claimed evaluation
710 // Note: The pairing check (without the degree check component X^{N_max-N-1}) can be expressed naturally as
711 // e(C_{\zeta,Z}, [1]_2) = e(pi, [X - x]_2). This can be rearranged (e.g. see the plonk paper) as
712 // e(C_{\zeta,Z} - x*pi, [1]_2) * e(-pi, [X]_2) = 1, or
713 // e(P_0, [1]_2) * e(P_1, [X]_2) = 1
714 Commitment P0;
715 if constexpr (Curve::is_stdlib_type) {
716 // Express operation as a batch_mul in order to use Goblinization if available
717 auto builder = rho.get_context();
718 std::vector<FF> scalars = { FF(builder, 1), x_challenge };
719 std::vector<Commitment> points = { C_zeta_Z, C_pi };
720 P0 = Commitment::batch_mul(points, scalars);
721 } else {
722 P0 = C_zeta_Z + C_pi * x_challenge;
723 }
724
725 auto P1 = -C_pi;
726
727 return { P0, P1 };
728 }
729};
730
731} // namespace proof_system::honk::pcs::zeromorph
A template class for a reference vector. Behaves as if std::vector<T&> was possible.
Definition: ref_vector.hpp:20
Definition: polynomial.hpp:12
void factor_roots(std::span< const Fr > roots)
Divides p(X) by (X-r₁)⋯(X−rₘ) in-place. Assumes that p(rⱼ)=0 for all j.
Definition: polynomial.hpp:214
Polynomial shifted() const
Returns an std::span of the left-shift of self.
Definition: polynomial.cpp:323
void add_scaled(std::span< const Fr > other, Fr scaling_factor)
adds the polynomial q(X) 'other', multiplied by a scaling factor.
Definition: polynomial.cpp:367
CommitmentKey object over a pairing group 𝔾₁.
Definition: commitment_key.hpp:35
Prover for ZeroMorph multilinear PCS.
Definition: zeromorph.hpp:33
static std::vector< Polynomial > compute_multilinear_quotients(Polynomial polynomial, std::span< const FF > u_challenge)
Compute multivariate quotients q_k(X_0, ..., X_{k-1}) for f(X_0, ..., X_{n-1})
Definition: zeromorph.hpp:63
static Polynomial compute_partially_evaluated_degree_check_polynomial(Polynomial &batched_quotient, std::vector< Polynomial > &quotients, FF y_challenge, FF x_challenge)
Compute partially evaluated degree check polynomial \zeta_x = q - \sum_k y^k * x^{N - d_k - 1} * q_k.
Definition: zeromorph.hpp:160
static void prove(const std::vector< Polynomial > &f_polynomials, const std::vector< Polynomial > &g_polynomials, const std::vector< FF > &f_evaluations, const std::vector< FF > &g_shift_evaluations, const std::vector< FF > &multilinear_challenge, const std::shared_ptr< CommitmentKey< Curve > > &commitment_key, const std::shared_ptr< BaseTranscript > &transcript, const std::vector< Polynomial > &concatenated_polynomials={}, const std::vector< FF > &concatenated_evaluations={}, const std::vector< RefVector< Polynomial > > &concatenation_groups={})
Prove a set of multilinear evaluation claims for unshifted polynomials f_i and to-be-shifted polynomi...
Definition: zeromorph.hpp:319
static Polynomial compute_batched_evaluation_and_degree_check_quotient(Polynomial &zeta_x, Polynomial &Z_x, FF x_challenge, FF z_challenge)
Compute combined evaluation and degree-check quotient polynomial pi.
Definition: zeromorph.hpp:277
static Polynomial compute_batched_lifted_degree_quotient(std::vector< Polynomial > &quotients, FF y_challenge, size_t N)
Construct batched, lifted-degree univariate quotient \hat{q} = \sum_k y^k * X^{N - d_k - 1} * q_k.
Definition: zeromorph.hpp:123
static Polynomial compute_partially_evaluated_zeromorph_identity_polynomial(Polynomial &f_batched, Polynomial &g_batched, std::vector< Polynomial > &quotients, FF v_evaluation, std::span< const FF > u_challenge, FF x_challenge, std::vector< Polynomial > concatenation_groups_batched={})
Compute partially evaluated zeromorph identity polynomial Z_x.
Definition: zeromorph.hpp:205
Verifier for ZeroMorph multilinear PCS.
Definition: zeromorph.hpp:440
static std::array< Commitment, 2 > verify(auto &&unshifted_commitments, auto &&to_be_shifted_commitments, auto &&unshifted_evaluations, auto &&shifted_evaluations, auto &multivariate_challenge, auto &transcript, const std::vector< RefVector< Commitment > > &concatenation_group_commitments={}, const std::vector< FF > &concatenated_evaluations={})
Verify a set of multilinear evaluation claims for unshifted polynomials f_i and to-be-shifted polynom...
Definition: zeromorph.hpp:636
static Commitment compute_C_zeta_x(Commitment C_q, std::vector< Commitment > &C_q_k, FF y_challenge, FF x_challenge)
Compute commitment to partially evaluated batched lifted degree quotient identity.
Definition: zeromorph.hpp:457
static Commitment batch_mul_native(const std::vector< Commitment > &points, const std::vector< FF > &scalars)
Utility for native batch multiplication of group elements.
Definition: zeromorph.hpp:617
static Commitment compute_C_Z_x(const std::vector< Commitment > &f_commitments, const std::vector< Commitment > &g_commitments, std::vector< Commitment > &C_q_k, FF rho, FF batched_evaluation, FF x_challenge, std::vector< FF > u_challenge, const std::vector< RefVector< Commitment > > &concatenation_groups_commitments={})
Compute commitment to partially evaluated ZeroMorph identity Z.
Definition: zeromorph.hpp:519
Definition: zip_view.hpp:159