diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/goblin_ultra_composer.test.cpp b/barretenberg/cpp/src/barretenberg/honk/composer/goblin_ultra_composer.test.cpp index ee13dff5b1e..584fb41d7a2 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/goblin_ultra_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/goblin_ultra_composer.test.cpp @@ -136,7 +136,7 @@ TEST_F(GoblinUltraHonkComposerTests, MultipleCircuitsMergeOnly) auto composer = GoblinUltraComposer(); - // Construct and verify Goblin ECC op queue Merge its proof + // Construct and verify Goblin ECC op queue Merge proof auto merge_verified = construct_and_verify_merge_proof(composer, op_queue); EXPECT_TRUE(merge_verified); } @@ -196,7 +196,7 @@ TEST_F(GoblinUltraHonkComposerTests, MultipleCircuitsHonkAndMerge) auto honk_verified = construct_and_verify_honk_proof(composer, builder); EXPECT_TRUE(honk_verified); - // Construct and verify Goblin ECC op queue Merge its proof + // Construct and verify Goblin ECC op queue Merge proof auto merge_verified = construct_and_verify_merge_proof(composer, op_queue); EXPECT_TRUE(merge_verified); } diff --git a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp index d9dda108c65..ed3beccf79e 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp @@ -1,11 +1,25 @@ #pragma once #include "barretenberg/polynomials/polynomial.hpp" +namespace proof_system::honk::pcs::zeromorph { + /** - * @brief + * @brief Compute powers of a given challenge * + * @tparam FF + * @param challenge + * @param num_powers + * @return std::vector */ -namespace proof_system::honk::pcs::zeromorph { +template inline std::vector powers_of_challenge(const FF challenge, const size_t num_powers) +{ + std::vector challenge_powers = { FF(1), challenge }; + challenge_powers.reserve(num_powers); + for (size_t j = 2; j < num_powers; j++) { + challenge_powers.emplace_back(challenge_powers[j - 1] * challenge); + } + return challenge_powers; +}; /** * @brief Prover for ZeroMorph multilinear PCS @@ -13,13 +27,13 @@ namespace proof_system::honk::pcs::zeromorph { * @tparam Curve */ template class ZeroMorphProver_ { - using Fr = typename Curve::ScalarField; + using FF = typename Curve::ScalarField; using Commitment = typename Curve::AffineElement; - using Polynomial = barretenberg::Polynomial; + using Polynomial = barretenberg::Polynomial; // TODO(#742): Set this N_max to be the number of G1 elements in the mocked zeromorph SRS once it's in place. (Then, - // eventually, set it based on the real SRS). - static const size_t N_max = 1 << 10; + // eventually, set it based on the real SRS). For now we set it to be large but more or less arbitrary. + static const size_t N_max = 1 << 22; public: /** @@ -33,7 +47,7 @@ template class ZeroMorphProver_ { * * q_k(X_0, ..., X_{k-1}) = f(X_0,...,X_{k-1}, u'') - f(X_0,...,X_{k-1}, u') * - * @note In practice, 2^d is equal to the circuit size + * @note In practice, 2^d is equal to the circuit size N * * TODO(#739): This method has been designed for clarity at the expense of efficiency. Implement the more efficient * algorithm detailed in the latest versions of the ZeroMorph paper. @@ -41,24 +55,24 @@ template class ZeroMorphProver_ { * @param u_challenge Multivariate challenge u = (u_0, ..., u_{d-1}) * @return std::vector The quotients q_k */ - static std::vector compute_multilinear_quotients(Polynomial polynomial, std::span u_challenge) + static std::vector compute_multilinear_quotients(Polynomial polynomial, std::span u_challenge) { - size_t log_poly_size = numeric::get_msb(polynomial.size()); + size_t log_N = numeric::get_msb(polynomial.size()); // The size of the multilinear challenge must equal the log of the polynomial size - ASSERT(log_poly_size == u_challenge.size()); + ASSERT(log_N == u_challenge.size()); - // Define the vector of quotients q_k, k = 0, ..., log_n-1 + // Define the vector of quotients q_k, k = 0, ..., log_N-1 std::vector quotients; - for (size_t k = 0; k < log_poly_size; ++k) { + for (size_t k = 0; k < log_N; ++k) { size_t size = 1 << k; quotients.emplace_back(Polynomial(size)); // degree 2^k - 1 } // Compute the q_k in reverse order, i.e. q_{n-1}, ..., q_0 - for (size_t k = 0; k < log_poly_size; ++k) { + for (size_t k = 0; k < log_N; ++k) { // Define partial evaluation point u' = (u_k, ..., u_{n-1}) auto evaluation_point_size = static_cast(k + 1); - std::vector u_partial(u_challenge.end() - evaluation_point_size, u_challenge.end()); + std::vector u_partial(u_challenge.end() - evaluation_point_size, u_challenge.end()); // Compute f' = f(X_0,...,X_{k-1}, u') auto f_1 = polynomial.partial_evaluate_mle(u_partial); @@ -73,7 +87,7 @@ template class ZeroMorphProver_ { auto q_k = f_2; q_k -= f_1; - quotients[log_poly_size - k - 1] = q_k; + quotients[log_N - k - 1] = q_k; } return quotients; @@ -88,11 +102,11 @@ template class ZeroMorphProver_ { * accumulate them into \hat{q} at the appropriate offset. * * @param quotients Polynomials q_k, interpreted as univariates; deg(q_k) = 2^k - 1 - * @param N + * @param N circuit size * @return Polynomial */ static Polynomial compute_batched_lifted_degree_quotient(std::vector& quotients, - Fr y_challenge, + FF y_challenge, size_t N) { // Batched lifted degree quotient polynomial @@ -100,7 +114,7 @@ template class ZeroMorphProver_ { // Compute \hat{q} = \sum_k y^k * X^{N - d_k - 1} * q_k size_t k = 0; - auto scalar = Fr(1); // y^k + auto scalar = FF(1); // y^k for (auto& quotient : quotients) { // Rather than explicitly computing the shifts of q_k by N - d_k - 1 (i.e. multiplying q_k by X^{N - d_k - // 1}) then accumulating them, we simply accumulate y^k*q_k into \hat{q} at the index offset N - d_k - 1 @@ -130,8 +144,8 @@ template class ZeroMorphProver_ { */ static Polynomial compute_partially_evaluated_degree_check_polynomial(Polynomial& batched_quotient, std::vector& quotients, - Fr y_challenge, - Fr x_challenge) + FF y_challenge, + FF x_challenge) { size_t N = batched_quotient.size(); size_t log_N = quotients.size(); @@ -139,7 +153,7 @@ template class ZeroMorphProver_ { // Initialize partially evaluated degree check polynomial \zeta_x to \hat{q} auto result = batched_quotient; - auto y_power = Fr(1); // y^k + auto y_power = FF(1); // y^k for (size_t k = 0; k < log_N; ++k) { // Accumulate y^k * x^{N - d_k - 1} * q_k into \hat{q} auto deg_k = static_cast((1 << k) - 1); @@ -160,7 +174,7 @@ template class ZeroMorphProver_ { * Z_x = x * f_batched + g_batched - v * x * \Phi_n(x) * - x * \sum_k (x^{2^k}\Phi_{n-k-1}(x^{2^{k-1}}) - u_k\Phi_{n-k}(x^{2^k})) * q_k * - * where f_batched = \sum_{i=0}^{m-1}\alpha^i*f_i, g_batched = \sum_{i=0}^{l-1}\alpha^{m+i}*g_i + * where f_batched = \sum_{i=0}^{m-1}\rho^i*f_i, g_batched = \sum_{i=0}^{l-1}\rho^{m+i}*g_i * * @param input_polynomial * @param quotients @@ -171,17 +185,16 @@ template class ZeroMorphProver_ { static Polynomial compute_partially_evaluated_zeromorph_identity_polynomial(Polynomial& f_batched, Polynomial& g_batched, std::vector& quotients, - Fr v_evaluation, - std::span u_challenge, - Fr x_challenge) + FF v_evaluation, + std::span u_challenge, + FF x_challenge) { size_t N = f_batched.size(); size_t log_N = quotients.size(); // Initialize Z_x with x * \sum_{i=0}^{m-1} f_i + \sum_{i=0}^{l-1} g_i - auto result = Polynomial(N); + auto result = g_batched; result.add_scaled(f_batched, x_challenge); - result += g_batched; // Compute Z_x -= v * x * \Phi_n(x) auto phi_numerator = x_challenge.pow(N) - 1; // x^N - 1 @@ -203,7 +216,7 @@ template class ZeroMorphProver_ { auto scalar = x_power * phi_term_1 - u_challenge[k] * phi_term_2; scalar *= x_challenge; - scalar *= Fr(-1); + scalar *= FF(-1); result.add_scaled(quotients[k], scalar); } @@ -226,8 +239,8 @@ template class ZeroMorphProver_ { */ static Polynomial compute_batched_evaluation_and_degree_check_quotient(Polynomial& zeta_x, Polynomial& Z_x, - Fr x_challenge, - Fr z_challenge) + FF x_challenge, + FF z_challenge) { // We cannot commit to polynomials with size > N_max size_t N = zeta_x.size(); @@ -254,6 +267,103 @@ template class ZeroMorphProver_ { return batched_shifted_quotient; } + + /** + * @brief Prove a set of multilinear evaluation claims for unshifted polynomials f_i and to-be-shifted polynomials + * g_i + * + * @param f_polynomials Unshifted polynomials + * @param g_polynomials To-be-shifted polynomials (of which the shifts h_i were evaluated by sumcheck) + * @param evaluations Set of evaluations v_i = f_i(u), w_i = h_i(u) = g_i_shifted(u) + * @param multilinear_challenge Multilinear challenge point u + * @param commitment_key + * @param transcript + */ + static void prove(const auto& f_polynomials, + const auto& g_polynomials, + auto& evaluations, + auto& multilinear_challenge, + auto& commitment_key, + auto& transcript) + { + // Generate batching challenge \rho and powers 1,...,\rho^{m-1} + FF rho = transcript.get_challenge("rho"); + std::vector rhos = powers_of_challenge(rho, evaluations.size()); + + // Extract multilinear challenge u and claimed multilinear evaluations from Sumcheck output + std::span u_challenge = multilinear_challenge; + std::span claimed_evaluations = evaluations; + size_t log_N = u_challenge.size(); + size_t N = 1 << log_N; + + // Compute batching of unshifted polynomials f_i and to-be-shifted polynomials g_i: + // f_batched = sum_{i=0}^{m-1}\rho^i*f_i and g_batched = sum_{i=0}^{l-1}\rho^{m+i}*g_i, + // and also batched evaluation + // v = sum_{i=0}^{m-1}\rho^i*f_i(u) + sum_{i=0}^{l-1}\rho^{m+i}*h_i(u). + // Note: g_batched is formed from the to-be-shifted polynomials, but the batched evaluation incorporates the + // evaluations produced by sumcheck of h_i = g_i_shifted. + auto batched_evaluation = FF(0); + Polynomial f_batched(N); // batched unshifted polynomials + size_t poly_idx = 0; // TODO(#391) zip + for (auto& f_poly : f_polynomials) { + f_batched.add_scaled(f_poly, rhos[poly_idx]); + batched_evaluation += rhos[poly_idx] * claimed_evaluations[poly_idx]; + ++poly_idx; + } + + Polynomial g_batched(N); // batched to-be-shifted polynomials + for (auto& g_poly : g_polynomials) { + g_batched.add_scaled(g_poly, rhos[poly_idx]); + batched_evaluation += rhos[poly_idx] * claimed_evaluations[poly_idx]; + ++poly_idx; + }; + + // Compute the full batched polynomial f = f_batched + g_batched.shifted() = f_batched + h_batched. This is the + // polynomial for which we compute the quotients q_k and prove f(u) = v_batched. + auto f_polynomial = f_batched; + f_polynomial += g_batched.shifted(); + + // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1}) + auto quotients = compute_multilinear_quotients(f_polynomial, u_challenge); + + // Compute and send commitments C_{q_k} = [q_k], k = 0,...,d-1 + std::vector q_k_commitments; + q_k_commitments.reserve(log_N); + for (size_t idx = 0; idx < log_N; ++idx) { + q_k_commitments[idx] = commitment_key->commit(quotients[idx]); + std::string label = "ZM:C_q_" + std::to_string(idx); + transcript.send_to_verifier(label, q_k_commitments[idx]); + } + + // Get challenge y + auto y_challenge = transcript.get_challenge("ZM:y"); + + // Compute the batched, lifted-degree quotient \hat{q} + auto batched_quotient = compute_batched_lifted_degree_quotient(quotients, y_challenge, N); + + // Compute and send the commitment C_q = [\hat{q}] + auto q_commitment = commitment_key->commit(batched_quotient); + transcript.send_to_verifier("ZM:C_q", q_commitment); + + // Get challenges x and z + auto [x_challenge, z_challenge] = transcript.get_challenges("ZM:x", "ZM:z"); + + // Compute degree check polynomial \zeta partially evaluated at x + auto zeta_x = + compute_partially_evaluated_degree_check_polynomial(batched_quotient, quotients, y_challenge, x_challenge); + + // Compute ZeroMorph identity polynomial Z partially evaluated at x + auto Z_x = compute_partially_evaluated_zeromorph_identity_polynomial( + f_batched, g_batched, quotients, batched_evaluation, u_challenge, x_challenge); + + // Compute batched degree-check and ZM-identity quotient polynomial pi + auto pi_polynomial = + compute_batched_evaluation_and_degree_check_quotient(zeta_x, Z_x, x_challenge, z_challenge); + + // Compute and send proof commitment pi + auto pi_commitment = commitment_key->commit(pi_polynomial); + transcript.send_to_verifier("ZM:PI", pi_commitment); + } }; /** @@ -262,7 +372,7 @@ template class ZeroMorphProver_ { * @tparam Curve */ template class ZeroMorphVerifier_ { - using Fr = typename Curve::ScalarField; + using FF = typename Curve::ScalarField; using Commitment = typename Curve::AffineElement; public: @@ -278,89 +388,218 @@ template class ZeroMorphVerifier_ { * @param x_challenge * @return Commitment */ - static Commitment compute_C_zeta_x(Commitment C_q, std::vector& C_q_k, Fr y_challenge, Fr x_challenge) + static Commitment compute_C_zeta_x(Commitment C_q, std::vector& C_q_k, FF y_challenge, FF x_challenge) { size_t log_N = C_q_k.size(); size_t N = 1 << log_N; - auto result = C_q; + // Instantiate containers for input to batch mul + std::vector scalars; + std::vector commitments; + + // Contribution from C_q + if constexpr (Curve::is_stdlib_type) { + auto builder = x_challenge.get_context(); + scalars.emplace_back(FF(builder, 1)); + } else { + scalars.emplace_back(FF(1)); + } + commitments.emplace_back(C_q); + + // Contribution from C_q_k, k = 0,...,log_N for (size_t k = 0; k < log_N; ++k) { auto deg_k = static_cast((1 << k) - 1); // Compute scalar y^k * x^{N - deg_k - 1} auto scalar = y_challenge.pow(k); scalar *= x_challenge.pow(N - deg_k - 1); - scalar *= Fr(-1); + scalar *= FF(-1); - result = result + C_q_k[k] * scalar; + scalars.emplace_back(scalar); + commitments.emplace_back(C_q_k[k]); + } + + // Compute batch mul to get the result + if constexpr (Curve::is_stdlib_type) { + return Commitment::batch_mul(commitments, scalars); + } else { + return batch_mul_native(commitments, scalars); } - return result; } /** * @brief Compute commitment to partially evaluated ZeroMorph identity Z * @details Compute commitment C_{Z_x} = [Z_x]_1 using homomorphicity: * - * C_{Z_x} = x * \sum_{i=0}^{m-1}\alpha^i*[f_i] + \sum_{i=0}^{l-1}\alpha^{m+i}*[g_i] - C_v_x + * C_{Z_x} = x * \sum_{i=0}^{m-1}\rho^i*[f_i] + \sum_{i=0}^{l-1}\rho^{m+i}*[g_i] - v * x * \Phi_n(x) * [1]_1 * - x * \sum_k (x^{2^k}\Phi_{n-k-1}(x^{2^{k-1}}) - u_k\Phi_{n-k}(x^{2^k})) * [q_k] * - * @param C_v_x v * x * \Phi_n(x) * [1]_1 * @param f_commitments Commitments to unshifted polynomials [f_i] * @param g_commitments Commitments to to-be-shifted polynomials [g_i] * @param C_q_k Commitments to q_k - * @param alpha + * @param rho + * @param batched_evaluation \sum_{i=0}^{m-1} \rho^i*f_i(u) + \sum_{i=0}^{l-1} \rho^{m+i}*h_i(u) * @param x_challenge * @param u_challenge multilinear challenge * @return Commitment */ - static Commitment compute_C_Z_x(Commitment C_v_x, - std::vector& f_commitments, - std::vector& g_commitments, + static Commitment compute_C_Z_x(std::vector f_commitments, + std::vector g_commitments, std::vector& C_q_k, - Fr alpha, - Fr x_challenge, - std::vector u_challenge) + FF rho, + FF batched_evaluation, + FF x_challenge, + std::vector u_challenge) { size_t log_N = C_q_k.size(); size_t N = 1 << log_N; + std::vector scalars; + std::vector commitments; + + // Phi_n(x) = (x^N - 1) / (x - 1) auto phi_numerator = x_challenge.pow(N) - 1; // x^N - 1 - // auto phi_n_x = phi_numerator / (x_challenge - 1); + auto phi_n_x = phi_numerator / (x_challenge - 1); - Commitment result = -C_v_x; // initialize with -C_{v,x} - auto alpha_pow = Fr(1); - // Add contribution x * \sum_{i=0}^{m-1} [f_i] + // Add contribution: -v * x * \Phi_n(x) * [1]_1 + if constexpr (Curve::is_stdlib_type) { + auto builder = x_challenge.get_context(); + scalars.emplace_back(FF(builder, -1) * batched_evaluation * x_challenge * phi_n_x); + commitments.emplace_back(Commitment::one(builder)); + } else { + scalars.emplace_back(FF(-1) * batched_evaluation * x_challenge * phi_n_x); + commitments.emplace_back(Commitment::one()); + } + + // Add contribution: x * \sum_{i=0}^{m-1} \rho^i*[f_i] + auto rho_pow = FF(1); for (auto& commitment : f_commitments) { - auto scalar = x_challenge * alpha_pow; - result = result + (commitment * scalar); - alpha_pow *= alpha; + scalars.emplace_back(x_challenge * rho_pow); + commitments.emplace_back(commitment); + rho_pow *= rho; } - // Add contribution \sum_{i=0}^{l-1} [g_i] + + // Add contribution: \sum_{i=0}^{l-1} \rho^{m+i}*[g_i] for (auto& commitment : g_commitments) { - auto scalar = alpha_pow; - result = result + (commitment * scalar); - alpha_pow *= alpha; + scalars.emplace_back(rho_pow); + commitments.emplace_back(commitment); + rho_pow *= rho; } - // Add contribution from q_k commitments + // Add contributions: scalar * [q_k], k = 0,...,log_N, where + // scalar = -x * (x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k})) + auto x_pow_2k = x_challenge; // x^{2^k} + auto x_pow_2kp1 = x_challenge * x_challenge; // x^{2^{k + 1}} for (size_t k = 0; k < log_N; ++k) { - // Compute scalar x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k}) - auto x_pow_2k = x_challenge.pow(1 << k); // x^{2^k} - // \Phi_{n-k-1}(x^{2^{k + 1}}) - auto phi_term_1 = phi_numerator / (x_challenge.pow(1 << (k + 1)) - 1); - - // \Phi_{n-k}(x^{2^k}) - auto phi_term_2 = phi_numerator / (x_challenge.pow(1 << k) - 1); + auto phi_term_1 = phi_numerator / (x_pow_2kp1 - 1); // \Phi_{n-k-1}(x^{2^{k + 1}}) + auto phi_term_2 = phi_numerator / (x_pow_2k - 1); // \Phi_{n-k}(x^{2^k}) auto scalar = x_pow_2k * phi_term_1; scalar -= u_challenge[k] * phi_term_2; scalar *= x_challenge; - scalar *= Fr(-1); + scalar *= FF(-1); + + scalars.emplace_back(scalar); + commitments.emplace_back(C_q_k[k]); - result = result + C_q_k[k] * scalar; + // Update powers of challenge x + x_pow_2k = x_pow_2kp1; + x_pow_2kp1 *= x_pow_2kp1; + } + + if constexpr (Curve::is_stdlib_type) { + return Commitment::batch_mul(commitments, scalars); + } else { + return batch_mul_native(commitments, scalars); + } + } + + /** + * @brief Utility for native batch multiplication of group elements + * @note This is used only for native verification and is not optimized for efficiency + */ + static Commitment batch_mul_native(std::vector points, std::vector scalars) + { + auto result = points[0] * scalars[0]; + for (size_t idx = 1; idx < scalars.size(); ++idx) { + result = result + points[idx] * scalars[idx]; } return result; } + + /** + * @brief Verify a set of multilinear evaluation claims for unshifted polynomials f_i and to-be-shifted polynomials + * g_i + * + * @param commitments Commitments to polynomials f_i and g_i (unshifted and to-be-shifted) + * @param claimed_evaluations Claimed evaluations v_i = f_i(u) and w_i = h_i(u) = g_i_shifted(u) + * @param multivariate_challenge Challenge point u + * @param transcript + * @return std::array Inputs to the final pairing check + */ + static std::array verify(auto& commitments, + auto& claimed_evaluations, + auto& multivariate_challenge, + auto& transcript) + { + size_t log_N = multivariate_challenge.size(); + FF rho = transcript.get_challenge("rho"); + + // Compute powers of batching challenge rho + std::vector rhos = pcs::zeromorph::powers_of_challenge(rho, claimed_evaluations.size()); + + // 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) + FF batched_evaluation = FF(0); + size_t evaluation_idx = 0; + for (auto& value : claimed_evaluations.get_unshifted_then_shifted()) { + batched_evaluation += value * rhos[evaluation_idx]; + ++evaluation_idx; + } + + // Receive commitments [q_k] + std::vector C_q_k; + C_q_k.reserve(log_N); + for (size_t i = 0; i < log_N; ++i) { + C_q_k.emplace_back(transcript.template receive_from_prover("ZM:C_q_" + std::to_string(i))); + } + + // Challenge y + auto y_challenge = transcript.get_challenge("ZM:y"); + + // Receive commitment C_{q} + auto C_q = transcript.template receive_from_prover("ZM:C_q"); + + // Challenges x, z + auto [x_challenge, z_challenge] = transcript.get_challenges("ZM:x", "ZM:z"); + + // Compute commitment C_{\zeta_x} + auto C_zeta_x = compute_C_zeta_x(C_q, C_q_k, y_challenge, x_challenge); + + // Compute commitment C_{Z_x} + Commitment C_Z_x = compute_C_Z_x(commitments.get_unshifted(), + commitments.get_to_be_shifted(), + C_q_k, + rho, + batched_evaluation, + x_challenge, + multivariate_challenge); + + // Compute commitment C_{\zeta,Z} + auto C_zeta_Z = C_zeta_x + C_Z_x * z_challenge; + + // Receive proof commitment \pi + auto C_pi = transcript.template receive_from_prover("ZM:PI"); + + // Construct inputs and perform pairing check to verify claimed evaluation + // Note: The pairing check (without the degree check component X^{N_max-N-1}) can be expressed naturally as + // e(C_{\zeta,Z}, [1]_2) = e(pi, [X - x]_2). This can be rearranged (e.g. see the plonk paper) as + // e(C_{\zeta,Z} - x*pi, [1]_2) * e(-pi, [X]_2) = 1, or + // e(P_0, [1]_2) * e(P_1, [X]_2) = 1 + auto P0 = C_zeta_Z + C_pi * x_challenge; + auto P1 = -C_pi; + + return { P0, P1 }; + } }; } // namespace proof_system::honk::pcs::zeromorph \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp index 8424491ba91..2dd12c429f9 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp @@ -81,24 +81,24 @@ template class ZeroMorphTest : public CommitmentTest { // Execute Prover protocol { - auto alpha = prover_transcript.get_challenge("ZM:alpha"); + auto rho = prover_transcript.get_challenge("ZM:rho"); - // Compute batching of f_i and g_i polynomials: sum_{i=0}^{m-1}\alpha^i*f_i and - // sum_{i=0}^{l-1}\alpha^{m+i}*h_i, and also batched evaluation v = sum_{i=0}^{m-1}\alpha^i*v_i + - // sum_{i=0}^{l-1}\alpha^{m+i}*w_i. + // Compute batching of f_i and g_i polynomials: sum_{i=0}^{m-1}\rho^i*f_i and + // sum_{i=0}^{l-1}\rho^{m+i}*h_i, and also batched evaluation v = sum_{i=0}^{m-1}\rho^i*v_i + + // sum_{i=0}^{l-1}\rho^{m+i}*w_i. auto f_batched = Polynomial(N); auto g_batched = Polynomial(N); auto v_evaluation = Fr(0); - auto alpha_pow = Fr(1); + auto rho_pow = Fr(1); for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { - f_batched.add_scaled(f_polynomials[i], alpha_pow); - v_evaluation += alpha_pow * v_evaluations[i]; - alpha_pow *= alpha; + f_batched.add_scaled(f_polynomials[i], rho_pow); + v_evaluation += rho_pow * v_evaluations[i]; + rho_pow *= rho; } for (size_t i = 0; i < NUM_SHIFTED; ++i) { - g_batched.add_scaled(g_polynomials[i], alpha_pow); - v_evaluation += alpha_pow * w_evaluations[i]; - alpha_pow *= alpha; + g_batched.add_scaled(g_polynomials[i], rho_pow); + v_evaluation += rho_pow * w_evaluations[i]; + rho_pow *= rho; } // The new f is f_batched + g_batched.shifted() = f_batched + h_batched @@ -151,19 +151,19 @@ template class ZeroMorphTest : public CommitmentTest { // Execute Verifier protocol { - // Challenge alpha - auto alpha = verifier_transcript.get_challenge("ZM:alpha"); + // Challenge rho + auto rho = verifier_transcript.get_challenge("ZM:rho"); - // Construct batched evaluation v = sum_{i=0}^{m-1}\alpha^i*v_i + sum_{i=0}^{l-1}\alpha^{m+i}*w_i + // Construct batched evaluation v = sum_{i=0}^{m-1}\rho^i*v_i + sum_{i=0}^{l-1}\rho^{m+i}*w_i auto v_evaluation = Fr(0); - auto alpha_pow = Fr(1); + auto rho_pow = Fr(1); for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { - v_evaluation += alpha_pow * v_evaluations[i]; - alpha_pow *= alpha; + v_evaluation += rho_pow * v_evaluations[i]; + rho_pow *= rho; } for (size_t i = 0; i < NUM_SHIFTED; ++i) { - v_evaluation += alpha_pow * w_evaluations[i]; - alpha_pow *= alpha; + v_evaluation += rho_pow * w_evaluations[i]; + rho_pow *= rho; } // Receive commitments [q_k] @@ -183,15 +183,12 @@ template class ZeroMorphTest : public CommitmentTest { // Challenges x, z auto [x_challenge, z_challenge] = verifier_transcript.get_challenges("ZM:x", "ZM:z"); - // Compute commitment C_{v,x} = v * x * \Phi_n(x) * [1]_1 - auto C_v_x = Commitment::one() * v_evaluation * x_challenge * this->Phi(x_challenge, log_N); - // Compute commitment C_{\zeta_x} auto C_zeta_x = ZeroMorphVerifier::compute_C_zeta_x(C_q, C_q_k, y_challenge, x_challenge); // Compute commitment C_{Z_x} Commitment C_Z_x = ZeroMorphVerifier::compute_C_Z_x( - C_v_x, f_commitments, g_commitments, C_q_k, alpha, x_challenge, u_challenge); + f_commitments, g_commitments, C_q_k, rho, v_evaluation, x_challenge, u_challenge); // Compute commitment C_{\zeta,Z} auto C_zeta_Z = C_zeta_x + C_Z_x * z_challenge; @@ -418,13 +415,13 @@ TYPED_TEST(ZeroMorphTest, PartiallyEvaluatedQuotientZ) Fr v_evaluation = multilinear_f.evaluate_mle(u_challenge); Fr w_evaluation = multilinear_g.evaluate_mle(u_challenge, /* shift = */ true); - auto alpha = Fr::random_element(); + auto rho = Fr::random_element(); // compute batched polynomial and evaluation auto f_batched = multilinear_f; auto g_batched = multilinear_g; - g_batched *= alpha; - auto v_batched = v_evaluation + alpha * w_evaluation; + g_batched *= rho; + auto v_batched = v_evaluation + rho * w_evaluation; // Define some mock q_k with deg(q_k) = 2^k - 1 auto q_0 = this->random_polynomial(1 << 0); diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp index 887bb096a2a..e5cb11ee069 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp @@ -1,9 +1,6 @@ #include "ultra_prover.hpp" -#include "barretenberg/honk/pcs/claim.hpp" #include "barretenberg/honk/sumcheck/sumcheck.hpp" #include "barretenberg/honk/utils/power_polynomial.hpp" -#include "barretenberg/polynomials/polynomial.hpp" -#include "barretenberg/transcript/transcript_wrappers.hpp" namespace proof_system::honk { @@ -111,99 +108,18 @@ template void UltraProver_::execute_relation_check_ } /** - * - Get rho challenge - * - Compute d+1 Fold polynomials and their evaluations. + * @brief Execute the ZeroMorph protocol to prove the multilinear evaluations produced by Sumcheck + * @details See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the unrolled protocol. * * */ -template void UltraProver_::execute_univariatization_round() +template void UltraProver_::execute_zeromorph_rounds() { - const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; - - // Generate batching challenge ρ and powers 1,ρ,…,ρᵐ⁻¹ - FF rho = transcript.get_challenge("rho"); - std::vector rhos = pcs::gemini::powers_of_rho(rho, NUM_POLYNOMIALS); - - // Batch the unshifted polynomials and the to-be-shifted polynomials using ρ - Polynomial batched_poly_unshifted(instance->proving_key->circuit_size); // batched unshifted polynomials - size_t poly_idx = 0; // TODO(https://github.com/AztecProtocol/barretenberg/issues/391) zip - for (auto& unshifted_poly : instance->prover_polynomials.get_unshifted()) { - batched_poly_unshifted.add_scaled(unshifted_poly, rhos[poly_idx]); - ++poly_idx; - } - - Polynomial batched_poly_to_be_shifted(instance->proving_key->circuit_size); // batched to-be-shifted polynomials - for (auto& to_be_shifted_poly : instance->prover_polynomials.get_to_be_shifted()) { - batched_poly_to_be_shifted.add_scaled(to_be_shifted_poly, rhos[poly_idx]); - ++poly_idx; - }; - - // Compute d-1 polynomials Fold^(i), i = 1, ..., d-1. - gemini_polynomials = Gemini::compute_gemini_polynomials( - sumcheck_output.challenge, std::move(batched_poly_unshifted), std::move(batched_poly_to_be_shifted)); - - // Compute and add to trasnscript the commitments [Fold^(i)], i = 1, ..., d-1 - for (size_t l = 0; l < instance->proving_key->log_circuit_size - 1; ++l) { - queue.add_commitment(gemini_polynomials[l + 2], "Gemini:FOLD_" + std::to_string(l + 1)); - } -} - -/** - * - Do Fiat-Shamir to get "r" challenge - * - Compute remaining two partially evaluated Fold polynomials Fold_{r}^(0) and Fold_{-r}^(0). - * - Compute and aggregate opening pairs (challenge, evaluation) for each of d Fold polynomials. - * - Add d-many Fold evaluations a_i, i = 0, ..., d-1 to the transcript, excluding eval of Fold_{r}^(0) - * */ -template void UltraProver_::execute_pcs_evaluation_round() -{ - const FF r_challenge = transcript.get_challenge("Gemini:r"); - univariate_openings = Gemini::compute_fold_polynomial_evaluations( - sumcheck_output.challenge, std::move(gemini_polynomials), r_challenge); - - for (size_t l = 0; l < instance->proving_key->log_circuit_size; ++l) { - std::string label = "Gemini:a_" + std::to_string(l); - const auto& evaluation = univariate_openings.opening_pairs[l + 1].evaluation; - transcript.send_to_verifier(label, evaluation); - } -} - -/** - * - Do Fiat-Shamir to get "nu" challenge. - * - Compute commitment [Q]_1 - * */ -template void UltraProver_::execute_shplonk_batched_quotient_round() -{ - nu_challenge = transcript.get_challenge("Shplonk:nu"); - - batched_quotient_Q = Shplonk::compute_batched_quotient( - univariate_openings.opening_pairs, univariate_openings.witnesses, nu_challenge); - - // commit to Q(X) and add [Q] to the transcript - queue.add_commitment(batched_quotient_Q, "Shplonk:Q"); -} - -/** - * - Do Fiat-Shamir to get "z" challenge. - * - Compute polynomial Q(X) - Q_z(X) - * */ -template void UltraProver_::execute_shplonk_partial_evaluation_round() -{ - const FF z_challenge = transcript.get_challenge("Shplonk:z"); - - shplonk_output = Shplonk::compute_partially_evaluated_batched_quotient(univariate_openings.opening_pairs, - univariate_openings.witnesses, - std::move(batched_quotient_Q), - nu_challenge, - z_challenge); -} -/** - * - Compute final PCS opening proof: - * - For KZG, this is the quotient commitmecnt [W]_1 - * - For IPA, the vectors L and R - * */ -template void UltraProver_::execute_final_pcs_round() -{ - PCS::compute_opening_proof(pcs_commitment_key, shplonk_output.opening_pair, shplonk_output.witness, transcript); - // queue.add_commitment(quotient_W, "KZG:W"); + ZeroMorph::prove(instance->prover_polynomials.get_unshifted(), + instance->prover_polynomials.get_to_be_shifted(), + sumcheck_output.claimed_evaluations, + sumcheck_output.challenge, + pcs_commitment_key, + transcript); } template plonk::proof& UltraProver_::export_proof() @@ -234,27 +150,9 @@ template plonk::proof& UltraProver_::construct_proo // Run sumcheck subprotocol. execute_relation_check_rounds(); - // Fiat-Shamir: rho - // Compute Fold polynomials and their commitments. - execute_univariatization_round(); - queue.process_queue(); - - // Fiat-Shamir: r - // Compute Fold evaluations - execute_pcs_evaluation_round(); - - // Fiat-Shamir: nu - // Compute Shplonk batched quotient commitment Q - execute_shplonk_batched_quotient_round(); - queue.process_queue(); - - // Fiat-Shamir: z - // Compute partial evaluation Q_z - execute_shplonk_partial_evaluation_round(); - - // Fiat-Shamir: z - // Compute PCS opening proof (either KZG quotient commitment or IPA opening proof) - execute_final_pcs_round(); + // Fiat-Shamir: rho, y, x, z + // Execute Zeromorph multilinear PCS + execute_zeromorph_rounds(); return export_proof(); } diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp index 43388fa380a..19a70210890 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp @@ -3,8 +3,7 @@ #include "barretenberg/honk/flavor/ultra.hpp" #include "barretenberg/honk/flavor/ultra_grumpkin.hpp" #include "barretenberg/honk/instance/prover_instance.hpp" -#include "barretenberg/honk/pcs/gemini/gemini.hpp" -#include "barretenberg/honk/pcs/shplonk/shplonk.hpp" +#include "barretenberg/honk/pcs/zeromorph/zeromorph.hpp" #include "barretenberg/honk/proof_system/work_queue.hpp" #include "barretenberg/honk/sumcheck/sumcheck_output.hpp" #include "barretenberg/honk/transcript/transcript.hpp" @@ -16,7 +15,6 @@ namespace proof_system::honk { template class UltraProver_ { using FF = typename Flavor::FF; using Commitment = typename Flavor::Commitment; - using PCS = typename Flavor::PCS; using CommitmentKey = typename Flavor::CommitmentKey; using ProvingKey = typename Flavor::ProvingKey; using Polynomial = typename Flavor::Polynomial; @@ -24,7 +22,6 @@ template class UltraProver_ { using CommitmentLabels = typename Flavor::CommitmentLabels; using Curve = typename Flavor::Curve; using Instance = ProverInstance_; - using OpenPair = pcs::OpeningPair; public: explicit UltraProver_(std::shared_ptr); @@ -33,12 +30,7 @@ template class UltraProver_ { void execute_sorted_list_accumulator_round(); void execute_grand_product_computation_round(); void execute_relation_check_rounds(); - void execute_univariatization_round(); - void execute_pcs_evaluation_round(); - void execute_op_queue_transcript_aggregation_round(); - void execute_shplonk_batched_quotient_round(); - void execute_shplonk_partial_evaluation_round(); - void execute_final_pcs_round(); + void execute_zeromorph_rounds(); plonk::proof& export_proof(); plonk::proof& construct_proof(); @@ -50,12 +42,6 @@ template class UltraProver_ { CommitmentLabels commitment_labels; - // Container for d + 1 Fold polynomials produced by Gemini - std::vector gemini_polynomials; - - Polynomial batched_quotient_Q; // batched quotient poly computed by Shplonk - FF nu_challenge; // needed in both Shplonk rounds - Polynomial quotient_W; work_queue queue; @@ -63,12 +49,9 @@ template class UltraProver_ { std::shared_ptr instance; sumcheck::SumcheckOutput sumcheck_output; - pcs::gemini::ProverOutput univariate_openings; - pcs::shplonk::ProverOutput shplonk_output; std::shared_ptr pcs_commitment_key; - using Gemini = pcs::gemini::GeminiProver_; - using Shplonk = pcs::shplonk::ShplonkProver_; + using ZeroMorph = pcs::zeromorph::ZeroMorphProver_; private: plonk::proof proof; diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp index 2b98a316108..0a29e7b08d9 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp @@ -1,7 +1,5 @@ #include "./ultra_verifier.hpp" -#include "barretenberg/honk/pcs/claim.hpp" -#include "barretenberg/honk/pcs/gemini/gemini.hpp" -#include "barretenberg/honk/pcs/shplonk/shplonk.hpp" +#include "barretenberg/honk/pcs/zeromorph/zeromorph.hpp" #include "barretenberg/honk/transcript/transcript.hpp" #include "barretenberg/honk/utils/power_polynomial.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" @@ -36,12 +34,9 @@ template UltraVerifier_& UltraVerifier_::opera template bool UltraVerifier_::verify_proof(const plonk::proof& proof) { using FF = typename Flavor::FF; - using GroupElement = typename Flavor::GroupElement; using Commitment = typename Flavor::Commitment; - using PCS = typename Flavor::PCS; using Curve = typename Flavor::Curve; - using Gemini = pcs::gemini::GeminiVerifier_; - using Shplonk = pcs::shplonk::ShplonkVerifier_; + using ZeroMorph = pcs::zeromorph::ZeroMorphVerifier_; using VerifierCommitments = typename Flavor::VerifierCommitments; using CommitmentLabels = typename Flavor::CommitmentLabels; @@ -114,7 +109,7 @@ template bool UltraVerifier_::verify_proof(const plonk // Execute Sumcheck Verifier auto sumcheck = SumcheckVerifier(circuit_size); - auto [multivariate_challenge, purported_evaluations, sumcheck_verified] = + auto [multivariate_challenge, claimed_evaluations, sumcheck_verified] = sumcheck.verify(relation_parameters, transcript); // If Sumcheck did not verify, return false @@ -122,53 +117,11 @@ template bool UltraVerifier_::verify_proof(const plonk return false; } - // Execute Gemini/Shplonk verification: + // Execute ZeroMorph rounds. See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the + // unrolled protocol. + auto pairing_points = ZeroMorph::verify(commitments, claimed_evaluations, multivariate_challenge, transcript); - // Construct inputs for Gemini verifier: - // - Multivariate opening point u = (u_0, ..., u_{d-1}) - // - batched unshifted and to-be-shifted polynomial commitments - auto batched_commitment_unshifted = GroupElement::zero(); - auto batched_commitment_to_be_shifted = GroupElement::zero(); - - // Compute powers of batching challenge rho - FF rho = transcript.get_challenge("rho"); - std::vector rhos = pcs::gemini::powers_of_rho(rho, Flavor::NUM_ALL_ENTITIES); - - // Compute batched multivariate evaluation - FF batched_evaluation = FF::zero(); - size_t evaluation_idx = 0; - for (auto& value : purported_evaluations.get_unshifted_then_shifted()) { - batched_evaluation += value * rhos[evaluation_idx]; - ++evaluation_idx; - } - - // Construct batched commitment for NON-shifted polynomials - size_t commitment_idx = 0; - for (auto& commitment : commitments.get_unshifted()) { - batched_commitment_unshifted += commitment * rhos[commitment_idx]; - ++commitment_idx; - } - - // Construct batched commitment for to-be-shifted polynomials - for (auto& commitment : commitments.get_to_be_shifted()) { - batched_commitment_to_be_shifted += commitment * rhos[commitment_idx]; - ++commitment_idx; - } - - // Produce a Gemini claim consisting of: - // - d+1 commitments [Fold_{r}^(0)], [Fold_{-r}^(0)], and [Fold^(l)], l = 1:d-1 - // - d+1 evaluations a_0_pos, and a_l, l = 0:d-1 - auto univariate_opening_claims = Gemini::reduce_verification(multivariate_challenge, - batched_evaluation, - batched_commitment_unshifted, - batched_commitment_to_be_shifted, - transcript); - - // Produce a Shplonk claim: commitment [Q] - [Q_z], evaluation zero (at random challenge z) - auto shplonk_claim = Shplonk::reduce_verification(pcs_verification_key, univariate_opening_claims, transcript); - - // Verify the Shplonk claim with KZG or IPA - auto verified = PCS::verify(pcs_verification_key, shplonk_claim, transcript); + auto verified = pcs_verification_key->pairing_check(pairing_points[0], pairing_points[1]); return sumcheck_verified.value() && verified; } diff --git a/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp b/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp index df3ca8526dd..bf563f2fe5c 100644 --- a/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp @@ -72,23 +72,16 @@ class UltraTranscriptTests : public ::testing::Test { manifest_expected.add_entry(round, "Sumcheck:evaluations", size_evals); manifest_expected.add_challenge(round, "rho"); - round++; - for (size_t i = 1; i < log_n; ++i) { - std::string idx = std::to_string(i); - manifest_expected.add_entry(round, "Gemini:FOLD_" + idx, size_G); - } - manifest_expected.add_challenge(round, "Gemini:r"); - round++; for (size_t i = 0; i < log_n; ++i) { std::string idx = std::to_string(i); - manifest_expected.add_entry(round, "Gemini:a_" + idx, size_FF); + manifest_expected.add_entry(round, "ZM:C_q_" + idx, size_G); } - manifest_expected.add_challenge(round, "Shplonk:nu"); + manifest_expected.add_challenge(round, "ZM:y"); round++; - manifest_expected.add_entry(round, "Shplonk:Q", size_G); - manifest_expected.add_challenge(round, "Shplonk:z"); + manifest_expected.add_entry(round, "ZM:C_q", size_G); + manifest_expected.add_challenge(round, "ZM:x", "ZM:z"); round++; // TODO(Mara): Make testing more flavor agnostic so we can test this with all flavors @@ -108,7 +101,7 @@ class UltraTranscriptTests : public ::testing::Test { round++; manifest_expected.add_entry(round, "IPA:a_0", size_FF); } else { - manifest_expected.add_entry(round, "KZG:W", size_G); + manifest_expected.add_entry(round, "ZM:PI", size_G); } manifest_expected.add_challenge(round); // no challenge diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/transcript/transcript.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/transcript/transcript.hpp index f02354207cb..3b25af820c1 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/transcript/transcript.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/transcript/transcript.hpp @@ -60,7 +60,7 @@ template class Transcript { */ std::array challenges; for (size_t i = 0; i < num_challenges; ++i) { - challenges[i] = native_challenges[i]; + challenges[i] = field_ct::from_witness(builder, native_challenges[i]); } return challenges; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp index ac66bc3cf77..068a62ca1c8 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp @@ -1,6 +1,5 @@ #include "barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp" -#include "barretenberg/honk/pcs/gemini/gemini.hpp" -#include "barretenberg/honk/pcs/shplonk/shplonk.hpp" +#include "barretenberg/honk/pcs/zeromorph/zeromorph.hpp" #include "barretenberg/honk/transcript/transcript.hpp" #include "barretenberg/honk/utils/grand_product_delta.hpp" #include "barretenberg/honk/utils/power_polynomial.hpp" @@ -24,9 +23,7 @@ std::array UltraRecursiveVerifier_::ve { using Sumcheck = ::proof_system::honk::sumcheck::SumcheckVerifier; using Curve = typename Flavor::Curve; - using Gemini = ::proof_system::honk::pcs::gemini::GeminiVerifier_; - using Shplonk = ::proof_system::honk::pcs::shplonk::ShplonkVerifier_; - using KZG = ::proof_system::honk::pcs::kzg::KZG; // note: This can only be KZG + using ZeroMorph = ::proof_system::honk::pcs::zeromorph::ZeroMorphVerifier_; using VerifierCommitments = typename Flavor::VerifierCommitments; using CommitmentLabels = typename Flavor::CommitmentLabels; using RelationParams = ::proof_system::RelationParameters; @@ -100,7 +97,7 @@ std::array UltraRecursiveVerifier_::ve // Execute Sumcheck Verifier and extract multivariate opening point u = (u_0, ..., u_{d-1}) and purported // multivariate evaluations at u auto sumcheck = Sumcheck(key->circuit_size); - auto [multivariate_challenge, purported_evaluations, verified] = sumcheck.verify(relation_parameters, transcript); + auto [multivariate_challenge, claimed_evaluations, verified] = sumcheck.verify(relation_parameters, transcript); info("Sumcheck: num gates = ", builder->get_num_gates() - prev_num_gates, @@ -109,94 +106,8 @@ std::array UltraRecursiveVerifier_::ve ")"); prev_num_gates = builder->get_num_gates(); - // Compute powers of batching challenge rho - FF rho = transcript.get_challenge("rho"); - std::vector rhos = ::proof_system::honk::pcs::gemini::powers_of_rho(rho, Flavor::NUM_ALL_ENTITIES); - - // Compute batched multivariate evaluation - FF batched_evaluation = FF(0); - size_t evaluation_idx = 0; - for (auto& value : purported_evaluations.get_unshifted_then_shifted()) { - batched_evaluation += value * rhos[evaluation_idx]; - ++evaluation_idx; - } - - info("Batched eval: num gates = ", - builder->get_num_gates() - prev_num_gates, - ", (total = ", - builder->get_num_gates(), - ")"); - prev_num_gates = builder->get_num_gates(); - - // Compute batched commitments needed for input to Gemini. - // Note: For efficiency in emulating the construction of the batched commitments, we want to perform a batch mul - // rather than naively accumulate the points one by one. To do this, we collect the points and scalars required for - // each MSM then perform the two batch muls. - const size_t NUM_UNSHIFTED = commitments.get_unshifted().size(); - const size_t NUM_TO_BE_SHIFTED = commitments.get_to_be_shifted().size(); - std::vector scalars_unshifted; - std::vector scalars_to_be_shifted; - size_t idx = 0; - for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { - scalars_unshifted.emplace_back(rhos[idx++]); - } - for (size_t i = 0; i < NUM_TO_BE_SHIFTED; ++i) { - scalars_to_be_shifted.emplace_back(rhos[idx++]); - } - // TODO(luke): The powers_of_rho fctn does not set the context of rhos[0] = FF(1) so we do it explicitly here. Can - // we do something silly like set it to rho.pow(0) in the fctn to make it work both native and stdlib? - scalars_unshifted[0] = FF(builder, 1); - - // Batch the commitments to the unshifted and to-be-shifted polynomials using powers of rho - auto batched_commitment_unshifted = GroupElement::batch_mul(commitments.get_unshifted(), scalars_unshifted); - - info("Batch mul (unshifted): num gates = ", - builder->get_num_gates() - prev_num_gates, - ", (total = ", - builder->get_num_gates(), - ")"); - prev_num_gates = builder->get_num_gates(); - - auto batched_commitment_to_be_shifted = - GroupElement::batch_mul(commitments.get_to_be_shifted(), scalars_to_be_shifted); - - info("Batch mul (to-be-shited): num gates = ", - builder->get_num_gates() - prev_num_gates, - ", (total = ", - builder->get_num_gates(), - ")"); - prev_num_gates = builder->get_num_gates(); - - // Produce a Gemini claim consisting of: - // - d+1 commitments [Fold_{r}^(0)], [Fold_{-r}^(0)], and [Fold^(l)], l = 1:d-1 - // - d+1 evaluations a_0_pos, and a_l, l = 0:d-1 - auto univariate_opening_claims = Gemini::reduce_verification(multivariate_challenge, - batched_evaluation, - batched_commitment_unshifted, - batched_commitment_to_be_shifted, - transcript); - - info("Gemini: num gates = ", - builder->get_num_gates() - prev_num_gates, - ", (total = ", - builder->get_num_gates(), - ")"); - prev_num_gates = builder->get_num_gates(); - - // Produce a Shplonk claim: commitment [Q] - [Q_z], evaluation zero (at random challenge z) - auto shplonk_claim = Shplonk::reduce_verification(pcs_verification_key, univariate_opening_claims, transcript); - - info("Shplonk: num gates = ", - builder->get_num_gates() - prev_num_gates, - ", (total = ", - builder->get_num_gates(), - ")"); - prev_num_gates = builder->get_num_gates(); - - // Constuct the inputs to the final KZG pairing check - auto pairing_points = KZG::compute_pairing_points(shplonk_claim, transcript); - - info("KZG: num gates = ", builder->get_num_gates() - prev_num_gates, ", (total = ", builder->get_num_gates(), ")"); + // Execute ZeroMorph multilinear PCS evaluation verifier + auto pairing_points = ZeroMorph::verify(commitments, claimed_evaluations, multivariate_challenge, transcript); return pairing_points; }