diff --git a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp index 4fb95d0b1ed..e1651117a08 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp @@ -1,23 +1,19 @@ #pragma once #include "barretenberg/polynomials/polynomial.hpp" -/** - * @brief - * - */ namespace proof_system::honk::pcs::zeromorph { /** * @brief Compute powers of a given challenge * - * @tparam Fr + * @tparam FF * @param challenge * @param num_powers - * @return std::vector + * @return std::vector */ -template inline std::vector powers_of_challenge(const Fr challenge, const size_t num_powers) +template inline std::vector powers_of_challenge(const FF challenge, const size_t num_powers) { - std::vector challenge_powers = { Fr(1), challenge }; + 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); @@ -31,12 +27,12 @@ template inline std::vector powers_of_challenge(const Fr challeng * @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). + // 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: @@ -51,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. @@ -59,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); @@ -91,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; @@ -106,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 @@ -118,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 @@ -148,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(); @@ -157,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); @@ -189,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 @@ -221,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); } @@ -244,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(); @@ -272,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}\alpha^i*f_i and g_batched = sum_{i=0}^{l-1}\alpha^{m+i}*g_i, + // and also batched evaluation + // v = sum_{i=0}^{m-1}\alpha^i*f_i(u) + sum_{i=0}^{l-1}\alpha^{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); + } }; /** @@ -280,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; using GroupElement = typename Curve::Element; @@ -297,33 +389,37 @@ 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; - std::vector scalars; + // 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(Fr(builder, 1)); + scalars.emplace_back(FF(builder, 1)); } else { - scalars.emplace_back(Fr(1)); + 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); 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 GroupElement::batch_mul(commitments, scalars); } else { @@ -350,62 +446,66 @@ template class ZeroMorphVerifier_ { static Commitment compute_C_Z_x(std::vector& f_commitments, std::vector& g_commitments, std::vector& C_q_k, - Fr alpha, - Fr batched_evaluation, - Fr x_challenge, - std::vector u_challenge) + FF alpha, + 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 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); + // Add contribution: -v * x * \Phi_n(x) * [1]_1 if constexpr (Curve::is_stdlib_type) { auto builder = x_challenge.get_context(); - scalars.emplace_back(Fr(builder, -1) * batched_evaluation * x_challenge * phi_n_x); + scalars.emplace_back(FF(builder, -1) * batched_evaluation * x_challenge * phi_n_x); commitments.emplace_back(Commitment::one(builder)); } else { - scalars.emplace_back(Fr(-1) * batched_evaluation * x_challenge * phi_n_x); + scalars.emplace_back(FF(-1) * batched_evaluation * x_challenge * phi_n_x); commitments.emplace_back(Commitment::one()); } - auto alpha_pow = Fr(1); - // Add contribution x * \sum_{i=0}^{m-1} [f_i] + // Add contribution: x * \sum_{i=0}^{m-1} \alpha^i*[f_i] + auto alpha_pow = FF(1); for (auto& commitment : f_commitments) { scalars.emplace_back(x_challenge * alpha_pow); commitments.emplace_back(commitment); alpha_pow *= alpha; } - // Add contribution \sum_{i=0}^{l-1} [g_i] + + // Add contribution: \sum_{i=0}^{l-1} \alpha^{m+i}*[g_i] for (auto& commitment : g_commitments) { scalars.emplace_back(alpha_pow); commitments.emplace_back(commitment); alpha_pow *= alpha; } - // 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]); + + // Update powers of challenge x + x_pow_2k = x_pow_2kp1; + x_pow_2kp1 *= x_pow_2kp1; } if constexpr (Curve::is_stdlib_type) { @@ -415,15 +515,96 @@ template class ZeroMorphVerifier_ { } } - static Commitment batch_mul_native(std::vector points, std::vector scalars) + /** + * @brief Utility for native batch multiplication of group elements + * + */ + 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}\alpha^i*f_i(u) + sum_{i=0}^{l-1}\alpha^{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); + + std::vector f_commitments; + std::vector g_commitments; + for (auto& commitment : commitments.get_unshifted()) { + f_commitments.emplace_back(commitment); + } + for (auto& commitment : commitments.get_to_be_shifted()) { + g_commitments.emplace_back(commitment); + } + + // Compute commitment C_{Z_x} + Commitment C_Z_x = compute_C_Z_x( + f_commitments, g_commitments, 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/proof_system/ultra_prover.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp index e090401c42c..e5cb11ee069 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp @@ -114,82 +114,12 @@ template void UltraProver_::execute_relation_check_ * */ template void UltraProver_::execute_zeromorph_rounds() { - const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; - const size_t circuit_size = instance->proving_key->circuit_size; - - // Generate batching challenge ρ and powers 1,ρ,…,ρᵐ⁻¹ - FF rho = transcript.get_challenge("rho"); - std::vector rhos = pcs::zeromorph::powers_of_challenge(rho, NUM_POLYNOMIALS); - - // Extract challenge u and claimed multilinear evaluations from Sumcheck output - std::span u_challenge = sumcheck_output.challenge; - std::span claimed_evaluations = sumcheck_output.claimed_evaluations; - size_t log_circuit_size = u_challenge.size(); - - // 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*f_i(u) + - // sum_{i=0}^{l-1}\alpha^{m+i}*h_i(u). - auto batched_evaluation = FF(0); - Polynomial f_batched(circuit_size); // batched unshifted polynomials - size_t poly_idx = 0; // TODO(#391) zip - for (auto& f_polynomial : instance->prover_polynomials.get_unshifted()) { - f_batched.add_scaled(f_polynomial, rhos[poly_idx]); - batched_evaluation += rhos[poly_idx] * claimed_evaluations[poly_idx]; - ++poly_idx; - } - - Polynomial g_batched(circuit_size); // batched to-be-shifted polynomials - for (auto& g_polynomial : instance->prover_polynomials.get_to_be_shifted()) { - g_batched.add_scaled(g_polynomial, 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 - 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 = ZeroMorph::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_circuit_size); - for (size_t idx = 0; idx < log_circuit_size; ++idx) { - q_k_commitments[idx] = pcs_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 = ZeroMorph::compute_batched_lifted_degree_quotient(quotients, y_challenge, circuit_size); - - // Compute and send the commitment C_q = [\hat{q}] - auto q_commitment = pcs_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 = ZeroMorph::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 = ZeroMorph::compute_partially_evaluated_zeromorph_identity_polynomial( - f_batched, g_batched, quotients, batched_evaluation, u_challenge, x_challenge); - - // Compute batched degree and ZM-identity quotient polynomial pi - auto pi_polynomial = - ZeroMorph::compute_batched_evaluation_and_degree_check_quotient(zeta_x, Z_x, x_challenge, z_challenge); - - // Compute and send proof commitment pi - auto pi_commitment = pcs_commitment_key->commit(pi_polynomial); - transcript.send_to_verifier("ZM:PI", pi_commitment); + 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() 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 e6b28ef7197..0a29e7b08d9 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp @@ -51,7 +51,6 @@ template bool UltraVerifier_::verify_proof(const plonk const auto circuit_size = transcript.template receive_from_prover("circuit_size"); const auto public_input_size = transcript.template receive_from_prover("public_input_size"); const auto pub_inputs_offset = transcript.template receive_from_prover("pub_inputs_offset"); - const size_t log_circuit_size = numeric::get_msb(circuit_size); if (circuit_size != key->circuit_size) { return false; @@ -118,67 +117,11 @@ template bool UltraVerifier_::verify_proof(const plonk return false; } - // Execute ZeroMorph rounds + // 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); - FF rho = transcript.get_challenge("rho"); - - // Compute powers of batching challenge rho - std::vector rhos = pcs::zeromorph::powers_of_challenge(rho, Flavor::NUM_ALL_ENTITIES); - - // Construct batched evaluation v = sum_{i=0}^{m-1}\alpha^i*v_i + sum_{i=0}^{l-1}\alpha^{m+i}*w_i - 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_circuit_size); - for (size_t i = 0; i < log_circuit_size; ++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 = ZeroMorph::compute_C_zeta_x(C_q, C_q_k, y_challenge, x_challenge); - - std::vector f_commitments; - std::vector g_commitments; - for (auto& commitment : commitments.get_unshifted()) { - f_commitments.emplace_back(commitment); - } - for (auto& commitment : commitments.get_to_be_shifted()) { - g_commitments.emplace_back(commitment); - } - - // Compute commitment C_{Z_x} - Commitment C_Z_x = ZeroMorph::compute_C_Z_x( - f_commitments, g_commitments, 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; - auto verified = pcs_verification_key->pairing_check(P0, P1); + 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/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp index a7bc5573ab7..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 @@ -41,7 +41,6 @@ std::array UltraRecursiveVerifier_::ve const auto circuit_size = transcript.template receive_from_prover("circuit_size"); const auto public_input_size = transcript.template receive_from_prover("public_input_size"); const auto pub_inputs_offset = transcript.template receive_from_prover("pub_inputs_offset"); - const auto log_circuit_size = numeric::get_msb32(static_cast(circuit_size.get_value())); // For debugging purposes only ASSERT(static_cast(circuit_size.get_value()) == key->circuit_size); @@ -107,65 +106,10 @@ 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::zeromorph::powers_of_challenge(rho, Flavor::NUM_ALL_ENTITIES); + // Execute ZeroMorph multilinear PCS evaluation verifier + auto pairing_points = ZeroMorph::verify(commitments, claimed_evaluations, multivariate_challenge, transcript); - // Construct batched evaluation v = sum_{i=0}^{m-1}\alpha^i*v_i + sum_{i=0}^{l-1}\alpha^{m+i}*w_i - 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_circuit_size); - for (size_t i = 0; i < log_circuit_size; ++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 = ZeroMorph::compute_C_zeta_x(C_q, C_q_k, y_challenge, x_challenge); - - std::vector f_commitments; - std::vector g_commitments; - for (auto& commitment : commitments.get_unshifted()) { - f_commitments.emplace_back(commitment); - } - for (auto& commitment : commitments.get_to_be_shifted()) { - g_commitments.emplace_back(commitment); - } - - // Compute commitment C_{Z_x} - Commitment C_Z_x = ZeroMorph::compute_C_Z_x( - f_commitments, g_commitments, 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 }; + return pairing_points; } template class UltraRecursiveVerifier_>;