Skip to content
13 changes: 6 additions & 7 deletions cpp/src/barretenberg/honk/pcs/commitment_key.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,7 @@ 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.
* 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.
*/
Expand All @@ -216,7 +215,7 @@ class CommitmentKey {
CommitmentKey() = delete;

/**
* @brief Construct a new Kate Commitment Key object from existing SRS
* @brief Construct a new IPA Commitment Key object from existing SRS
*
* @param n
* @param path
Expand Down Expand Up @@ -257,10 +256,10 @@ class VerificationKey {
VerificationKey() = delete;

/**
* @brief Construct a new Kate Commitment Key object from existing SRS
*
*
* @param verifier_srs verifier G2 point
* @brief Construct a new IPA Verification Key object from existing SRS. srs and pippenger_runtime_state are set
* public to be used in 'reduce_verify` method for MSM
* @param num_points
* @param path
*/
VerificationKey(const size_t num_points, std::string_view path)
: pippenger_runtime_state(num_points)
Expand Down
100 changes: 55 additions & 45 deletions cpp/src/barretenberg/honk/pcs/ipa/ipa.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,44 +32,48 @@ template <typename Params> class InnerProductArgument {
std::vector<affine_element> R_vec;
Fr a_zero;
};
// 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 PubInput {
element commitment;
Fr challenge_point;
Fr evaluation;
size_t poly_degree;
element aux_generator; // To be removed
std::vector<Fr> round_challenges; // To be removed
};

