Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions barretenberg/cpp/src/barretenberg/commitment_schemes/claim.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,17 @@ template <typename Curve> class OpeningClaim {

bool operator==(const OpeningClaim& other) const = default;
};

/**
* @brief An accumulator consisting of the Shplonk evaluation challenge and vectors of commitments and scalars.
*
* @details This structure is used in the `reduce_verify_batch_opening_claim` method of KZG or IPA.
*
* @tparam Curve: BN254 or Grumpkin.
*/
template <typename Curve> struct BatchOpeningClaim {
std::vector<typename Curve::AffineElement> commitments;
std::vector<typename Curve::ScalarField> scalars;
typename Curve::ScalarField evaluation_point;
};
} // namespace bb
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ GeminiProverOutput<Curve> GeminiProver_<Curve>::compute_fold_polynomial_evaluati
Polynomial& batched_G = gemini_polynomials[1]; // G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X)

// Compute univariate opening queries rₗ = r^{2ˡ} for l = 0, 1, ..., m-1
std::vector<Fr> r_squares = gemini::squares_of_r(r_challenge, num_variables);
std::vector<Fr> r_squares = gemini::powers_of_evaluation_challenge(r_challenge, num_variables);

// Compute G/r
Fr r_inv = r_challenge.invert();
Expand Down
108 changes: 65 additions & 43 deletions barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ template <class Fr> inline std::vector<Fr> powers_of_rho(const Fr rho, const siz
* @param num_squares The number of foldings
* @return std::vector<typename Curve::ScalarField>
*/
template <class Fr> inline std::vector<Fr> squares_of_r(const Fr r, const size_t num_squares)
template <class Fr> inline std::vector<Fr> powers_of_evaluation_challenge(const Fr r, const size_t num_squares)
{
std::vector<Fr> squares = { r };
squares.reserve(num_squares);
Expand Down Expand Up @@ -132,36 +132,25 @@ template <typename Curve> class GeminiVerifier_ {
* (Cⱼ, Aⱼ(-r^{2ʲ}), -r^{2}), j = [1, ..., m-1]
*/
static std::vector<OpeningClaim<Curve>> reduce_verification(std::span<const Fr> mle_opening_point, /* u */
const Fr batched_evaluation, /* all */
Fr& batched_evaluation, /* all */
Copy link
Contributor

Choose a reason for hiding this comment

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

Side note: every prover/verifier class should have prove/verify methods

GroupElement& batched_f, /* unshifted */
GroupElement& batched_g, /* to-be-shifted */
auto& transcript)
{
const size_t num_variables = mle_opening_point.size();

// Get polynomials Fold_i, i = 1,...,m-1 from transcript
std::vector<Commitment> commitments;
commitments.reserve(num_variables - 1);
for (size_t i = 0; i < num_variables - 1; ++i) {
auto commitment =
transcript->template receive_from_prover<Commitment>("Gemini:FOLD_" + std::to_string(i + 1));
commitments.emplace_back(commitment);
}
const std::vector<Commitment> commitments = get_gemini_commitments(num_variables, transcript);

// compute vector of powers of random evaluation point r
const Fr r = transcript->template get_challenge<Fr>("Gemini:r");
std::vector<Fr> r_squares = gemini::squares_of_r(r, num_variables);
const std::vector<Fr> r_squares = gemini::powers_of_evaluation_challenge(r, num_variables);

// Get evaluations a_i, i = 0,...,m-1 from transcript
std::vector<Fr> evaluations;
evaluations.reserve(num_variables);
for (size_t i = 0; i < num_variables; ++i) {
auto eval = transcript->template receive_from_prover<Fr>("Gemini:a_" + std::to_string(i));
evaluations.emplace_back(eval);
}

const std::vector<Fr> evaluations = get_gemini_evaluations(num_variables, transcript);
// Compute evaluation A₀(r)
auto a_0_pos = compute_eval_pos(batched_evaluation, mle_opening_point, r_squares, evaluations);
auto a_0_pos =
compute_gemini_batched_univariate_evaluation(batched_evaluation, mle_opening_point, r_squares, evaluations);

// C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] + r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ]
// C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] - r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ]
Expand All @@ -183,42 +172,75 @@ template <typename Curve> class GeminiVerifier_ {
return fold_polynomial_opening_claims;
}

private:
static std::vector<Commitment> get_gemini_commitments(const size_t log_circuit_size, auto& transcript)
{
std::vector<Commitment> gemini_commitments;
gemini_commitments.reserve(log_circuit_size - 1);
for (size_t i = 0; i < log_circuit_size - 1; ++i) {
const Commitment commitment =
transcript->template receive_from_prover<Commitment>("Gemini:FOLD_" + std::to_string(i + 1));
gemini_commitments.emplace_back(commitment);
}
return gemini_commitments;
}
static std::vector<Fr> get_gemini_evaluations(const size_t log_circuit_size, auto& transcript)
{
std::vector<Fr> gemini_evaluations;
gemini_evaluations.reserve(log_circuit_size);
for (size_t i = 0; i < log_circuit_size; ++i) {
const Fr evaluation = transcript->template receive_from_prover<Fr>("Gemini:a_" + std::to_string(i));
gemini_evaluations.emplace_back(evaluation);
}
return gemini_evaluations;
}

/**
* @brief Compute the expected evaluation of the univariate commitment to the batched polynomial.
*
* @param batched_mle_eval The evaluation of the folded polynomials
* @param mle_vars MLE opening point u
* @param r_squares squares of r, r², ..., r^{2ᵐ⁻¹}
* @param fold_polynomial_evals series of Aᵢ₋₁(−r^{2ⁱ⁻¹})
* @return evaluation A₀(r)
* Compute the evaluation \f$ A_0(r) = \sum \rho^i \cdot f_i + \frac{1}{r} \cdot \sum \rho^{i+k} g_i \f$, where \f$
* k \f$ is the number of "unshifted" commitments.
*
* @details Initialize \f$ A_{d}(r) \f$ with the batched evaluation \f$ \sum \rho^i f_i(\vec{u}) + \sum \rho^{i+k}
* g_i(\vec{u}) \f$. The folding property ensures that
* \f{align}{
* A_\ell\left(r^{2^\ell}\right) = (1 - u_{\ell-1}) \cdot \frac{A_{\ell-1}\left(r^{2^{\ell-1}}\right) +
* A_{\ell-1}\left(-r^{2^{\ell-1}}\right)}{2}
* + u_{\ell-1} \cdot \frac{A_{\ell-1}\left(r^{2^{\ell-1}}\right) -
* A_{\ell-1}\left(-r^{2^{\ell-1}}\right)}{2r^{2^{\ell-1}}}
* \f}
* Therefore, the verifier can recover \f$ A_0(r) \f$ by solving several linear equations.
*
* @param batched_mle_eval The evaluation of the batched polynomial at \f$ (u_0, \ldots, u_{d-1})\f$.
* @param evaluation_point Evaluation point \f$ (u_0, \ldots, u_{d-1}) \f$.
* @param challenge_powers Powers of \f$ r \f$, \f$ r^2 \), ..., \( r^{2^{m-1}} \f$.
* @param fold_polynomial_evals Evaluations \f$ A_{i-1}(-r^{2^{i-1}}) \f$.
* @return Evaluation \f$ A_0(r) \f$.
*/
static Fr compute_eval_pos(const Fr batched_mle_eval,
std::span<const Fr> mle_vars,
std::span<const Fr> r_squares,
std::span<const Fr> fold_polynomial_evals)
static Fr compute_gemini_batched_univariate_evaluation(Fr& batched_eval_accumulator,
std::span<const Fr> evaluation_point,
std::span<const Fr> challenge_powers,
std::span<const Fr> fold_polynomial_evals)
{
const size_t num_variables = mle_vars.size();
const size_t num_variables = evaluation_point.size();

const auto& evals = fold_polynomial_evals;

// Initialize eval_pos with batched MLE eval v = ∑ⱼ ρʲ vⱼ + ∑ⱼ ρᵏ⁺ʲ v↺ⱼ
Fr eval_pos = batched_mle_eval;
// Solve the sequence of linear equations
for (size_t l = num_variables; l != 0; --l) {
const Fr r = r_squares[l - 1]; // = rₗ₋₁ = r^{2ˡ⁻¹}
const Fr eval_neg = evals[l - 1]; // = Aₗ₋₁(−r^{2ˡ⁻¹})
const Fr u = mle_vars[l - 1]; // = uₗ₋₁

// The folding property ensures that
// Aₗ₋₁(r^{2ˡ⁻¹}) + Aₗ₋₁(−r^{2ˡ⁻¹}) Aₗ₋₁(r^{2ˡ⁻¹}) - Aₗ₋₁(−r^{2ˡ⁻¹})
// Aₗ(r^{2ˡ}) = (1-uₗ₋₁) ----------------------------- + uₗ₋₁ -----------------------------
// 2 2r^{2ˡ⁻¹}
// We solve the above equation in Aₗ₋₁(r^{2ˡ⁻¹}), using the previously computed Aₗ(r^{2ˡ}) in eval_pos
// and using Aₗ₋₁(−r^{2ˡ⁻¹}) sent by the prover in the proof.
eval_pos = ((r * eval_pos * 2) - eval_neg * (r * (Fr(1) - u) - u)) / (r * (Fr(1) - u) + u);
// Get r²⁽ˡ⁻¹⁾
const Fr& challenge_power = challenge_powers[l - 1];
// Get A₍ₗ₋₁₎(−r²⁽ˡ⁻¹⁾)
const Fr& eval_neg = evals[l - 1];
// Get uₗ₋₁
const Fr& u = evaluation_point[l - 1];
// Compute the numerator
batched_eval_accumulator =
((challenge_power * batched_eval_accumulator * 2) - eval_neg * (challenge_power * (Fr(1) - u) - u));
// Divide by the denominator
batched_eval_accumulator *= (challenge_power * (Fr(1) - u) + u).invert();
}

return eval_pos; // return A₀(r)
return batched_eval_accumulator;
}

/**
Expand Down
44 changes: 44 additions & 0 deletions barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include "barretenberg/commitment_schemes/claim.hpp"
#include "barretenberg/commitment_schemes/utils/batch_mul_native.hpp"
#include "barretenberg/commitment_schemes/verification_key.hpp"
#include "barretenberg/common/assert.hpp"
#include "barretenberg/common/container.hpp"
Expand Down Expand Up @@ -572,6 +573,49 @@ template <typename Curve_> class IPA {
{
return reduce_verify_internal(vk, opening_claim, transcript);
}
/**
* @brief A method that produces an IPA opening claim from Shplemini accumulator containing vectors of commitments
* and scalars and a Shplonk evaluation challenge.
*
* @details Compute the commitment \f$ C \f$ that will be used to prove that Shplonk batching is performed correctly
* and check the evaluation claims of the batched univariate polynomials. The check is done by verifying that the
* polynomial corresponding to \f$ C \f$ evaluates to \f$ 0 \f$ at the Shplonk challenge point \f$ z \f$.
*
*/
static OpeningClaim<Curve> reduce_batch_opening_claim(
const BatchOpeningClaim<Curve>& batch_opening_claim)
{
using Utils = CommitmentSchemesUtils<Curve>;
// Extract batch_mul arguments from the accumulator
const auto& commitments = batch_opening_claim.commitments;
const auto& scalars = batch_opening_claim.scalars;
const Fr& shplonk_eval_challenge = batch_opening_claim.evaluation_point;
// Compute \f$ C = \sum \text{commitments}_i \cdot \text{scalars}_i \f$
GroupElement shplonk_output_commitment;
if constexpr (Curve::is_stdlib_type) {
shplonk_output_commitment =
GroupElement::batch_mul(commitments, scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
} else {
shplonk_output_commitment = Utils::batch_mul_native(commitments, scalars);
}
// Output an opening claim to be verified by the IPA opening protocol
return { { shplonk_eval_challenge, Fr(0) }, shplonk_output_commitment };
}
/**
* @brief Verify the IPA opening claim obtained from a Shplemini accumulator
*
* @param batch_opening_claim
* @param vk
* @param transcript
* @return VerifierAccumulator
*/
static VerifierAccumulator reduce_verify_batch_opening_claim(const BatchOpeningClaim<Curve>& batch_opening_claim,
const std::shared_ptr<VK>& vk,
auto& transcript)
{
const auto opening_claim = reduce_batch_opening_claim(batch_opening_claim);
return reduce_verify_internal(vk, opening_claim, transcript);
}
};

} // namespace bb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

#include "../gemini/gemini.hpp"
#include "../shplonk/shplemini_verifier.hpp"
#include "../shplonk/shplonk.hpp"
#include "./mock_transcript.hpp"
#include "barretenberg/commitment_schemes/commitment_key.test.hpp"
Expand All @@ -22,6 +23,7 @@ class IPATest : public CommitmentTest<Curve> {
using CK = CommitmentKey<Curve>;
using VK = VerifierCommitmentKey<Curve>;
using Polynomial = bb::Polynomial<Fr>;
using Commitment = typename Curve::AffineElement;
};
} // namespace

Expand Down Expand Up @@ -246,7 +248,7 @@ TEST_F(IPATest, GeminiShplonkIPAWithShift)

// Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random
// point.
const auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
auto poly1 = this->random_polynomial(n);
auto poly2 = this->random_polynomial(n);
poly2[0] = Fr::zero(); // this property is required of polynomials whose shift is used
Expand Down Expand Up @@ -321,3 +323,87 @@ TEST_F(IPATest, GeminiShplonkIPAWithShift)

EXPECT_EQ(result, true);
}
TEST_F(IPATest, ShpleminiIPAWithShift)
{
using IPA = IPA<Curve>;
using ShplonkProver = ShplonkProver_<Curve>;
using ShpleminiVerifier = ShpleminiVerifier_<Curve>;
using GeminiProver = GeminiProver_<Curve>;

const size_t n = 8;
const size_t log_n = 3;

// Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random
// point.
auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
auto poly1 = this->random_polynomial(n);
auto poly2 = this->random_polynomial(n);
poly2[0] = Fr::zero(); // this property is required of polynomials whose shift is used

Commitment commitment1 = this->commit(poly1);
Commitment commitment2 = this->commit(poly2);
std::vector<Commitment> unshifted_commitments = { commitment1, commitment2 };
std::vector<Commitment> shifted_commitments = { commitment2 };
auto eval1 = poly1.evaluate_mle(mle_opening_point);
auto eval2 = poly2.evaluate_mle(mle_opening_point);
auto eval2_shift = poly2.evaluate_mle(mle_opening_point, true);

std::vector<Fr> multilinear_evaluations = { eval1, eval2, eval2_shift };

auto prover_transcript = NativeTranscript::prover_init_empty();
Fr rho = prover_transcript->template get_challenge<Fr>("rho");
std::vector<Fr> rhos = gemini::powers_of_rho(rho, multilinear_evaluations.size());

Fr batched_evaluation = Fr::zero();
for (size_t i = 0; i < rhos.size(); ++i) {
batched_evaluation += multilinear_evaluations[i] * rhos[i];
}

Polynomial batched_unshifted(n);
Polynomial batched_to_be_shifted(n);
batched_unshifted.add_scaled(poly1, rhos[0]);
batched_unshifted.add_scaled(poly2, rhos[1]);
batched_to_be_shifted.add_scaled(poly2, rhos[2]);

auto gemini_polynomials = GeminiProver::compute_gemini_polynomials(
mle_opening_point, std::move(batched_unshifted), std::move(batched_to_be_shifted));

for (size_t l = 0; l < log_n - 1; ++l) {
std::string label = "FOLD_" + std::to_string(l + 1);
auto commitment = this->ck()->commit(gemini_polynomials[l + 2]);
prover_transcript->send_to_verifier(label, commitment);
}

const Fr r_challenge = prover_transcript->template get_challenge<Fr>("Gemini:r");

const auto [gemini_opening_pairs, gemini_witnesses] = GeminiProver::compute_fold_polynomial_evaluations(
mle_opening_point, std::move(gemini_polynomials), r_challenge);

std::vector<ProverOpeningClaim<Curve>> opening_claims;

for (size_t l = 0; l < log_n; ++l) {
std::string label = "Gemini:a_" + std::to_string(l);
const auto& evaluation = gemini_opening_pairs[l + 1].evaluation;
prover_transcript->send_to_verifier(label, evaluation);
opening_claims.emplace_back(gemini_witnesses[l], gemini_opening_pairs[l]);
}
opening_claims.emplace_back(gemini_witnesses[log_n], gemini_opening_pairs[log_n]);

const auto opening_claim = ShplonkProver::prove(this->ck(), opening_claims, prover_transcript);
IPA::compute_opening_proof(this->ck(), opening_claim, prover_transcript);

auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);

const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(log_n,
RefVector(unshifted_commitments),
RefVector(shifted_commitments),
RefVector(multilinear_evaluations),
mle_opening_point,
this->vk()->get_g1_identity(),
verifier_transcript);

auto result = IPA::reduce_verify_batch_opening_claim(batch_opening_claim, this->vk(), verifier_transcript);
// auto result = IPA::reduce_verify(this->vk(), shplonk_verifier_claim, verifier_transcript);

EXPECT_EQ(result, true);
}
Loading