From 432131ce561a58ff4c96f11d7830bc352f4812d2 Mon Sep 17 00:00:00 2001 From: arijitdutta67 Date: Wed, 4 Jan 2023 15:00:48 +0000 Subject: [PATCH 1/3] initial commit to import ipa --- .../aztec/honk/commitment_scheme/ipa/ipa.hpp | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp diff --git a/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp b/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp new file mode 100644 index 0000000000..6d2fd5e7e6 --- /dev/null +++ b/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp @@ -0,0 +1,264 @@ +#pragma once +#include +#include +#include +#include +#include "../commitment_scheme.hpp" +#include "stdlib/primitives/curves/bn254.hpp" + +// Suggested by Zac: Future optimisations +// 1: write a program that generates a large set of generator points (2^23?) and writes to a file on disk +// 2: create a SRS class for IPA similar to existing SRS class, that loads these points from disk +// and stores in struct *and* applies the pippenger point table endomorphism transforation +// 3: when constructing a InnerProductArgument class, pass std::shared_ptr as input param and store as member +// variable +// 4: (SRS class should contain a `pippenger_runtime_state` object so it does not need to be repeatedly +// generated) + +namespace waffle { + +/** + * @brief IPA (inner-product argument) commitment scheme class. Conforms to the specification + * https://hackmd.io/q-A8y6aITWyWJrvsGGMWNA?view. + * + * @tparam Fr: The underlying field + * @tparam Fq: The field corresponding to the elliptic curve + * @tparam G1: The elliptic curve group + */ +template class InnerProductArgument { + public: + using element = typename G1::element; + using affine_element = typename G1::affine_element; + struct IpaProof { + std::vector L_vec; + std::vector R_vec; + Fr a_zero; + }; + // To contain the public inputs for IPA proof + // For now we are including the aux_generator in public input. It will be computed by the prover and the verifier + // separately when the challengeGenerator is defined. + struct IpaPubInput { + element commitment; + Fr challenge_point; + Fr evaluation; + size_t poly_degree; + element aux_generator; // To be removed + std::vector round_challenges; // To be removed + }; + + /** + * @brief Commit to a polynomial + * + * @param polynomial The input polynomial in the coefficient form + * @param poly_degree The degree of the polynomial + * @param G_vector The common set of generators required to compute the commitment, to be replaced by srs + * + * @return a group element + */ + static element commit(std::vector& polynomial, const size_t poly_degree, std::vector& G_vector) + { + auto pippenger_runtime_state = barretenberg::scalar_multiplication::pippenger_runtime_state(poly_degree); + auto commitment = barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points( + &polynomial[0], &G_vector[0], poly_degree, pippenger_runtime_state); + return commitment; + } + + /** + * @brief Compute an IpaProof for opening a single polynomial at a single evaluation point + * + * @param ipa_pub_input Data required to compute the opening proof. See spec for more details + * @param polynomial The witness polynomial whose opening proof needs to be computed + * @param G_vector the common set of generators, to be replaced by the srs + * + * @return an IpaProof, containing information required to verify whether the commitment is computed correctly and + * the polynomial evaluation is correct in the given challenge point. + */ + static IpaProof ipa_prove(const IpaPubInput& ipa_pub_input, + const std::vector& polynomial, + const std::vector& G_vector) + { + IpaProof proof; + auto& challenge_point = ipa_pub_input.challenge_point; + ASSERT(challenge_point != 0); + const size_t poly_degree = ipa_pub_input.poly_degree; + ASSERT(poly_degree != 0); + auto& aux_generator = ipa_pub_input.aux_generator; + auto a_vec = polynomial; + std::vector G_vec(poly_degree); + for (size_t i = 0; i < poly_degree; i++) { + G_vec[i] = G_vector[i]; + } + // Construct b vector + std::vector b_vec(poly_degree); + Fr b_power = 1; + for (size_t i = 0; i < poly_degree; i++) { + b_vec[i] = b_power; + b_power *= challenge_point; + } + // Iterate for log_2(poly_degree) rounds to compute the round commitments. + const size_t log_poly_degree = static_cast(numeric::get_msb(poly_degree)); + std::vector L_elements(log_poly_degree); + std::vector R_elements(log_poly_degree); + size_t round_size = poly_degree; + + for (size_t i = 0; i < log_poly_degree; i++) { + round_size >>= 1; + // Compute inner_prod_L := < a_vec_lo, b_vec_hi > and inner_prod_R := < a_vec_hi, b_vec_lo > + Fr inner_prod_L = Fr::zero(); + Fr inner_prod_R = Fr::zero(); + for (size_t j = 0; j < round_size; j++) { + inner_prod_L += a_vec[j] * b_vec[round_size + j]; + inner_prod_R += a_vec[round_size + j] * b_vec[j]; + } + // L_i = < a_vec_lo, G_vec_hi > + inner_prod_L * aux_generator + auto pippenger_runtime_state = barretenberg::scalar_multiplication::pippenger_runtime_state(round_size); + element partial_L = barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points( + &a_vec[0], &G_vec[round_size], round_size, pippenger_runtime_state); + partial_L += aux_generator * inner_prod_L; + + // R_i = < a_vec_hi, G_vec_lo > + inner_prod_R * aux_generator + element partial_R = barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points( + &a_vec[round_size], &G_vec[0], round_size, pippenger_runtime_state); + partial_R += aux_generator * inner_prod_R; + + std::vector round_commitments(2); + round_commitments[0] = partial_L; + round_commitments[1] = partial_R; + element::batch_normalize(&round_commitments[0], 2); + + L_elements[i] = affine_element({ round_commitments[0].x, round_commitments[0].y }); + R_elements[i] = affine_element({ round_commitments[1].x, round_commitments[1].y }); + + // Generate the round challenge. Todo: Use Fiat-Shamir + const Fr round_challenge = ipa_pub_input.round_challenges[i]; + const Fr round_challenge_inv = round_challenge.invert(); + + // Update the vectors a_vec, b_vec and G_vec. + // a_vec_next = a_vec_lo * round_challenge + a_vec_hi * round_challenge_inv + // b_vec_next = b_vec_lo * round_challenge_inv + b_vec_hi * round_challenge + // G_vec_next = G_vec_lo * round_challenge_inv + G_vec_hi * round_challenge + for (size_t j = 0; j < round_size; j++) { + a_vec[j] *= round_challenge; + a_vec[j] += round_challenge_inv * a_vec[round_size + j]; + b_vec[j] *= round_challenge_inv; + b_vec[j] += round_challenge * b_vec[round_size + j]; + + // element has (x,y,z) coord, affine_element has (x,y) coord, G_vec is a vector of affine_element, + + auto G_lo = element(G_vec[j]) * round_challenge_inv; + auto G_hi = element(G_vec[round_size + j]) * round_challenge; + auto temp = G_lo + G_hi; + G_vec[j] = temp.normalize(); + } + } + proof.L_vec = std::vector(log_poly_degree); + proof.R_vec = std::vector(log_poly_degree); + for (size_t i = 0; i < log_poly_degree; i++) { + proof.L_vec[i] = L_elements[i]; + proof.R_vec[i] = R_elements[i]; + } + proof.a_zero = a_vec[0]; + return proof; + } + + /** + * @brief Verify the correctness of an IpaProof + * + * @param ipa_proof The proof containg L_vec, R_vec and a_zero + * @param Ipa_pub_input Data required to verify the ipa_proof + * @param G_vector The common set of generators, to be replaced by the srs + * + * @return true/false depending on if the proof verifies + */ + static bool ipa_verify(const IpaProof& ipa_proof, + const IpaPubInput& ipa_pub_input, + const std::vector& G_vector) + { + // Local copies of public inputs + auto& a_zero = ipa_proof.a_zero; + auto& commitment = ipa_pub_input.commitment; + auto& challenge_point = ipa_pub_input.challenge_point; + auto& evaluation = ipa_pub_input.evaluation; + auto& poly_degree = ipa_pub_input.poly_degree; + auto& aux_generator = ipa_pub_input.aux_generator; + + // Compute C_prime + element C_prime = commitment + (aux_generator * evaluation); + + // Compute the round challeneges and their inverses. + const size_t log_poly_degree = static_cast(numeric::get_msb(poly_degree)); + std::vector round_challenges(log_poly_degree); + for (size_t i = 0; i < log_poly_degree; i++) { + round_challenges[i] = ipa_pub_input.round_challenges[i]; + } + std::vector round_challenges_inv(log_poly_degree); + for (size_t i = 0; i < log_poly_degree; i++) { + round_challenges_inv[i] = ipa_pub_input.round_challenges[i]; + } + Fr::batch_invert(&round_challenges_inv[0], log_poly_degree); + std::vector L_vec(log_poly_degree); + std::vector R_vec(log_poly_degree); + for (size_t i = 0; i < log_poly_degree; i++) { + L_vec[i] = ipa_proof.L_vec[i]; + R_vec[i] = ipa_proof.R_vec[i]; + } + + // Compute C_zero = C_prime + ∑_{j ∈ [k]} u_j^2L_j + ∑_{j ∈ [k]} u_j^{-2}R_j + const size_t pippenger_size = 2 * log_poly_degree; + std::vector msm_elements(pippenger_size); + std::vector msm_scalars(pippenger_size); + for (size_t i = 0; i < log_poly_degree; i++) { + msm_elements[size_t(2) * i] = L_vec[i]; + msm_elements[size_t(2) * i + 1] = R_vec[i]; + msm_scalars[size_t(2) * i] = round_challenges[i] * round_challenges[i]; + msm_scalars[size_t(2) * i + 1] = round_challenges_inv[i] * round_challenges_inv[i]; + } + auto pippenger_runtime_state_1 = barretenberg::scalar_multiplication::pippenger_runtime_state(pippenger_size); + element LR_sums = barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points( + &msm_scalars[0], &msm_elements[0], pippenger_size, pippenger_runtime_state_1); + element C_zero = C_prime + LR_sums; + + /** + * Compute b_zero where b_zero can be computed using the polynomial: + * + * g(X) = ∏_{i ∈ [k]} (u_{k-i}^{-1} + u_{k-i}.X^{2^{i-1}}). + * + * b_zero = g(evaluation) = ∏_{i ∈ [k]} (u_{k-i}^{-1} + u_{k-i}. (evaluation)^{2^{i-1}}) + */ + Fr b_zero = Fr::one(); + for (size_t i = 0; i < log_poly_degree; i++) { + auto exponent = static_cast(Fr(2).pow(i)); + b_zero *= round_challenges_inv[log_poly_degree - 1 - i] + + (round_challenges[log_poly_degree - 1 - i] * challenge_point.pow(exponent)); + } + // Compute G_zero + // First construct s_vec + std::vector s_vec(poly_degree); + for (size_t i = 0; i < poly_degree; i++) { + Fr s_vec_scalar = Fr::one(); + for (size_t j = (log_poly_degree - 1); j != size_t(-1); j--) { + auto bit = (i >> j) & 1; + bool b = static_cast(bit); + if (b) { + s_vec_scalar *= round_challenges[log_poly_degree - 1 - j]; + } else { + s_vec_scalar *= round_challenges_inv[log_poly_degree - 1 - j]; + } + } + s_vec[i] = s_vec_scalar; + } + // Copy the G_vector to local memory. + std::vector G_vec2(poly_degree); + for (size_t i = 0; i < poly_degree; i++) { + G_vec2[i] = G_vector[i]; + } + auto pippenger_runtime_state_2 = barretenberg::scalar_multiplication::pippenger_runtime_state(poly_degree); + auto G_zero = barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points( + &s_vec[0], &G_vec2[0], poly_degree, pippenger_runtime_state_2); + element right_hand_side = G_zero * a_zero; + Fr a_zero_b_zero = a_zero * b_zero; + right_hand_side += aux_generator * a_zero_b_zero; + return (C_zero.normalize() == right_hand_side.normalize()); + } +}; +} // namespace waffle \ No newline at end of file From 52812e5a28169e5686bfcd12c162f3eee642c8a8 Mon Sep 17 00:00:00 2001 From: arijitdutta67 Date: Thu, 5 Jan 2023 07:32:21 +0000 Subject: [PATCH 2/3] Tests are added --- .../scalar_multiplication.cpp | 9 +++ .../scalar_multiplication.hpp | 4 ++ .../aztec/honk/commitment_scheme/ipa/ipa.hpp | 32 ++++----- .../honk/commitment_scheme/ipa/ipa.test.cpp | 67 +++++++++++++++++++ 4 files changed, 97 insertions(+), 15 deletions(-) create mode 100644 cpp/src/aztec/honk/commitment_scheme/ipa/ipa.test.cpp diff --git a/cpp/src/aztec/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.cpp b/cpp/src/aztec/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.cpp index cc2b74ec19..6e9fb9dde6 100644 --- a/cpp/src/aztec/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.cpp +++ b/cpp/src/aztec/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.cpp @@ -933,5 +933,14 @@ g1::element pippenger_unsafe(fr* scalars, { return pippenger(scalars, points, num_initial_points, state, false); } +g1::element pippenger_without_endomorphism_basis_points(fr* scalars, + g1::affine_element* points, + const size_t num_initial_points, + pippenger_runtime_state& state) +{ + std::vector G_mod(num_initial_points * 2); + barretenberg::scalar_multiplication::generate_pippenger_point_table(points, &G_mod[0], num_initial_points); + return pippenger(scalars, &G_mod[0], num_initial_points, state, false); +} } // namespace scalar_multiplication } // namespace barretenberg \ No newline at end of file diff --git a/cpp/src/aztec/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.hpp b/cpp/src/aztec/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.hpp index c5ea7f472b..36613a47a9 100644 --- a/cpp/src/aztec/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.hpp +++ b/cpp/src/aztec/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.hpp @@ -146,6 +146,10 @@ g1::element pippenger_unsafe(fr* scalars, g1::affine_element* points, const size_t num_initial_points, pippenger_runtime_state& state); +g1::element pippenger_without_endomorphism_basis_points(fr* scalars, + g1::affine_element* points, + const size_t num_initial_points, + pippenger_runtime_state& state); } // namespace scalar_multiplication } // namespace barretenberg diff --git a/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp b/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp index 6d2fd5e7e6..970ca40efe 100644 --- a/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp +++ b/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp @@ -35,8 +35,8 @@ template class InnerProductArgument { Fr a_zero; }; // To contain the public inputs for IPA proof - // For now we are including the aux_generator in public input. It will be computed by the prover and the verifier - // separately when the challengeGenerator is defined. + // For now we are including the aux_generator and round_challenges in public input. They will be computed by the + // prover and the verifier by Fiat-Shamir when the challengeGenerator is defined. struct IpaPubInput { element commitment; Fr challenge_point; @@ -79,14 +79,16 @@ template class InnerProductArgument { { IpaProof proof; auto& challenge_point = ipa_pub_input.challenge_point; - ASSERT(challenge_point != 0); + ASSERT(challenge_point != 0 && "The challenge point should not be zero"); const size_t poly_degree = ipa_pub_input.poly_degree; - ASSERT(poly_degree != 0); + // To check poly_degree is greater than zero and a power of two + ASSERT((poly_degree > 0) && (!(poly_degree & (poly_degree - 1))) && + "The poly_degree should be positive and a power of two"); auto& aux_generator = ipa_pub_input.aux_generator; auto a_vec = polynomial; - std::vector G_vec(poly_degree); + std::vector G_vec_local(poly_degree); for (size_t i = 0; i < poly_degree; i++) { - G_vec[i] = G_vector[i]; + G_vec_local[i] = G_vector[i]; } // Construct b vector std::vector b_vec(poly_degree); @@ -113,12 +115,12 @@ template class InnerProductArgument { // L_i = < a_vec_lo, G_vec_hi > + inner_prod_L * aux_generator auto pippenger_runtime_state = barretenberg::scalar_multiplication::pippenger_runtime_state(round_size); element partial_L = barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points( - &a_vec[0], &G_vec[round_size], round_size, pippenger_runtime_state); + &a_vec[0], &G_vec_local[round_size], round_size, pippenger_runtime_state); partial_L += aux_generator * inner_prod_L; // R_i = < a_vec_hi, G_vec_lo > + inner_prod_R * aux_generator element partial_R = barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points( - &a_vec[round_size], &G_vec[0], round_size, pippenger_runtime_state); + &a_vec[round_size], &G_vec_local[0], round_size, pippenger_runtime_state); partial_R += aux_generator * inner_prod_R; std::vector round_commitments(2); @@ -143,12 +145,12 @@ template class InnerProductArgument { b_vec[j] *= round_challenge_inv; b_vec[j] += round_challenge * b_vec[round_size + j]; - // element has (x,y,z) coord, affine_element has (x,y) coord, G_vec is a vector of affine_element, + // element has (x,y,z) coord, affine_element has (x,y) coord, G_vec_local is a vector of affine_element, - auto G_lo = element(G_vec[j]) * round_challenge_inv; - auto G_hi = element(G_vec[round_size + j]) * round_challenge; + auto G_lo = element(G_vec_local[j]) * round_challenge_inv; + auto G_hi = element(G_vec_local[round_size + j]) * round_challenge; auto temp = G_lo + G_hi; - G_vec[j] = temp.normalize(); + G_vec_local[j] = temp.normalize(); } } proof.L_vec = std::vector(log_poly_degree); @@ -248,13 +250,13 @@ template class InnerProductArgument { s_vec[i] = s_vec_scalar; } // Copy the G_vector to local memory. - std::vector G_vec2(poly_degree); + std::vector G_vec_local(poly_degree); for (size_t i = 0; i < poly_degree; i++) { - G_vec2[i] = G_vector[i]; + G_vec_local[i] = G_vector[i]; } auto pippenger_runtime_state_2 = barretenberg::scalar_multiplication::pippenger_runtime_state(poly_degree); auto G_zero = barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points( - &s_vec[0], &G_vec2[0], poly_degree, pippenger_runtime_state_2); + &s_vec[0], &G_vec_local[0], poly_degree, pippenger_runtime_state_2); element right_hand_side = G_zero * a_zero; Fr a_zero_b_zero = a_zero * b_zero; right_hand_side += aux_generator * a_zero_b_zero; diff --git a/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.test.cpp b/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.test.cpp new file mode 100644 index 0000000000..0cb23c1155 --- /dev/null +++ b/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.test.cpp @@ -0,0 +1,67 @@ +#include "ipa.hpp" +#include +#include +#include "./polynomials/polynomial_arithmetic.hpp" +#include "./polynomials/polynomial.hpp" +#include +#include +using namespace barretenberg; +using namespace waffle; + +TEST(honk_commitment_scheme, ipa_commit) +{ + constexpr size_t n = 1024; + std::vector scalars(n); + std::vector points(n); + + for (size_t i = 0; i < n; i++) { + scalars[i] = barretenberg::fr::random_element(); + points[i] = barretenberg::g1::affine_element(barretenberg::g1::element::random_element()); + } + + barretenberg::g1::element expected = points[0] * scalars[0]; + for (size_t i = 1; i < n; i++) { + expected += points[i] * scalars[i]; + } + + waffle::InnerProductArgument newIpa; + barretenberg::g1::element result = newIpa.commit(scalars, n, points); + EXPECT_EQ(expected.normalize(), result.normalize()); +} + +TEST(honk_commitment_scheme, ipa_open) +{ + // generate a random polynomial coeff, degree needs to be a power of two + size_t n = 1024; + std::vector coeffs(n); + for (size_t i = 0; i < n; ++i) { + coeffs[i] = barretenberg::fr::random_element(); + } + // generate random evaluation point x and the evaluation + auto x = barretenberg::fr::random_element(); + auto eval = polynomial_arithmetic::evaluate(&coeffs[0], x, n); + // generate G_vec for testing, bypassing srs + std::vector G_vec(n); + for (size_t i = 0; i < n; ++i) { + auto scalar = fr::random_element(); + G_vec[i] = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); + } + waffle::InnerProductArgument newIpa; + auto C = newIpa.commit(coeffs, n, G_vec); + waffle::InnerProductArgument::IpaPubInput pub_input; + pub_input.commitment = C; + pub_input.challenge_point = x; + pub_input.evaluation = eval; + pub_input.poly_degree = n; + auto aux_scalar = fr::random_element(); + pub_input.aux_generator = barretenberg::g1::one * aux_scalar; + const size_t log_n = static_cast(numeric::get_msb(n)); + pub_input.round_challenges = std::vector(log_n); + for (size_t i = 0; i < log_n; i++) { + pub_input.round_challenges[i] = barretenberg::fr::random_element(); + } + auto proof = newIpa.ipa_prove(pub_input, coeffs, G_vec); + auto result = waffle::InnerProductArgument::ipa_verify( + proof, pub_input, G_vec); + EXPECT_TRUE(result); +} From fdd84466260b9d64b1dc60e18dc6f192d07a1545 Mon Sep 17 00:00:00 2001 From: arijitdutta67 Date: Thu, 12 Jan 2023 11:20:04 +0000 Subject: [PATCH 3/3] Fixes suggested by Zac are added --- .../aztec/honk/commitment_scheme/ipa/ipa.hpp | 33 ++++++++++++------- .../honk/commitment_scheme/ipa/ipa.test.cpp | 11 +++---- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp b/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp index 970ca40efe..991d264328 100644 --- a/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp +++ b/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.hpp @@ -15,8 +15,6 @@ // 4: (SRS class should contain a `pippenger_runtime_state` object so it does not need to be repeatedly // generated) -namespace waffle { - /** * @brief IPA (inner-product argument) commitment scheme class. Conforms to the specification * https://hackmd.io/q-A8y6aITWyWJrvsGGMWNA?view. @@ -82,15 +80,22 @@ template class InnerProductArgument { ASSERT(challenge_point != 0 && "The challenge point should not be zero"); const size_t poly_degree = ipa_pub_input.poly_degree; // To check poly_degree is greater than zero and a power of two + // Todo: To accomodate non power of two poly_degree ASSERT((poly_degree > 0) && (!(poly_degree & (poly_degree - 1))) && "The poly_degree should be positive and a power of two"); auto& aux_generator = ipa_pub_input.aux_generator; auto a_vec = polynomial; + // Todo: to make it more efficient by directly using G_vector for the input points when i = 0 and write the + // output points to G_vec_local. Then use G_vec_local for rounds where i>0, this can be done after we use SRS + // instead of G_vector. std::vector G_vec_local(poly_degree); for (size_t i = 0; i < poly_degree; i++) { G_vec_local[i] = G_vector[i]; } // Construct b vector + // Todo: For round i=0, b_vec can be derived in-place. + // This means that the size of b_vec can be 50% of the current size (i.e. we only write values to b_vec at the + // end of round 0) std::vector b_vec(poly_degree); Fr b_power = 1; for (size_t i = 0; i < poly_degree; i++) { @@ -113,6 +118,7 @@ template class InnerProductArgument { inner_prod_R += a_vec[round_size + j] * b_vec[j]; } // L_i = < a_vec_lo, G_vec_hi > + inner_prod_L * aux_generator + // Todo: Remove usage of multiple runtime_state, pass it as an element of the SRS. auto pippenger_runtime_state = barretenberg::scalar_multiplication::pippenger_runtime_state(round_size); element partial_L = barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points( &a_vec[0], &G_vec_local[round_size], round_size, pippenger_runtime_state); @@ -123,13 +129,8 @@ template class InnerProductArgument { &a_vec[round_size], &G_vec_local[0], round_size, pippenger_runtime_state); partial_R += aux_generator * inner_prod_R; - std::vector round_commitments(2); - round_commitments[0] = partial_L; - round_commitments[1] = partial_R; - element::batch_normalize(&round_commitments[0], 2); - - L_elements[i] = affine_element({ round_commitments[0].x, round_commitments[0].y }); - R_elements[i] = affine_element({ round_commitments[1].x, round_commitments[1].y }); + L_elements[i] = affine_element(partial_L); + R_elements[i] = affine_element(partial_R); // Generate the round challenge. Todo: Use Fiat-Shamir const Fr round_challenge = ipa_pub_input.round_challenges[i]; @@ -145,7 +146,16 @@ template class InnerProductArgument { b_vec[j] *= round_challenge_inv; b_vec[j] += round_challenge * b_vec[round_size + j]; - // element has (x,y,z) coord, affine_element has (x,y) coord, G_vec_local is a vector of affine_element, + /* + Todo: (performance improvement suggested by Zac): We can improve performance here by using + element::batch_mul_with_endomorphism. This method takes a vector of input points points and a scalar x + and outputs a vector containing points[i]*x. It's 30% faster than a basic mul operation due to + performing group additions in 2D affine coordinates instead of 3D projective coordinates (affine point + additions are usually more expensive than projective additions due to the need to compute a modular + inverse. However we get around this by computing a single batch inverse. This only works if you are + adding a lot of independent point pairs so you can amortise the cost of the single batch inversion + across multiple points). + */ auto G_lo = element(G_vec_local[j]) * round_challenge_inv; auto G_hi = element(G_vec_local[round_size + j]) * round_challenge; @@ -262,5 +272,4 @@ template class InnerProductArgument { right_hand_side += aux_generator * a_zero_b_zero; return (C_zero.normalize() == right_hand_side.normalize()); } -}; -} // namespace waffle \ No newline at end of file +}; \ No newline at end of file diff --git a/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.test.cpp b/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.test.cpp index 0cb23c1155..0c7eb51aa7 100644 --- a/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.test.cpp +++ b/cpp/src/aztec/honk/commitment_scheme/ipa/ipa.test.cpp @@ -6,7 +6,6 @@ #include #include using namespace barretenberg; -using namespace waffle; TEST(honk_commitment_scheme, ipa_commit) { @@ -24,7 +23,7 @@ TEST(honk_commitment_scheme, ipa_commit) expected += points[i] * scalars[i]; } - waffle::InnerProductArgument newIpa; + InnerProductArgument newIpa; barretenberg::g1::element result = newIpa.commit(scalars, n, points); EXPECT_EQ(expected.normalize(), result.normalize()); } @@ -46,9 +45,9 @@ TEST(honk_commitment_scheme, ipa_open) auto scalar = fr::random_element(); G_vec[i] = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); } - waffle::InnerProductArgument newIpa; + InnerProductArgument newIpa; auto C = newIpa.commit(coeffs, n, G_vec); - waffle::InnerProductArgument::IpaPubInput pub_input; + InnerProductArgument::IpaPubInput pub_input; pub_input.commitment = C; pub_input.challenge_point = x; pub_input.evaluation = eval; @@ -61,7 +60,7 @@ TEST(honk_commitment_scheme, ipa_open) pub_input.round_challenges[i] = barretenberg::fr::random_element(); } auto proof = newIpa.ipa_prove(pub_input, coeffs, G_vec); - auto result = waffle::InnerProductArgument::ipa_verify( - proof, pub_input, G_vec); + auto result = + InnerProductArgument::ipa_verify(proof, pub_input, G_vec); EXPECT_TRUE(result); }