Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Honk recursive verifier Pt. 1 #1488

Merged
merged 15 commits into from
Aug 18, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@ class BN254 {
using G2AffineElement = typename barretenberg::g2::affine_element;
using G2BaseField = typename barretenberg::fq2;
using TargetField = barretenberg::fq12;

// TODO(#673): This flag is temporary. It is needed in the verifier classes (GeminiVerifier, etc.) while these
// classes are instantiated with "native" curve types. Eventually, the verifier classes will be instantiated only
// with stdlib types, and "native" verification will be acheived via a simulated builder.
static constexpr bool is_stdlib_type = false;
};
} // namespace curve
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,10 @@ class Grumpkin {
using Group = typename grumpkin::g1;
using Element = typename Group::element;
using AffineElement = typename Group::affine_element;

// TODO(#673): This flag is temporary. It is needed in the verifier classes (GeminiVerifier, etc.) while these
// classes are instantiated with "native" curve types. Eventually, the verifier classes will be instantiated only
// with stdlib types, and "native" verification will be acheived via a simulated builder.
static constexpr bool is_stdlib_type = false;
};
} // namespace curve

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -186,136 +186,7 @@ ProverOutput<Curve> GeminiProver_<Curve>::compute_fold_polynomial_evaluations(st
return { fold_poly_opening_pairs, std::move(fold_polynomials) };
};

Copy link
Contributor Author

@ledwards2225 ledwards2225 Aug 16, 2023

Choose a reason for hiding this comment

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

I've moved the definitions of the the GeminiVerifier methods back to the header. This avoids a lot of pain, at least while we need to instantiate it with both native and stdlib Curves. It should be easy to revert once this is no longer the case (see bberg issue no. 673)

/**
* @brief Returns univariate opening claims for the Fold polynomials to be checked later
*
* @param mle_opening_point the MLE evaluation point u
* @param batched_evaluation batched evaluation from multivariate evals at the point u
* @param batched_f batched commitment to unshifted polynomials
* @param batched_g batched commitment to to-be-shifted polynomials
* @param proof commitments to the m-1 folded polynomials, and alleged evaluations.
* @param transcript
* @return Fold polynomial opening claims: (r, A₀(r), C₀₊), (-r, A₀(-r), C₀₋), and
* (Cⱼ, Aⱼ(-r^{2ʲ}), -r^{2}), j = [1, ..., m-1]
*/

template <typename Curve>
std::vector<OpeningClaim<Curve>> GeminiVerifier_<Curve>::reduce_verification(std::span<const Fr> mle_opening_point, /* u */
const Fr batched_evaluation, /* all */
GroupElement& batched_f, /* unshifted */
GroupElement& batched_g, /* to-be-shifted */
VerifierTranscript<Fr>& 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);
}

// compute vector of powers of random evaluation point r
const Fr r = transcript.get_challenge("Gemini:r");
std::vector<Fr> r_squares = squares_of_r(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);
}

// Compute evaluation A₀(r)
auto a_0_pos = compute_eval_pos(batched_evaluation, mle_opening_point, r_squares, evaluations);

// C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] + r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ]
// C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] - r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ]
auto [c0_r_pos, c0_r_neg] = compute_simulated_commitments(batched_f, batched_g, r);

std::vector<OpeningClaim<Curve>> fold_polynomial_opening_claims;
fold_polynomial_opening_claims.reserve(num_variables + 1);

// ( [A₀₊], r, A₀(r) )
fold_polynomial_opening_claims.emplace_back(OpeningClaim<Curve>{ { r, a_0_pos }, c0_r_pos });
// ( [A₀₋], -r, A₀(-r) )
fold_polynomial_opening_claims.emplace_back(OpeningClaim<Curve>{ { -r, evaluations[0] }, c0_r_neg });
for (size_t l = 0; l < num_variables - 1; ++l) {
// ([A₀₋], −r^{2ˡ}, Aₗ(−r^{2ˡ}) )
fold_polynomial_opening_claims.emplace_back(
OpeningClaim<Curve>{ { -r_squares[l + 1], evaluations[l + 1] }, commitments[l] });
}

return fold_polynomial_opening_claims;
};

/**
* @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)
*/
template <typename Curve>
typename Curve::ScalarField GeminiVerifier_<Curve>::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)
{
const size_t num_variables = mle_vars.size();

const auto& evals = fold_polynomial_evals;

// Initialize eval_pos with batched MLE eval v = ∑ⱼ ρʲ vⱼ + ∑ⱼ ρᵏ⁺ʲ v↺ⱼ
Fr eval_pos = batched_mle_eval;
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);
}

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

/**
* @brief Computes two commitments to A₀ partially evaluated in r and -r.
*
* @param batched_f batched commitment to non-shifted polynomials
* @param batched_g batched commitment to to-be-shifted polynomials
* @param r evaluation point at which we have partially evaluated A₀ at r and -r.
* @return std::pair<Commitment, Commitment> c0_r_pos, c0_r_neg
*/
template <typename Curve>
std::pair<typename Curve::Element, typename Curve::Element> GeminiVerifier_<Curve>::compute_simulated_commitments(
GroupElement& batched_f, GroupElement& batched_g, Fr r)
{
// C₀ᵣ₊ = [F] + r⁻¹⋅[G]
GroupElement C0_r_pos = batched_f;
// C₀ᵣ₋ = [F] - r⁻¹⋅[G]
GroupElement C0_r_neg = batched_f;
Fr r_inv = r.invert();
if (!batched_g.is_point_at_infinity()) {
batched_g *= r_inv;
C0_r_pos += batched_g;
C0_r_neg -= batched_g;
}
return { C0_r_pos, C0_r_neg };
};