/**
* @brief Compute a proof for opening a single polynomial at a single evaluation point
*
* @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 ck Commitment key contains srs and pippenger_runtime_state for computing MSM
* @param opening_pair OpeningPair = {challenge_point, evaluation = polynomial(challenge_point)}
* @param polynomial The witness polynomial whose opening proof needs to be computed
*
* @return a Proof, containing information required to verify whether the commitment is computed correctly and
* @return a Proof, contains information required to verify whether the commitment is computed correctly and
* the polynomial evaluation is correct in the given challenge point.
*/
static Proof reduce_prove(std::shared_ptr<CK> ck, const PubInput& pub_input, const Polynomial& polynomial)
static Proof reduce_prove(std::shared_ptr<CK> ck,
Comment thread
arijitdutta67 marked this conversation as resolved.
const OpeningPair<Params>& opening_pair,
const Polynomial& polynomial,
const auto& transcript)
{
Proof proof;
auto& challenge_point = pub_input.challenge_point;
auto& challenge_point = opening_pair.query;
ASSERT(challenge_point != 0 && "The challenge point should not be zero");
const size_t poly_degree = pub_input.poly_degree;
const size_t poly_degree = polynomial.size();
// 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 = 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.

auto commitment = ck->commit(polynomial);
auto& evaluation = opening_pair.evaluation;
transcript->add_element("ipa_commitment", static_cast<affine_element>(commitment).to_buffer());
transcript->add_element("ipa_challenge_point", static_cast<Fr>(challenge_point).to_buffer());
transcript->add_element("ipa_eval", static_cast<Fr>(evaluation).to_buffer());
transcript->apply_fiat_shamir("ipa_aux");

const Fr aux_challenge = Fr::serialize_from_buffer(transcript->get_challenge("ipa_aux").begin());
auto srs_elements = ck->srs.get_monomial_points();
const auto aux_generator = srs_elements[poly_degree] * aux_challenge;

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.

std::vector<affine_element> G_vec_local(poly_degree);
for (size_t i = 0; i < poly_degree; i++) {
G_vec_local[i] = srs_elements[i];
Expand Down Expand Up @@ -111,9 +115,13 @@ template <typename Params> class InnerProductArgument {

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 = pub_input.round_challenges[i];
// Using Fiat-Shamir
auto label = std::to_string(i);
transcript->add_element("L_" + label, static_cast<affine_element>(partial_L).to_buffer());
transcript->add_element("R_" + label, static_cast<affine_element>(partial_R).to_buffer());
transcript->apply_fiat_shamir("ipa_round_" + label);
const Fr round_challenge =
Fr::serialize_from_buffer(transcript->get_challenge("ipa_round_" + label).begin());
const Fr round_challenge_inv = round_challenge.invert();

// Update the vectors a_vec, b_vec and G_vec.
Expand Down Expand Up @@ -156,34 +164,41 @@ template <typename Params> class InnerProductArgument {
/**
* @brief Verify the correctness of a Proof
*
* @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
* @param vk Verification_key contains the srs and pippenger_runtime_state to be used for MSM
* @param claim OpeningClaim contains the commitment, challenge, and the evaluation
* @param proof Proof contains L_vec, R_vec, and a_zero
* @param transcript Transcript contains the round challenges and the aux challenge
*
* @return true/false depending on if the proof verifies
*/
static bool reduce_verify(std::shared_ptr<VK> vk, const Proof& proof, const PubInput& pub_input)
static bool reduce_verify(std::shared_ptr<VK> vk,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To follow the idiom established in KZG, we would use a class ipa::VerificationKey to avoid passing the vk as an argument here.. I see you defined this and tested it but that you don't use it. Apologies if I forgot a discussion on this--did you explain why you chose this approach?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This problem stems from the fact that ipa::VerificationKey class does not take ownership of SRS and pippenger_runtime_state (we have an issue for this here

* TODO(#218)(Adrian): This class should take ownership of the SRS, and handle reading the file from disk.
). These two parameters are needed in ipa::reduce_verify for MSM unlike other PCSs. These parameter are set by calling a constructor in commitment_key.test.cpp here. Under this settings, we access these parameters by passing the vk shared ptr as argument to ipa::reduce_verify in ipa_test.cpp here.
This was a due point in PR #205, and we decided to go with this keeping this as an issue (#236). In the current PR, we somehow solved the issue with reduce_prove by using the transcript. But I could not see a path to resolve it for reduce_verify. Please let me know if this makes sense. I shall record this in the issue #236.

const OpeningClaim<Params>& claim,
const Proof& proof,
const auto& transcript)
{
// Local copies of public inputs
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;
auto& commitment = claim.commitment;
auto& challenge_point = claim.opening_pair.query;
auto& evaluation = claim.opening_pair.evaluation;
const auto& log_poly_degree = static_cast<size_t>(proof.L_vec.size());
const auto& poly_degree = static_cast<size_t>(static_cast<uint64_t>(Fr(2).pow(log_poly_degree)));

const Fr aux_challenge = Fr::serialize_from_buffer(transcript->get_challenge("ipa_aux").begin());
auto srs_elements = vk->srs.get_monomial_points();
const auto aux_generator = srs_elements[poly_degree] * aux_challenge;

// 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<size_t>(numeric::get_msb(poly_degree));
std::vector<Fr> round_challenges(log_poly_degree);
for (size_t i = 0; i < log_poly_degree; i++) {
round_challenges[i] = pub_input.round_challenges[i];
auto label = std::to_string(i);
round_challenges[i] = Fr::serialize_from_buffer(transcript->get_challenge("ipa_round_" + label).begin());
}
std::vector<Fr> round_challenges_inv(log_poly_degree);
for (size_t i = 0; i < log_poly_degree; i++) {
round_challenges_inv[i] = pub_input.round_challenges[i];
round_challenges_inv[i] = round_challenges[i];
}
Fr::batch_invert(&round_challenges_inv[0], log_poly_degree);
std::vector<affine_element> L_vec(log_poly_degree);
Expand Down Expand Up @@ -236,14 +251,9 @@ template <typename Params> 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<affine_element> G_vec_local(poly_degree);
for (size_t i = 0; i < poly_degree; i++) {
G_vec_local[i] = srs_elements[i];
}

auto G_zero = barretenberg::scalar_multiplication::pippenger_without_endomorphism_basis_points(
&s_vec[0], &G_vec_local[0], poly_degree, vk->pippenger_runtime_state);
&s_vec[0], &srs_elements[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;
Expand Down
62 changes: 46 additions & 16 deletions cpp/src/barretenberg/honk/pcs/ipa/ipa.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "barretenberg/ecc/curves/bn254/fq12.hpp"
#include "barretenberg/honk/pcs/commitment_key.hpp"
#include "barretenberg/honk/pcs/commitment_key.test.hpp"
#include "barretenberg/transcript/manifest.hpp"
using namespace barretenberg;
namespace honk::pcs::ipa {

Expand All @@ -18,6 +19,41 @@ template <class Params> class IpaCommitmentTest : public CommitmentTest<Params>
using Polynomial = barretenberg::Polynomial<Fr>;
};

// Creating mock manifest to test only the IPA PCS

static transcript::Manifest create_mock_manifest(const size_t num_ipa_rounds)
{
constexpr size_t g1_size = 64;
constexpr size_t fr_size = 32;
std::vector<transcript::Manifest::RoundManifest> manifest_rounds;

std::vector<transcript::Manifest::ManifestEntry> aux_generator_entries;
aux_generator_entries.emplace_back(transcript::Manifest::ManifestEntry(
{ .name = "ipa_commitment", .num_bytes = g1_size, .derived_by_verifier = false }));
aux_generator_entries.emplace_back(transcript::Manifest::ManifestEntry(
{ .name = "ipa_challenge_point", .num_bytes = fr_size, .derived_by_verifier = false }));
aux_generator_entries.emplace_back(transcript::Manifest::ManifestEntry(
{ .name = "ipa_eval", .num_bytes = fr_size, .derived_by_verifier = false }));
manifest_rounds.emplace_back(transcript::Manifest::RoundManifest(aux_generator_entries,
/* challenge_name = */ "ipa_aux",
/* num_challenges_in */ 1));
std::vector<transcript::Manifest::ManifestEntry> ipa_round_challenges_entries;
for (size_t i = 0; i < num_ipa_rounds; i++) {
auto label = std::to_string(i);
ipa_round_challenges_entries.emplace_back(transcript::Manifest::ManifestEntry(
{ .name = "L_" + label, .num_bytes = g1_size, .derived_by_verifier = false }));
ipa_round_challenges_entries.emplace_back(transcript::Manifest::ManifestEntry(
{ .name = "R_" + label, .num_bytes = g1_size, .derived_by_verifier = false }));
manifest_rounds.emplace_back(transcript::Manifest::RoundManifest(ipa_round_challenges_entries,
/* challenge_name = */ "ipa_round_" + label,
/* num_challenges_in */ 1));
ipa_round_challenges_entries.clear();
};

auto output = transcript::Manifest(manifest_rounds);
return output;
}

TYPED_TEST_SUITE(IpaCommitmentTest, IpaCommitmentSchemeParams);

TYPED_TEST(IpaCommitmentTest, commit)
Expand All @@ -36,26 +72,20 @@ TYPED_TEST(IpaCommitmentTest, commit)
TYPED_TEST(IpaCommitmentTest, open)
{
using IPA = InnerProductArgument<TypeParam>;
using PubInput = typename IPA::PubInput;
// generate a random polynomial, degree needs to be a power of two
size_t n = 128;
const size_t log_n = static_cast<size_t>(numeric::get_msb(n));
using Transcript = transcript::StandardTranscript;
auto transcript = std::make_shared<Transcript>(create_mock_manifest(log_n));
auto poly = this->random_polynomial(n);
auto [x, eval] = this->random_eval(poly);
auto [challenge_point, 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;
auto aux_scalar = fr::random_element();
pub_input.aux_generator = barretenberg::g1::one * aux_scalar;
const size_t log_n = static_cast<size_t>(numeric::get_msb(n));
pub_input.round_challenges = std::vector<barretenberg::fr>(log_n);
for (size_t i = 0; i < log_n; i++) {
pub_input.round_challenges[i] = barretenberg::fr::random_element();
}
auto proof = IPA::reduce_prove(this->ck(), pub_input, poly);
auto result = IPA::reduce_verify(this->vk(), proof, pub_input);

auto opening_pair = OpeningPair<TypeParam>{ challenge_point, eval };
auto opening_claim = OpeningClaim<TypeParam>{ opening_pair, commitment };

auto proof = IPA::reduce_prove(this->ck(), opening_pair, poly, transcript);
auto result = IPA::reduce_verify(this->vk(), opening_claim, proof, transcript);
EXPECT_TRUE(result);
}
} // namespace honk::pcs::ipa