diff --git a/cpp/src/barretenberg/honk/pcs/commitment_key.hpp b/cpp/src/barretenberg/honk/pcs/commitment_key.hpp index 9778851c73..81aec686f4 100644 --- a/cpp/src/barretenberg/honk/pcs/commitment_key.hpp +++ b/cpp/src/barretenberg/honk/pcs/commitment_key.hpp @@ -193,4 +193,95 @@ template struct Params { using VK = VerificationKey; }; } // namespace fake + +namespace ipa { + +/** + * @brief CommitmentKey object over a group 𝔾₁, using a structured reference string (SRS). + * The SRS is given as a list of 𝔾₁ points + * { [xʲ]₁ }ⱼ where 'x' is unknown. + * + * @todo This class should take ownership of the SRS, and handle reading the file from disk. + */ +class CommitmentKey { + using Fr = typename barretenberg::g1::Fr; + // C is a "raw commitment" resulting to be fed to the transcript. + using C = typename barretenberg::g1::affine_element; + // Commitment represent's a homomorphically computed group element. + using Commitment = barretenberg::g1::element; + + using Polynomial = barretenberg::Polynomial; + + public: + CommitmentKey() = delete; + + /** + * @brief Construct a new Kate Commitment Key object from existing SRS + * + * @param n + * @param path + * + * @todo change path to string_view + */ + CommitmentKey(const size_t num_points, std::string_view path) + : pippenger_runtime_state(num_points) + , srs(num_points, std::string(path)) + {} + + /** + * @brief Uses the ProverSRS to create a commitment to p(X) + * + * @param polynomial a univariate polynomial p(X) = ∑ᵢ aᵢ⋅Xⁱ () + * @return Commitment computed as C = [p(x)] = ∑ᵢ aᵢ⋅[xⁱ]₁ + */ + C commit(std::span polynomial) + { + const size_t degree = polynomial.size(); + ASSERT(degree <= srs.get_monomial_size()); + return barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points( + const_cast(polynomial.data()), srs.get_monomial_points(), degree, pippenger_runtime_state); + }; + + barretenberg::scalar_multiplication::pippenger_runtime_state pippenger_runtime_state; + bonk::FileReferenceString srs; +}; + +class VerificationKey { + using Fr = typename barretenberg::g1::Fr; + using C = typename barretenberg::g1::affine_element; + + using Commitment = barretenberg::g1::element; + using Polynomial = barretenberg::Polynomial; + + public: + VerificationKey() = delete; + + /** + * @brief Construct a new Kate Commitment Key object from existing SRS + * + * + * @param verifier_srs verifier G2 point + */ + VerificationKey(const size_t num_points, std::string_view path) + : pippenger_runtime_state(num_points) + , srs(num_points, std::string(path)) + {} + + barretenberg::scalar_multiplication::pippenger_runtime_state pippenger_runtime_state; + bonk::FileReferenceString srs; +}; + +struct Params { + using Fr = typename barretenberg::g1::Fr; + using C = typename barretenberg::g1::affine_element; + + using Commitment = barretenberg::g1::element; + using Polynomial = barretenberg::Polynomial; + + using CK = CommitmentKey; + using VK = VerificationKey; +}; + +} // namespace ipa + } // namespace honk::pcs diff --git a/cpp/src/barretenberg/honk/pcs/commitment_key.test.hpp b/cpp/src/barretenberg/honk/pcs/commitment_key.test.hpp index 016f1b7067..0e59c6f951 100644 --- a/cpp/src/barretenberg/honk/pcs/commitment_key.test.hpp +++ b/cpp/src/barretenberg/honk/pcs/commitment_key.test.hpp @@ -31,6 +31,12 @@ template <> inline std::shared_ptr CreateCommitmentKey(n, kzg_srs_path); } +// For IPA +template <> inline std::shared_ptr CreateCommitmentKey() +{ + const size_t n = 128; + return std::make_shared(n, kzg_srs_path); +} template inline std::shared_ptr CreateCommitmentKey() // requires std::default_initializable @@ -44,13 +50,17 @@ template <> inline std::shared_ptr CreateVerificationKey(kzg_srs_path); } - +// For IPA +template <> inline std::shared_ptr CreateVerificationKey() +{ + const size_t n = 128; + return std::make_shared(n, kzg_srs_path); +} template inline std::shared_ptr CreateVerificationKey() // requires std::default_initializable { return std::make_shared(); } - template class CommitmentTest : public ::testing::Test { using CK = typename Params::CK; using VK = typename Params::VK; @@ -187,6 +197,7 @@ template typename std::shared_ptr CommitmentTest::verification_key = nullptr; using CommitmentSchemeParams = ::testing::Types; +using IpaCommitmentSchemeParams = ::testing::Types; // IMPROVEMENT: reinstate typed-tests for multiple field types, i.e.: // using CommitmentSchemeParams = // ::testing::Types, fake::Params, kzg::Params>; diff --git a/cpp/src/barretenberg/honk/pcs/ipa/ipa.hpp b/cpp/src/barretenberg/honk/pcs/ipa/ipa.hpp index cf6127582e..b4add52fe5 100644 --- a/cpp/src/barretenberg/honk/pcs/ipa/ipa.hpp +++ b/cpp/src/barretenberg/honk/pcs/ipa/ipa.hpp @@ -16,15 +16,18 @@ * @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 { +namespace honk::pcs::ipa { +template class InnerProductArgument { + using Fr = typename Params::Fr; + using element = typename Params::Commitment; + using affine_element = typename Params::C; + using CK = typename Params::CK; + using VK = typename Params::VK; + using Polynomial = barretenberg::Polynomial; + public: - using element = typename G1::element; - using affine_element = typename G1::affine_element; - struct IpaProof { + struct Proof { std::vector L_vec; std::vector R_vec; Fr a_zero; @@ -32,7 +35,7 @@ template class InnerProductArgument { // To contain the public inputs for IPA proof // 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 { + struct PubInput { element commitment; Fr challenge_point; Fr evaluation; @@ -42,52 +45,34 @@ template class InnerProductArgument { }; /** - * @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 + * @brief Compute a proof for opening a single polynomial at a single evaluation point * - * @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 ck The commitment key containing srs and pippenger_runtime_state for computing MSM + * @param 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 + * @return a Proof, 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) + static Proof reduce_prove(std::shared_ptr ck, const PubInput& pub_input, const Polynomial& polynomial) { - IpaProof proof; - auto& challenge_point = ipa_pub_input.challenge_point; + Proof proof; + auto& challenge_point = pub_input.challenge_point; ASSERT(challenge_point != 0 && "The challenge point should not be zero"); - const size_t poly_degree = ipa_pub_input.poly_degree; + const size_t poly_degree = pub_input.poly_degree; // To check poly_degree is greater than zero and a power of two // TODO(#220)(Arijit): 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& aux_generator = pub_input.aux_generator; auto a_vec = polynomial; - // TODO(#220)(Arijit): 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. + // TODO(#220)(Arijit): 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. + auto srs_elements = ck->srs.get_monomial_points(); std::vector G_vec_local(poly_degree); for (size_t i = 0; i < poly_degree; i++) { - G_vec_local[i] = G_vector[i]; + G_vec_local[i] = srs_elements[i]; } // Construct b vector // TODO(#220)(Arijit): For round i=0, b_vec can be derived in-place. @@ -115,22 +100,20 @@ 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(#220)(Arijit): 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); + &a_vec[0], &G_vec_local[round_size], round_size, ck->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_local[0], round_size, pippenger_runtime_state); + &a_vec[round_size], &G_vec_local[0], round_size, ck->pippenger_runtime_state); partial_R += aux_generator * inner_prod_R; L_elements[i] = affine_element(partial_L); R_elements[i] = affine_element(partial_R); // Generate the round challenge. TODO(#220)(Arijit): Use Fiat-Shamir - const Fr round_challenge = ipa_pub_input.round_challenges[i]; + const Fr round_challenge = pub_input.round_challenges[i]; const Fr round_challenge_inv = round_challenge.invert(); // Update the vectors a_vec, b_vec and G_vec. @@ -171,25 +154,23 @@ template class InnerProductArgument { } /** - * @brief Verify the correctness of an IpaProof + * @brief Verify the correctness of a Proof * - * @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 + * @param vk Verification_key containing srs and pippenger_runtime_state to be used for MSM + * @param proof The proof containg L_vec, R_vec and a_zero + * @param pub_input Data required to verify the proof * * @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) + static bool reduce_verify(std::shared_ptr vk, const Proof& proof, const PubInput& pub_input) { // 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; + auto& a_zero = proof.a_zero; + auto& commitment = pub_input.commitment; + auto& challenge_point = pub_input.challenge_point; + auto& evaluation = pub_input.evaluation; + auto& poly_degree = pub_input.poly_degree; + auto& aux_generator = pub_input.aux_generator; // Compute C_prime element C_prime = commitment + (aux_generator * evaluation); @@ -198,18 +179,18 @@ template class InnerProductArgument { 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]; + round_challenges[i] = 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]; + round_challenges_inv[i] = 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]; + L_vec[i] = proof.L_vec[i]; + R_vec[i] = proof.R_vec[i]; } // Compute C_zero = C_prime + ∑_{j ∈ [k]} u_j^2L_j + ∑_{j ∈ [k]} u_j^{-2}R_j @@ -222,9 +203,8 @@ template class InnerProductArgument { 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); + &msm_scalars[0], &msm_elements[0], pippenger_size, vk->pippenger_runtime_state); element C_zero = C_prime + LR_sums; /** @@ -256,17 +236,19 @@ template class InnerProductArgument { } s_vec[i] = s_vec_scalar; } + auto srs_elements = vk->srs.get_monomial_points(); // Copy the G_vector to local memory. std::vector G_vec_local(poly_degree); for (size_t i = 0; i < poly_degree; i++) { - G_vec_local[i] = G_vector[i]; + G_vec_local[i] = srs_elements[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_vec_local[0], poly_degree, pippenger_runtime_state_2); + &s_vec[0], &G_vec_local[0], poly_degree, vk->pippenger_runtime_state); 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 honk::pcs::ipa diff --git a/cpp/src/barretenberg/honk/pcs/ipa/ipa.test.cpp b/cpp/src/barretenberg/honk/pcs/ipa/ipa.test.cpp index 614e5225c8..39bd880312 100644 --- a/cpp/src/barretenberg/honk/pcs/ipa/ipa.test.cpp +++ b/cpp/src/barretenberg/honk/pcs/ipa/ipa.test.cpp @@ -4,50 +4,46 @@ #include "barretenberg/polynomials/polynomial_arithmetic.hpp" #include "barretenberg/polynomials/polynomial.hpp" #include "barretenberg/ecc/curves/bn254/fq12.hpp" +#include "barretenberg/honk/pcs/commitment_key.hpp" +#include "barretenberg/honk/pcs/commitment_key.test.hpp" using namespace barretenberg; +namespace honk::pcs::ipa { -TEST(honk_commitment_scheme, ipa_commit) -{ - constexpr size_t n = 1024; - std::vector scalars(n); - std::vector points(n); +template class IpaCommitmentTest : public CommitmentTest { + using Fr = typename Params::Fr; + using element = typename Params::Commitment; + using affine_element = typename Params::C; + using CK = typename Params::CK; + using VK = typename Params::VK; + using Polynomial = barretenberg::Polynomial; +}; - 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()); - } +TYPED_TEST_SUITE(IpaCommitmentTest, IpaCommitmentSchemeParams); - barretenberg::g1::element expected = points[0] * scalars[0]; +TYPED_TEST(IpaCommitmentTest, commit) +{ + constexpr size_t n = 128; + auto poly = this->random_polynomial(n); + barretenberg::g1::element commitment = this->commit(poly); + auto srs_elements = this->ck()->srs.get_monomial_points(); + barretenberg::g1::element expected = srs_elements[0] * poly[0]; for (size_t i = 1; i < n; i++) { - expected += points[i] * scalars[i]; + expected += srs_elements[i] * poly[i]; } - - InnerProductArgument newIpa; - barretenberg::g1::element result = newIpa.commit(scalars, n, points); - EXPECT_EQ(expected.normalize(), result.normalize()); + EXPECT_EQ(expected.normalize(), commitment.normalize()); } -TEST(honk_commitment_scheme, ipa_open) +TYPED_TEST(IpaCommitmentTest, 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); - } - InnerProductArgument newIpa; - auto C = newIpa.commit(coeffs, n, G_vec); - InnerProductArgument::IpaPubInput pub_input; - pub_input.commitment = C; + using IPA = InnerProductArgument; + using PubInput = typename IPA::PubInput; + // generate a random polynomial, degree needs to be a power of two + size_t n = 128; + auto poly = this->random_polynomial(n); + auto [x, eval] = this->random_eval(poly); + barretenberg::g1::element commitment = this->commit(poly); + PubInput pub_input; + pub_input.commitment = commitment; pub_input.challenge_point = x; pub_input.evaluation = eval; pub_input.poly_degree = n; @@ -58,8 +54,8 @@ TEST(honk_commitment_scheme, ipa_open) 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 = - InnerProductArgument::ipa_verify(proof, pub_input, G_vec); + auto proof = IPA::reduce_prove(this->ck(), pub_input, poly); + auto result = IPA::reduce_verify(this->vk(), proof, pub_input); EXPECT_TRUE(result); } +} // namespace honk::pcs::ipa