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"
8namespace proof_system::honk::pcs::zeromorph {
18template <
class FF>
inline std::vector<FF> powers_of_challenge(
const FF challenge,
const size_t num_powers)
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);
25 return challenge_powers;
34 using FF =
typename Curve::ScalarField;
35 using Commitment =
typename Curve::AffineElement;
40 static const size_t N_max = 1 << 22;
65 size_t log_N = numeric::get_msb(
polynomial.size());
67 ASSERT(log_N == u_challenge.size());
70 std::vector<Polynomial> quotients;
71 for (
size_t k = 0; k < log_N; ++k) {
77 size_t size_q = 1 << (log_N - 1);
79 for (
size_t l = 0; l < size_q; ++l) {
83 quotients[log_N - 1] = q.share();
91 for (
size_t k = 1; k < log_N; ++k) {
93 for (
size_t l = 0; l < size_q; ++l) {
94 f_k[l] = g[l] + u_challenge[log_N - k] * q[l];
100 for (
size_t l = 0; l < size_q; ++l) {
101 q[l] = f_k[size_q + l] - f_k[l];
104 quotients[log_N - k - 1] = q.share();
133 for (
auto& quotient : quotients) {
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];
141 scalar *= y_challenge;
161 std::vector<Polynomial>& quotients,
165 size_t N = batched_quotient.size();
166 size_t log_N = quotients.size();
169 auto result = batched_quotient;
171 auto y_power = FF(1);
172 for (
size_t k = 0; k < log_N; ++k) {
174 auto deg_k =
static_cast<size_t>((1 << k) - 1);
175 auto x_power = x_challenge.pow(N - deg_k - 1);
177 result.add_scaled(quotients[k], -y_power * x_power);
179 y_power *= y_challenge;
208 std::vector<Polynomial>& quotients,
210 std::span<const FF> u_challenge,
212 std::vector<Polynomial> concatenation_groups_batched = {})
214 size_t N = f_batched.size();
215 size_t log_N = quotients.size();
218 auto result = g_batched;
222 auto phi_numerator = x_challenge.pow(N) - 1;
223 auto phi_n_x = phi_numerator / (x_challenge - 1);
224 result[0] -= v_evaluation * x_challenge * phi_n_x;
227 auto x_power = x_challenge;
228 for (
size_t k = 0; k < log_N; ++k) {
229 x_power = x_challenge.pow(1 << k);
232 auto phi_term_1 = phi_numerator / (x_challenge.pow(1 << (k + 1)) - 1);
235 auto phi_term_2 = phi_numerator / (x_challenge.pow(1 << k) - 1);
238 auto scalar = x_power * phi_term_1 - u_challenge[k] * phi_term_2;
240 scalar *= x_challenge;
243 result.add_scaled(quotients[k], scalar);
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);
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;
283 size_t N = zeta_x.size();
291 auto batched_quotient = zeta_x;
292 batched_quotient.
add_scaled(Z_x, z_challenge);
303 auto batched_shifted_quotient = batched_quotient;
305 return batched_shifted_quotient;
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,
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 = {})
331 const FF rho = transcript->get_challenge(
"rho");
334 std::span<const FF> u_challenge = multilinear_challenge;
335 size_t log_N = u_challenge.size();
336 size_t N = 1 << log_N;
344 FF batched_evaluation{ 0 };
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;
353 Polynomial g_batched{ N };
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;
360 size_t num_groups = concatenation_groups.size();
361 size_t num_chunks_per_group = concatenation_groups.empty() ? 0 : concatenation_groups[0].size();
363 Polynomial concatenated_batched(N);
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));
371 for (
size_t i = 0; i < num_groups; ++i) {
372 concatenated_batched.add_scaled(concatenated_polynomials[i], batching_scalar);
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);
377 batched_evaluation += batching_scalar * concatenated_evaluations[i];
378 batching_scalar *= rho;
383 Polynomial f_polynomial = f_batched;
384 f_polynomial += g_batched.
shifted();
385 f_polynomial += concatenated_batched;
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]);
400 FF y_challenge = transcript->get_challenge(
"ZM:y");
406 auto q_commitment = commitment_key->commit(batched_quotient);
407 transcript->send_to_verifier(
"ZM:C_q", q_commitment);
410 auto [x_challenge, z_challenge] = challenges_to_field_elements<FF>(transcript->get_challenges(
"ZM:x",
"ZM:z"));
423 concatenation_groups_batched);
430 auto pi_commitment = commitment_key->commit(pi_polynomial);
431 transcript->send_to_verifier(
"ZM:PI", pi_commitment);
441 using FF =
typename Curve::ScalarField;
442 using Commitment =
typename Curve::AffineElement;
457 static Commitment
compute_C_zeta_x(Commitment C_q, std::vector<Commitment>& C_q_k, FF y_challenge, FF x_challenge)
459 size_t log_N = C_q_k.size();
460 size_t N = 1 << log_N;
463 std::vector<FF> scalars;
464 std::vector<Commitment> commitments;
467 if constexpr (Curve::is_stdlib_type) {
468 auto builder = x_challenge.get_context();
469 scalars.emplace_back(FF(builder, 1));
471 scalars.emplace_back(FF(1));
473 commitments.emplace_back(C_q);
476 for (
size_t k = 0; k < log_N; ++k) {
477 auto deg_k =
static_cast<size_t>((1 << k) - 1);
479 auto scalar = y_challenge.pow(k);
480 scalar *= x_challenge.pow(N - deg_k - 1);
483 scalars.emplace_back(scalar);
484 commitments.emplace_back(C_q_k[k]);
488 if constexpr (Curve::is_stdlib_type) {
489 return Commitment::batch_mul(commitments, scalars);
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,
523 FF batched_evaluation,
525 std::vector<FF> u_challenge,
528 size_t log_N = C_q_k.size();
529 size_t N = 1 << log_N;
531 std::vector<FF> scalars;
532 std::vector<Commitment> commitments;
535 auto phi_numerator = x_challenge.pow(N) - 1;
536 auto phi_n_x = phi_numerator / (x_challenge - 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));
544 scalars.emplace_back(FF(-1) * batched_evaluation * x_challenge * phi_n_x);
545 commitments.emplace_back(Commitment::one());
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);
557 for (
auto& commitment : g_commitments) {
558 scalars.emplace_back(rho_pow);
559 commitments.emplace_back(commitment);
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;
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]);
586 auto x_pow_2k = x_challenge;
587 auto x_pow_2kp1 = x_challenge * x_challenge;
588 for (
size_t k = 0; k < log_N; ++k) {
590 auto phi_term_1 = phi_numerator / (x_pow_2kp1 - 1);
591 auto phi_term_2 = phi_numerator / (x_pow_2k - 1);
593 auto scalar = x_pow_2k * phi_term_1;
594 scalar -= u_challenge[k] * phi_term_2;
595 scalar *= x_challenge;
598 scalars.emplace_back(scalar);
599 commitments.emplace_back(C_q_k[k]);
602 x_pow_2k = x_pow_2kp1;
603 x_pow_2kp1 *= x_pow_2kp1;
606 if constexpr (Curve::is_stdlib_type) {
607 return Commitment::batch_mul(commitments, scalars);
617 static Commitment
batch_mul_native(
const std::vector<Commitment>& points,
const std::vector<FF>& scalars)
619 auto result = points[0] * scalars[0];
620 for (
size_t idx = 1; idx < scalars.size(); ++idx) {
621 result = result + points[idx] * scalars[idx];
637 auto&& unshifted_commitments,
638 auto&& to_be_shifted_commitments,
639 auto&& unshifted_evaluations,
640 auto&& shifted_evaluations,
641 auto& multivariate_challenge,
644 const std::vector<FF>& concatenated_evaluations = {})
646 size_t log_N = multivariate_challenge.size();
647 FF rho = transcript->get_challenge(
"rho");
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;
656 for (
auto& value : shifted_evaluations) {
657 batched_evaluation += value * batching_scalar;
658 batching_scalar *= rho;
660 for (
auto& value : concatenated_evaluations) {
661 batched_evaluation += value * batching_scalar;
662 batching_scalar *= rho;
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)));
673 FF y_challenge = transcript->get_challenge(
"ZM:y");
676 auto C_q = transcript->template receive_from_prover<Commitment>(
"ZM:C_q");
679 auto [x_challenge, z_challenge] = challenges_to_field_elements<FF>(transcript->get_challenges(
"ZM:x",
"ZM:z"));
686 to_be_shifted_commitments,
691 multivariate_challenge,
692 concatenation_group_commitments);
696 if constexpr (Curve::is_stdlib_type) {
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);
703 C_zeta_Z = C_zeta_x + C_Z_x * z_challenge;
707 auto C_pi = transcript->template receive_from_prover<Commitment>(
"ZM:PI");
715 if constexpr (Curve::is_stdlib_type) {
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);
722 P0 = C_zeta_Z + C_pi * x_challenge;
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 > "ients, 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 > "ients, 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 > "ients, 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