From 8294902857068895963143f2653a0c71cb50e534 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Tue, 24 Oct 2023 17:09:36 +0000 Subject: [PATCH 1/6] Zeromorph with concatenation --- .../honk/pcs/zeromorph/zeromorph.hpp | 183 +++++++++++ .../honk/pcs/zeromorph/zeromorph.test.cpp | 294 ++++++++++++++++++ 2 files changed, 477 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp index ed3beccf79e..3839d4e8908 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp @@ -224,6 +224,79 @@ template class ZeroMorphProver_ { return result; } + /** + * @brief Compute partially evaluated zeromorph identity polynomial Z_x with concatenated polynomials + * @details Compute Z_x, where + * + * Z_x = x * f_batched + g_batched + \sum_{i=0}^{concatenation_index}( + * x^{i * min_N + 1}concatenation_groups_batched_{i}) - 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}\rho^i*f_i, g_batched = \sum_{i=0}^{l-1}\rho^{m+i}*g_i, + * concatenation_groups_batched_{j}=\sum{i=0}^{o-1}\rho{m+l+i}*polynomial_before_concatenation_{j}_{i} + * + * @param input_polynomial + * @param quotients + * @param v_evaluation + * @param x_challenge + * @return Polynomial + */ + static Polynomial compute_partially_evaluated_zeromorph_identity_polynomial_with_concatenations( + Polynomial& f_batched, + Polynomial& g_batched, + std::vector& concatenation_groups_batched, + std::vector& quotients, + FF v_evaluation, + std::span u_challenge, + FF x_challenge) + { + size_t N = f_batched.size(); + size_t log_N = quotients.size(); + size_t MINICIRCUIT_N = N / concatenation_groups_batched.size(); + // Initialize Z_x with x * \sum_{i=0}^{m-1} f_i + \sum_{i=0}^{l-1} g_i + auto result = g_batched; + result.add_scaled(f_batched, x_challenge); + + // Compute the power of x used for shifting polynomials to the right + auto x_to_minicircuit_N = x_challenge.pow(MINICIRCUIT_N); + auto running_shift = x_challenge; + // clang-format off + // Make Z_x = x * f_batched + g_batched + \sum_{i=0}^{concatenation_index}(x^{i * min_n + 1}concatenation_groups_batched_{i}) + // We are effectively reconstructing concatenated polynomials from their chunks now that we now x + // clang-format on + for (size_t i = 0; i < concatenation_groups_batched.size(); i++) { + result.add_scaled(concatenation_groups_batched[i], running_shift); + running_shift *= x_to_minicircuit_N; + } + + // Compute Z_x -= v * x * \Phi_n(x) + auto phi_numerator = x_challenge.pow(N) - 1; // x^N - 1 + auto phi_n_x = phi_numerator / (x_challenge - 1); + result[0] -= v_evaluation * x_challenge * phi_n_x; + + // Add contribution from q_k polynomials + auto x_power = x_challenge; // x^{2^k} + for (size_t k = 0; k < log_N; ++k) { + x_power = 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); + + // x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k}) + auto scalar = x_power * phi_term_1 - u_challenge[k] * phi_term_2; + + scalar *= x_challenge; + scalar *= FF(-1); + + result.add_scaled(quotients[k], scalar); + } + + return result; + } + /** * @brief Compute combined evaluation and degree-check quotient polynomial pi * @details Compute univariate quotient pi, where @@ -514,6 +587,116 @@ template class ZeroMorphVerifier_ { } } + /** + * @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}\rho^i*[f_i] + \sum_{i=0}^{l-1}\rho^{m+i}*[g_i] + + * \sum{i=0}^{o-1}\sum_{j=0}^{concatenation_index}(rho^{m+l+i} * x^{j * min_N + 1} + * *concatenation_groups_commitments_{i}_{j}) - 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 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 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_with_concatenations( + std::vector f_commitments, + std::vector g_commitments, + std::vector> concatenation_groups_commitments, + std::vector& C_q_k, + 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); + + // 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) { + scalars.emplace_back(x_challenge * rho_pow); + commitments.emplace_back(commitment); + rho_pow *= rho; + } + + // Add contribution: \sum_{i=0}^{l-1} \rho^{m+i}*[g_i] + for (auto& commitment : g_commitments) { + scalars.emplace_back(rho_pow); + commitments.emplace_back(commitment); + rho_pow *= rho; + } + + if (!concatenation_groups_commitments.empty()) { + size_t CONCATENATION_INDEX = concatenation_groups_commitments[0].size(); + size_t MINICIRCUIT_N = N / CONCATENATION_INDEX; + std::vector x_shifts; + auto current_x_shift = x_challenge; + auto x_to_minicircuit_n = x_challenge.pow(MINICIRCUIT_N); + for (size_t i = 0; i < CONCATENATION_INDEX; ++i) { + x_shifts.emplace_back(current_x_shift); + current_x_shift *= x_to_minicircuit_n; + } + for (auto& concatenation_group_commitment : concatenation_groups_commitments) { + for (size_t i = 0; i < CONCATENATION_INDEX; ++i) { + scalars.emplace_back(rho_pow * x_shifts[i]); + commitments.emplace_back(concatenation_group_commitment[i]); + } + rho_pow *= rho; + } + } + // 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) { + + 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 *= 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) { + 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 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 2dd12c429f9..34c1496ec13 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp @@ -2,7 +2,9 @@ #include "../commitment_key.test.hpp" #include "barretenberg/honk/transcript/transcript.hpp" +#include #include +#include namespace proof_system::honk::pcs::zeromorph { @@ -213,8 +215,288 @@ template class ZeroMorphTest : public CommitmentTest { } }; +template class ZeroMorphWithConcatenationTest : public CommitmentTest { + public: + using Fr = typename Curve::ScalarField; + using Polynomial = barretenberg::Polynomial; + using Commitment = typename Curve::AffineElement; + using GroupElement = typename Curve::Element; + using ZeroMorphProver = ZeroMorphProver_; + using ZeroMorphVerifier = ZeroMorphVerifier_; + + // Evaluate Phi_k(x) = \sum_{i=0}^k x^i using the direct inefficent formula + Fr Phi(Fr challenge, size_t subscript) + { + size_t length = 1 << subscript; + auto result = Fr(0); + for (size_t idx = 0; idx < length; ++idx) { + result += challenge.pow(idx); + } + return result; + } + + /** + * @brief Construct and verify ZeroMorph proof of batched multilinear evaluation with shifts and concatenation + * @details The goal is to construct and verify a single batched multilinear evaluation proof for m polynomials f_i, + * l polynomials h_i and o groups of polynomials where each polynomial is concatenated from several shorter + * polynomials. It is assumed that the h_i are shifts of polynomials g_i (the "to-be-shifted" polynomials), which + * are a subset of the f_i. This is what is encountered in practice. We accomplish this using evaluations of h_i but + * commitments to only their unshifted counterparts g_i (which we get for "free" since commitments [g_i] are + * contained in the set of commitments [f_i]). + * + */ + bool execute_zeromorph_protocol(size_t NUM_UNSHIFTED, size_t NUM_SHIFTED, size_t NUM_CONCATENATED) + { + bool verified = false; + size_t concatenation_index = 2; + size_t N = 64; + size_t MINI_CIRCUIT_N = N / concatenation_index; + size_t log_N = numeric::get_msb(N); + + auto u_challenge = this->random_evaluation_point(log_N); + + // Construct some random multilinear polynomials f_i and their evaluations v_i = f_i(u) + std::vector f_polynomials; // unshifted polynomials + std::vector v_evaluations; + for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { + f_polynomials.emplace_back(this->random_polynomial(N)); + f_polynomials[i][0] = Fr(0); // ensure f is "shiftable" + v_evaluations.emplace_back(f_polynomials[i].evaluate_mle(u_challenge)); + } + + // Construct some "shifted" multilinear polynomials h_i as the left-shift-by-1 of f_i + std::vector g_polynomials; // to-be-shifted polynomials + std::vector h_polynomials; // shifts of the to-be-shifted polynomials + std::vector w_evaluations; + for (size_t i = 0; i < NUM_SHIFTED; ++i) { + g_polynomials.emplace_back(f_polynomials[i]); + h_polynomials.emplace_back(g_polynomials[i].shifted()); + w_evaluations.emplace_back(h_polynomials[i].evaluate_mle(u_challenge)); + // ASSERT_EQ(w_evaluations[i], g_polynomials[i].evaluate_mle(u_challenge, /* shift = */ true)); + } + + // Polynomials "chunks" that are concatenated in the PCS + std::vector> concatenation_groups; + + // Concatenated polynomials + std::vector concatenated_polynomials; + + // Evaluations of concatenated polynomials + std::vector c_evaluations; + + // For each polynomial to be concatenated + for (size_t i = 0; i < NUM_CONCATENATED; ++i) { + std::vector concatenation_group; + Polynomial concatenated_polynomial(N); + // For each chunk + for (size_t j = 0; j < concatenation_index; j++) { + Polynomial chunk_polynomial(N); + // Fill the chunk polynomial with random values and appropriately fill the space in + // concatenated_polynomial + for (size_t k = 0; k < MINI_CIRCUIT_N; k++) { + // Chunks should be shiftable + auto tmp = Fr(0); + if (k > 0) { + tmp = Fr::random_element(this->engine); + } + chunk_polynomial[k] = tmp; + concatenated_polynomial[j * MINI_CIRCUIT_N + k] = tmp; + } + concatenation_group.emplace_back(chunk_polynomial); + } + // Store chunks + concatenation_groups.emplace_back(concatenation_group); + // Store concatenated polynomial + concatenated_polynomials.emplace_back(concatenated_polynomial); + // Get evaluation + c_evaluations.emplace_back(concatenated_polynomial.evaluate_mle(u_challenge)); + } + + // Compute commitments [f_i] + std::vector f_commitments; + for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { + f_commitments.emplace_back(this->commit(f_polynomials[i])); + } + + // Construct container of commitments of the "to-be-shifted" polynomials [g_i] (= [f_i]) + std::vector g_commitments; + for (size_t i = 0; i < NUM_SHIFTED; ++i) { + g_commitments.emplace_back(f_commitments[i]); + } + + // Compute commitments of all polynomial chunks + std::vector> concatenation_groups_commitments; + for (size_t i = 0; i < NUM_CONCATENATED; ++i) { + std::vector concatenation_group_commitment; + for (size_t j = 0; j < concatenation_index; j++) { + concatenation_group_commitment.emplace_back(this->commit(concatenation_groups[i][j])); + } + concatenation_groups_commitments.emplace_back(concatenation_group_commitment); + } + + // Initialize an empty ProverTranscript + auto prover_transcript = ProverTranscript::init_empty(); + + // Execute Prover protocol + { + auto rho = prover_transcript.get_challenge("ZM:rho"); + + // 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 concatenated_batched = Polynomial(N); + std::vector concatenation_groups_batched; + auto v_evaluation = Fr(0); + auto rho_pow = Fr(1); + for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { + 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], rho_pow); + v_evaluation += rho_pow * w_evaluations[i]; + rho_pow *= rho; + } + for (size_t i = 0; i < concatenation_index; ++i) { + concatenation_groups_batched.push_back(Polynomial(N)); + } + for (size_t i = 0; i < NUM_CONCATENATED; ++i) { + concatenated_batched.add_scaled(concatenated_polynomials[i], rho_pow); + for (size_t j = 0; j < concatenation_index; ++j) { + concatenation_groups_batched[j].add_scaled(concatenation_groups[i][j], rho_pow); + } + v_evaluation += rho_pow * c_evaluations[i]; + rho_pow *= rho; + } + + // The new f is f_batched + g_batched.shifted() = f_batched + h_batched + auto f_polynomial = f_batched; + f_polynomial += g_batched.shifted(); + f_polynomial += concatenated_batched; + + // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1}) + auto quotients = ZeroMorphProver::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] = this->commit(quotients[idx]); + std::string label = "ZM:C_q_" + std::to_string(idx); + prover_transcript.send_to_verifier(label, q_k_commitments[idx]); + } + + // Get challenge y + auto y_challenge = prover_transcript.get_challenge("ZM:y"); + + // Compute the batched, lifted-degree quotient \hat{q} + auto batched_quotient = ZeroMorphProver::compute_batched_lifted_degree_quotient(quotients, y_challenge, N); + + // Compute and send the commitment C_q = [\hat{q}] + auto q_commitment = this->commit(batched_quotient); + prover_transcript.send_to_verifier("ZM:C_q", q_commitment); + + // Get challenges x and z + auto [x_challenge, z_challenge] = prover_transcript.get_challenges("ZM:x", "ZM:z"); + + // Compute degree check polynomial \zeta partially evaluated at x + auto zeta_x = ZeroMorphProver::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 = ZeroMorphProver::compute_partially_evaluated_zeromorph_identity_polynomial_with_concatenations( + f_batched, g_batched, concatenation_groups_batched, quotients, v_evaluation, u_challenge, x_challenge); + + // Compute batched degree and ZM-identity quotient polynomial pi + auto pi_polynomial = ZeroMorphProver::compute_batched_evaluation_and_degree_check_quotient( + zeta_x, Z_x, x_challenge, z_challenge); + + // Compute and send proof commitment pi + auto pi_commitment = this->commit(pi_polynomial); + prover_transcript.send_to_verifier("ZM:PI", pi_commitment); + } + + auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); + + // Execute Verifier protocol + { + // Challenge rho + auto rho = verifier_transcript.get_challenge("ZM:rho"); + + // 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 rho_pow = Fr(1); + for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { + v_evaluation += rho_pow * v_evaluations[i]; + rho_pow *= rho; + } + for (size_t i = 0; i < NUM_SHIFTED; ++i) { + v_evaluation += rho_pow * w_evaluations[i]; + rho_pow *= rho; + } + for (size_t i = 0; i < NUM_CONCATENATED; ++i) { + v_evaluation += rho_pow * c_evaluations[i]; + rho_pow *= rho; + } + // 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( + verifier_transcript.template receive_from_prover("ZM:C_q_" + std::to_string(i))); + } + + // Challenge y + auto y_challenge = verifier_transcript.get_challenge("ZM:y"); + + // Receive commitment C_{q} + auto C_q = verifier_transcript.template receive_from_prover("ZM:C_q"); + + // Challenges x, z + auto [x_challenge, z_challenge] = verifier_transcript.get_challenges("ZM:x", "ZM:z"); + + // 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_with_concatenations(f_commitments, + g_commitments, + concatenation_groups_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; + + // Receive proof commitment \pi + auto C_pi = verifier_transcript.template receive_from_prover("ZM:PI"); + + // The prover and verifier manifests should agree + EXPECT_EQ(prover_transcript.get_manifest(), verifier_transcript.get_manifest()); + + // 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; + verified = this->vk()->pairing_check(P0, P1); + // EXPECT_TRUE(verified); + } + return verified; + } +}; + using CurveTypes = ::testing::Types; TYPED_TEST_SUITE(ZeroMorphTest, CurveTypes); +TYPED_TEST_SUITE(ZeroMorphWithConcatenationTest, CurveTypes); /** * @brief Test method for computing q_k given multilinear f @@ -476,4 +758,16 @@ TYPED_TEST(ZeroMorphTest, ProveAndVerifyBatchedWithShifts) EXPECT_TRUE(verified); } +/** + * @brief Test full Prover/Verifier protocol for proving single multilinear evaluation + * + */ +TYPED_TEST(ZeroMorphWithConcatenationTest, ProveAndVerify) +{ + size_t num_unshifted = 1; + size_t num_shifted = 0; + size_t num_concatenated = 3; + auto verified = this->execute_zeromorph_protocol(num_unshifted, num_shifted, num_concatenated); + EXPECT_TRUE(verified); +} } // namespace proof_system::honk::pcs::zeromorph From f24f7367391608ffc2188a1526d2391147f864a3 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 24 Oct 2023 21:18:06 +0000 Subject: [PATCH 2/6] simplify Zx computation --- .../honk/pcs/zeromorph/zeromorph.hpp | 43 ++++--------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp index 3839d4e8908..f7890183998 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp @@ -251,49 +251,22 @@ template class ZeroMorphProver_ { FF x_challenge) { size_t N = f_batched.size(); - size_t log_N = quotients.size(); size_t MINICIRCUIT_N = N / concatenation_groups_batched.size(); - // Initialize Z_x with x * \sum_{i=0}^{m-1} f_i + \sum_{i=0}^{l-1} g_i - auto result = g_batched; - result.add_scaled(f_batched, x_challenge); + auto x_to_minicircuit_N = x_challenge.pow(MINICIRCUIT_N); // power of x used to shift polynomials to the right - // Compute the power of x used for shifting polynomials to the right - auto x_to_minicircuit_N = x_challenge.pow(MINICIRCUIT_N); + // Initialize Z_x with conventional ZM identity + auto result = compute_partially_evaluated_zeromorph_identity_polynomial( + f_batched, g_batched, quotients, v_evaluation, u_challenge, x_challenge); + + // Add to Z_x the contribution: + // \sum_{i=0}^{concatenation_index}(x^{i * min_n + 1}concatenation_groups_batched_{i}). + // We are effectively reconstructing concatenated polynomials from their chunks now that we know x auto running_shift = x_challenge; - // clang-format off - // Make Z_x = x * f_batched + g_batched + \sum_{i=0}^{concatenation_index}(x^{i * min_n + 1}concatenation_groups_batched_{i}) - // We are effectively reconstructing concatenated polynomials from their chunks now that we now x - // clang-format on for (size_t i = 0; i < concatenation_groups_batched.size(); i++) { result.add_scaled(concatenation_groups_batched[i], running_shift); running_shift *= x_to_minicircuit_N; } - // Compute Z_x -= v * x * \Phi_n(x) - auto phi_numerator = x_challenge.pow(N) - 1; // x^N - 1 - auto phi_n_x = phi_numerator / (x_challenge - 1); - result[0] -= v_evaluation * x_challenge * phi_n_x; - - // Add contribution from q_k polynomials - auto x_power = x_challenge; // x^{2^k} - for (size_t k = 0; k < log_N; ++k) { - x_power = 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); - - // x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k}) - auto scalar = x_power * phi_term_1 - u_challenge[k] * phi_term_2; - - scalar *= x_challenge; - scalar *= FF(-1); - - result.add_scaled(quotients[k], scalar); - } - return result; } From 5b8ce67e4a4c6db31faabc8563763d4b295cdf8e Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 24 Oct 2023 21:45:46 +0000 Subject: [PATCH 3/6] simplify computation of CZx --- .../honk/pcs/zeromorph/zeromorph.hpp | 116 +++--------------- .../honk/pcs/zeromorph/zeromorph.test.cpp | 16 +-- 2 files changed, 25 insertions(+), 107 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp index f7890183998..a8123234c9a 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp @@ -473,19 +473,27 @@ template class ZeroMorphVerifier_ { } /** - * @brief Compute commitment to partially evaluated ZeroMorph identity Z + * @brief * @details Compute commitment C_{Z_x} = [Z_x]_1 using homomorphicity: * * 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] + * + concatentation_term + * where * - * @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 + * concatenation_term = \sum{i=0}^{o-1}\sum_{j=0}^{concatenation_index}(rho^{m+l+i} * x^{j * min_N + 1} + * * concatenation_groups_commitments_{i}_{j}) + * + * @note The concatenation term arises from an implementation detail in the Goblin Translator and is not part of the + * conventional ZM protocol + * @param f_commitments + * @param g_commitments + * @param C_q_k * @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 batched_evaluation * @param x_challenge - * @param u_challenge multilinear challenge + * @param u_challenge + * @param concatenation_groups_commitments * @return Commitment */ static Commitment compute_C_Z_x(std::vector f_commitments, @@ -494,99 +502,8 @@ template class ZeroMorphVerifier_ { 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); - - // 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) { - scalars.emplace_back(x_challenge * rho_pow); - commitments.emplace_back(commitment); - rho_pow *= rho; - } - - // Add contribution: \sum_{i=0}^{l-1} \rho^{m+i}*[g_i] - for (auto& commitment : g_commitments) { - scalars.emplace_back(rho_pow); - commitments.emplace_back(commitment); - rho_pow *= rho; - } - - // 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) { - - 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 *= 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) { - return Commitment::batch_mul(commitments, scalars); - } else { - return batch_mul_native(commitments, scalars); - } - } - - /** - * @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}\rho^i*[f_i] + \sum_{i=0}^{l-1}\rho^{m+i}*[g_i] + - * \sum{i=0}^{o-1}\sum_{j=0}^{concatenation_index}(rho^{m+l+i} * x^{j * min_N + 1} - * *concatenation_groups_commitments_{i}_{j}) - 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 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 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_with_concatenations( - std::vector f_commitments, - std::vector g_commitments, - std::vector> concatenation_groups_commitments, - std::vector& C_q_k, - FF rho, - FF batched_evaluation, - FF x_challenge, - std::vector u_challenge) + std::vector u_challenge, + std::vector> concatenation_groups_commitments = {}) { size_t log_N = C_q_k.size(); size_t N = 1 << log_N; @@ -641,6 +558,7 @@ template class ZeroMorphVerifier_ { rho_pow *= rho; } } + // 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} 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 34c1496ec13..ba1e8cc24fb 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp @@ -462,14 +462,14 @@ template class ZeroMorphWithConcatenationTest : public CommitmentT 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_with_concatenations(f_commitments, - g_commitments, - concatenation_groups_commitments, - C_q_k, - rho, - v_evaluation, - x_challenge, - u_challenge); + Commitment C_Z_x = ZeroMorphVerifier::compute_C_Z_x(f_commitments, + g_commitments, + C_q_k, + rho, + v_evaluation, + x_challenge, + u_challenge, + concatenation_groups_commitments); // Compute commitment C_{\zeta,Z} auto C_zeta_Z = C_zeta_x + C_Z_x * z_challenge; From f337c1b6ca28982789dacc40aceec8cc128d04df Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 24 Oct 2023 22:45:27 +0000 Subject: [PATCH 4/6] further simplify Zx computation --- .../honk/pcs/zeromorph/zeromorph.hpp | 72 +++++++------------ .../honk/pcs/zeromorph/zeromorph.test.cpp | 4 +- 2 files changed, 26 insertions(+), 50 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp index a8123234c9a..b2a0520b426 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp @@ -173,21 +173,28 @@ 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 + * + concatentation_term * * where f_batched = \sum_{i=0}^{m-1}\rho^i*f_i, g_batched = \sum_{i=0}^{l-1}\rho^{m+i}*g_i * + * and concatenation_term = \sum_{i=0}^{concatenation_index}(x^{i * min_N + 1}concatenation_groups_batched_{i}) + * + * @note The concatenation term arises from an implementation detail in the Goblin Translator and is not part of the + * conventional ZM protocol * @param input_polynomial * @param quotients * @param v_evaluation * @param x_challenge * @return Polynomial */ - static Polynomial compute_partially_evaluated_zeromorph_identity_polynomial(Polynomial& f_batched, - Polynomial& g_batched, - std::vector& quotients, - FF v_evaluation, - std::span u_challenge, - FF x_challenge) + static Polynomial compute_partially_evaluated_zeromorph_identity_polynomial( + Polynomial& f_batched, + Polynomial& g_batched, + std::vector& quotients, + FF v_evaluation, + std::span u_challenge, + FF x_challenge, + std::vector concatenation_groups_batched = {}) { size_t N = f_batched.size(); size_t log_N = quotients.size(); @@ -221,50 +228,19 @@ template class ZeroMorphProver_ { result.add_scaled(quotients[k], scalar); } - return result; - } - - /** - * @brief Compute partially evaluated zeromorph identity polynomial Z_x with concatenated polynomials - * @details Compute Z_x, where - * - * Z_x = x * f_batched + g_batched + \sum_{i=0}^{concatenation_index}( - * x^{i * min_N + 1}concatenation_groups_batched_{i}) - 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}\rho^i*f_i, g_batched = \sum_{i=0}^{l-1}\rho^{m+i}*g_i, - * concatenation_groups_batched_{j}=\sum{i=0}^{o-1}\rho{m+l+i}*polynomial_before_concatenation_{j}_{i} - * - * @param input_polynomial - * @param quotients - * @param v_evaluation - * @param x_challenge - * @return Polynomial - */ - static Polynomial compute_partially_evaluated_zeromorph_identity_polynomial_with_concatenations( - Polynomial& f_batched, - Polynomial& g_batched, - std::vector& concatenation_groups_batched, - std::vector& quotients, - FF v_evaluation, - std::span u_challenge, - FF x_challenge) - { - size_t N = f_batched.size(); - size_t MINICIRCUIT_N = N / concatenation_groups_batched.size(); - auto x_to_minicircuit_N = x_challenge.pow(MINICIRCUIT_N); // power of x used to shift polynomials to the right - - // Initialize Z_x with conventional ZM identity - auto result = compute_partially_evaluated_zeromorph_identity_polynomial( - f_batched, g_batched, quotients, v_evaluation, u_challenge, x_challenge); - - // Add to Z_x the contribution: + // If necessary, add to Z_x the contribution related to concatenated polynomials: // \sum_{i=0}^{concatenation_index}(x^{i * min_n + 1}concatenation_groups_batched_{i}). // We are effectively reconstructing concatenated polynomials from their chunks now that we know x - auto running_shift = x_challenge; - for (size_t i = 0; i < concatenation_groups_batched.size(); i++) { - result.add_scaled(concatenation_groups_batched[i], running_shift); - running_shift *= x_to_minicircuit_N; + // Note: this is an implementation detail related to Goblin Translator and is not part of the standard protocol. + if (!concatenation_groups_batched.empty()) { + size_t MINICIRCUIT_N = N / concatenation_groups_batched.size(); + auto x_to_minicircuit_N = + x_challenge.pow(MINICIRCUIT_N); // power of x used to shift polynomials to the right + auto running_shift = x_challenge; + for (size_t i = 0; i < concatenation_groups_batched.size(); i++) { + result.add_scaled(concatenation_groups_batched[i], running_shift); + running_shift *= x_to_minicircuit_N; + } } return result; 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 ba1e8cc24fb..49e63d0118d 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp @@ -407,8 +407,8 @@ template class ZeroMorphWithConcatenationTest : public CommitmentT batched_quotient, quotients, y_challenge, x_challenge); // Compute ZeroMorph identity polynomial Z partially evaluated at x - auto Z_x = ZeroMorphProver::compute_partially_evaluated_zeromorph_identity_polynomial_with_concatenations( - f_batched, g_batched, concatenation_groups_batched, quotients, v_evaluation, u_challenge, x_challenge); + auto Z_x = ZeroMorphProver::compute_partially_evaluated_zeromorph_identity_polynomial( + f_batched, g_batched, quotients, v_evaluation, u_challenge, x_challenge, concatenation_groups_batched); // Compute batched degree and ZM-identity quotient polynomial pi auto pi_polynomial = ZeroMorphProver::compute_batched_evaluation_and_degree_check_quotient( From 46f84f16c08959e6d2ee941be2ad3fa1bdb3af5f Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 24 Oct 2023 22:56:47 +0000 Subject: [PATCH 5/6] reinstate deleted comments --- .../barretenberg/honk/pcs/zeromorph/zeromorph.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp index b2a0520b426..aa088d726ef 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp @@ -449,7 +449,7 @@ template class ZeroMorphVerifier_ { } /** - * @brief + * @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}\rho^i*[f_i] + \sum_{i=0}^{l-1}\rho^{m+i}*[g_i] - v * x * \Phi_n(x) * [1]_1 @@ -462,13 +462,13 @@ template class ZeroMorphVerifier_ { * * @note The concatenation term arises from an implementation detail in the Goblin Translator and is not part of the * conventional ZM protocol - * @param f_commitments - * @param g_commitments - * @param C_q_k + * @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 rho - * @param batched_evaluation + * @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 + * @param u_challenge multilinear challenge * @param concatenation_groups_commitments * @return Commitment */ From 39b7c7ced79be370b8c93cb6353ab6391b94229c Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 25 Oct 2023 17:16:23 +0000 Subject: [PATCH 6/6] formatting and comments --- .../cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp | 2 ++ .../cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp index aa088d726ef..30b9a64eb6d 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.hpp @@ -516,6 +516,8 @@ template class ZeroMorphVerifier_ { rho_pow *= rho; } + // If applicable, add contribution from concatenated polynomial commitments + // Note: this is an implementation detail related to Goblin Translator and is not part of the standard protocol. if (!concatenation_groups_commitments.empty()) { size_t CONCATENATION_INDEX = concatenation_groups_commitments[0].size(); size_t MINICIRCUIT_N = N / CONCATENATION_INDEX; 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 49e63d0118d..c4c4dd95984 100644 --- a/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/pcs/zeromorph/zeromorph.test.cpp @@ -2,9 +2,7 @@ #include "../commitment_key.test.hpp" #include "barretenberg/honk/transcript/transcript.hpp" -#include #include -#include namespace proof_system::honk::pcs::zeromorph {