template class GeminiProver_<curve::BN254>;
template class GeminiProver_<curve::Grumpkin>;
template class GeminiVerifier_<curve::BN254>;
template class GeminiVerifier_<curve::Grumpkin>;
}; // namespace proof_system::honk::pcs::gemini
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ template <typename Curve> struct ProverOutput {

/**
* @brief Compute powers of challenge ρ
*
* @tparam Fr
* @param rho
* @param num_powers
* @return std::vector<Fr>
*
* @tparam Fr
* @param rho
* @param num_powers
* @return std::vector<Fr>
*/
template <class Fr> inline std::vector<Fr> powers_of_rho(const Fr rho, const size_t num_powers)
{
Expand Down Expand Up @@ -107,8 +107,8 @@ template <typename Curve> class GeminiProver_ {
Polynomial&& batched_to_be_shifted);

static ProverOutput<Curve> compute_fold_polynomial_evaluations(std::span<const Fr> mle_opening_point,
std::vector<Polynomial>&& fold_polynomials,
const Fr& r_challenge);
std::vector<Polynomial>&& fold_polynomials,
const Fr& r_challenge);
}; // namespace proof_system::honk::pcs::gemini

template <typename Curve> class GeminiVerifier_ {
Expand All @@ -117,26 +117,143 @@ template <typename Curve> class GeminiVerifier_ {
using Commitment = typename Curve::AffineElement;

public:
/**
* @brief Returns univariate opening claims for the Fold polynomials to be checked later
*
* @param mle_opening_point the MLE evaluation point u
* @param batched_evaluation batched evaluation from multivariate evals at the point u
* @param batched_f batched commitment to unshifted polynomials
* @param batched_g batched commitment to to-be-shifted polynomials
* @param proof commitments to the m-1 folded polynomials, and alleged evaluations.
* @param transcript
* @return Fold polynomial opening claims: (r, A₀(r), C₀₊), (-r, A₀(-r), C₀₋), and
* (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 */
GroupElement& batched_f, /* unshifted */
GroupElement& batched_g, /* to-be-shifted */
VerifierTranscript<Fr>& transcript);
const Fr batched_evaluation, /* all */
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);
}

// compute vector of powers of random evaluation point r
const Fr r = transcript.get_challenge("Gemini:r");
std::vector<Fr> r_squares = squares_of_r(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);
}

// Compute evaluation A₀(r)
auto a_0_pos = compute_eval_pos(batched_evaluation, mle_opening_point, r_squares, evaluations);

// C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] + r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ]
// C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] - r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ]
auto [c0_r_pos, c0_r_neg] = compute_simulated_commitments(batched_f, batched_g, r);

std::vector<OpeningClaim<Curve>> fold_polynomial_opening_claims;
fold_polynomial_opening_claims.reserve(num_variables + 1);

// ( [A₀₊], r, A₀(r) )
fold_polynomial_opening_claims.emplace_back(OpeningClaim<Curve>{ { r, a_0_pos }, c0_r_pos });
// ( [A₀₋], -r, A₀(-r) )
fold_polynomial_opening_claims.emplace_back(OpeningClaim<Curve>{ { -r, evaluations[0] }, c0_r_neg });
for (size_t l = 0; l < num_variables - 1; ++l) {
// ([A₀₋], −r^{2ˡ}, Aₗ(−r^{2ˡ}) )
fold_polynomial_opening_claims.emplace_back(
OpeningClaim<Curve>{ { -r_squares[l + 1], evaluations[l + 1] }, commitments[l] });
}

return fold_polynomial_opening_claims;
}

private:
/**
* @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)
*/
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);
std::span<const Fr> fold_polynomial_evals)
{
const size_t num_variables = mle_vars.size();

const auto& evals = fold_polynomial_evals;

// Initialize eval_pos with batched MLE eval v = ∑ⱼ ρʲ vⱼ + ∑ⱼ ρᵏ⁺ʲ v↺ⱼ
Fr eval_pos = batched_mle_eval;
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);
}

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

/**
* @brief Computes two commitments to A₀ partially evaluated in r and -r.
*
* @param batched_f batched commitment to non-shifted polynomials
* @param batched_g batched commitment to to-be-shifted polynomials
* @param r evaluation point at which we have partially evaluated A₀ at r and -r.
* @return std::pair<Commitment, Commitment> c0_r_pos, c0_r_neg
*/
static std::pair<GroupElement, GroupElement> compute_simulated_commitments(GroupElement& batched_f,
GroupElement& batched_g,
Fr r);
Fr r)
{
// C₀ᵣ₊ = [F] + r⁻¹⋅[G]
GroupElement C0_r_pos = batched_f;
// C₀ᵣ₋ = [F] - r⁻¹⋅[G]
GroupElement C0_r_neg = batched_f;
Fr r_inv = r.invert();

// TODO(luke): reinstate some kind of !batched_g.is_point_at_infinity() check for stdlib types? This is mostly
// relevant for Gemini unit tests since in practice batched_g != zero (i.e. we will always have shifted polys).
bool batched_g_is_point_at_infinity = false;
if constexpr (!Curve::is_stdlib_type) { // Note: required for Gemini tests with no shifts
batched_g_is_point_at_infinity = batched_g.is_point_at_infinity();
}
if (!batched_g_is_point_at_infinity) {
batched_g = batched_g * r_inv;
C0_r_pos += batched_g;
C0_r_neg -= batched_g;
}

return { C0_r_pos, C0_r_neg };
}

}; // namespace proof_system::honk::pcs::gemini

extern template class GeminiProver_<curve::BN254>;
extern template class GeminiProver_<curve::Grumpkin>;
extern template class GeminiVerifier_<curve::BN254>;
extern template class GeminiVerifier_<curve::Grumpkin>;

} // namespace proof_system::honk::pcs::gemini
Loading