Skip to content

Commit

Permalink
refactor: Protogalaxy recursive verifier matches native verifier (#8568)
Browse files Browse the repository at this point in the history
Continuation of PG refactoring

- PG recursive verifier made similar to native PG verifier
- In recursive setting: accumulator is mutated in place. 
- Sharing the code with prover_verifier_shared for functions that
compute powers of round challenges and update gate challenges
- Renaming + Constifying a lot
  • Loading branch information
iakovenkos authored Sep 17, 2024
1 parent dd09f05 commit a4f61b3
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 239 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,20 +1,57 @@
#pragma once
#include "barretenberg/ecc/curves/bn254/fr.hpp"
#include <vector>

namespace bb {

/**
* @brief Compute the gate challenges used in the combiner calculation.
* @details This is Step 8 of the protocol as written in the paper.
*/
std::vector<fr> update_gate_challenges(const fr& perturbator_challenge,
const std::vector<fr>& gate_challenges,
const std::vector<fr>& init_challenges);
template <typename FF>
std::vector<FF> update_gate_challenges(const FF& perturbator_challenge,
const std::vector<FF>& gate_challenges,
const std::vector<FF>& init_challenges)
{
const size_t num_challenges = gate_challenges.size();
std::vector<FF> next_gate_challenges(num_challenges);

for (size_t idx = 0; idx < num_challenges; idx++) {
next_gate_challenges[idx] = gate_challenges[idx] + perturbator_challenge * init_challenges[idx];
}
return next_gate_challenges;
}
/**
* @brief Given δ, compute the vector [δ, δ^2,..., δ^num_powers].
* @details This is Step 2 of the protocol as written in the paper.
*/
std::vector<fr> compute_round_challenge_pows(const size_t num_powers, const fr& round_challenge);
template <typename FF> std::vector<FF> compute_round_challenge_pows(const size_t num_powers, const FF& round_challenge)
{
std::vector<FF> pows(num_powers);
pows[0] = round_challenge;
for (size_t i = 1; i < num_powers; i++) {
pows[i] = pows[i - 1].sqr();
}
return pows;
}

/**
* @brief Evaluates the perturbator at a given scalar, in a sequential manner for the recursive setting.
*
* @details This method is equivalent to the one in the Polynomial class for evaluating a polynomial, represented by
* coefficients in monomial basis, at a given point. The Polynomial class is used in the native verifier for
* constructing and computing the perturbator. We implement this separate functionality here in the recursive
* folding verifier to avoid instantiating the entire Polynomial class on stdlib::bn254. Furthermore, the evaluation
* needs to be done sequentially as we don't support a parallel_for in circuits.
*
*/
template <typename FF> static FF evaluate_perturbator(std::vector<FF> coeffs, FF point)
{
FF point_acc = FF(1);
FF result = FF(0);
for (size_t i = 0; i < coeffs.size(); i++) {
result += coeffs[i] * point_acc;
point_acc *= point;
}
return result;
};

} // namespace bb
} // namespace bb
Original file line number Diff line number Diff line change
@@ -1,137 +1,118 @@
#include "protogalaxy_recursive_verifier.hpp"
#include "barretenberg/plonk_honk_shared/library/grand_product_delta.hpp"
#include "barretenberg/stdlib/protogalaxy_verifier/recursive_decider_verification_keys.hpp"
#include "barretenberg/protogalaxy/prover_verifier_shared.hpp"
#include "barretenberg/stdlib/honk_verifier/oink_recursive_verifier.hpp"
#include "barretenberg/ultra_honk/decider_keys.hpp"

namespace bb::stdlib::recursion::honk {

template <class DeciderVerificationKeys>
void ProtogalaxyRecursiveVerifier_<DeciderVerificationKeys>::receive_and_finalise_key(
const std::shared_ptr<DeciderVK>& inst, std::string& domain_separator)
void ProtogalaxyRecursiveVerifier_<DeciderVerificationKeys>::run_oink_verifier_on_one_incomplete_key(
const std::shared_ptr<DeciderVK>& key, std::string& domain_separator)
{
domain_separator = domain_separator + "_";
OinkVerifier oink_verifier{ builder, inst, transcript, domain_separator };
OinkRecursiveVerifier_<Flavor> oink_verifier{ builder, key, transcript, domain_separator + '_' };
oink_verifier.verify();
}

// TODO(https://github.com/AztecProtocol/barretenberg/issues/795): The rounds prior to actual verifying are common
// between decider and folding verifier and could be somehow shared so we do not duplicate code so much.
template <class DeciderVerificationKeys>
void ProtogalaxyRecursiveVerifier_<DeciderVerificationKeys>::prepare_for_folding()
void ProtogalaxyRecursiveVerifier_<DeciderVerificationKeys>::run_oink_verifier_on_each_incomplete_key(
const std::vector<FF>& proof)
{
auto index = 0;
auto inst = keys_to_fold[0];
transcript = std::make_shared<Transcript>(proof);
size_t index = 0;
auto key = keys_to_fold[0];
auto domain_separator = std::to_string(index);

if (!inst->is_accumulator) {
receive_and_finalise_key(inst, domain_separator);
inst->target_sum = 0;
inst->gate_challenges = std::vector<FF>(static_cast<size_t>(inst->verification_key->log_circuit_size), 0);
if (!key->is_accumulator) {
run_oink_verifier_on_one_incomplete_key(key, domain_separator);
key->target_sum = 0;
key->gate_challenges = std::vector<FF>(static_cast<size_t>(key->verification_key->log_circuit_size), 0);
}
index++;

for (auto it = keys_to_fold.begin() + 1; it != keys_to_fold.end(); it++, index++) {
auto inst = *it;
auto key = *it;
auto domain_separator = std::to_string(index);
receive_and_finalise_key(inst, domain_separator);
run_oink_verifier_on_one_incomplete_key(key, domain_separator);
}
}

template <class DeciderVerificationKeys>
std::shared_ptr<typename DeciderVerificationKeys::DeciderVK> ProtogalaxyRecursiveVerifier_<
DeciderVerificationKeys>::verify_folding_proof(const StdlibProof<Builder>& proof)
{
using Transcript = typename Flavor::Transcript;
static constexpr size_t BATCHED_EXTENDED_LENGTH = DeciderVerificationKeys::BATCHED_EXTENDED_LENGTH;
static constexpr size_t NUM_KEYS = DeciderVerificationKeys::NUM;
static constexpr size_t COMBINER_LENGTH = BATCHED_EXTENDED_LENGTH - NUM_KEYS;

transcript = std::make_shared<Transcript>(proof);
prepare_for_folding();
run_oink_verifier_on_each_incomplete_key(proof);

auto delta = transcript->template get_challenge<FF>("delta");
auto accumulator = get_accumulator();
auto deltas =
compute_round_challenge_pows(static_cast<size_t>(accumulator->verification_key->log_circuit_size), delta);
std::shared_ptr<DeciderVK> accumulator = keys_to_fold[0];
const size_t log_circuit_size = static_cast<size_t>(accumulator->verification_key->log_circuit_size);

std::vector<FF> perturbator_coeffs(static_cast<size_t>(accumulator->verification_key->log_circuit_size) + 1, 0);
// Perturbator round
const FF delta = transcript->template get_challenge<FF>("delta");
const std::vector<FF> deltas = compute_round_challenge_pows(log_circuit_size, delta);
std::vector<FF> perturbator_coeffs(log_circuit_size + 1, 0);
if (accumulator->is_accumulator) {
for (size_t idx = 1; idx <= static_cast<size_t>(accumulator->verification_key->log_circuit_size); idx++) {
for (size_t idx = 1; idx <= log_circuit_size; idx++) {
perturbator_coeffs[idx] =
transcript->template receive_from_prover<FF>("perturbator_" + std::to_string(idx));
}
}
const FF perturbator_challenge = transcript->template get_challenge<FF>("perturbator_challenge");

// Combiner quotient round
perturbator_coeffs[0] = accumulator->target_sum;

FF perturbator_challenge = transcript->template get_challenge<FF>("perturbator_challenge");

auto perturbator_at_challenge = evaluate_perturbator(perturbator_coeffs, perturbator_challenge);
// The degree of K(X) is dk - k - 1 = k(d - 1) - 1. Hence we need k(d - 1) evaluations to represent it.
std::array<FF, DeciderVerificationKeys::BATCHED_EXTENDED_LENGTH - DeciderVerificationKeys::NUM>
combiner_quotient_evals;
for (size_t idx = 0; idx < DeciderVerificationKeys::BATCHED_EXTENDED_LENGTH - DeciderVerificationKeys::NUM; idx++) {
combiner_quotient_evals[idx] = transcript->template receive_from_prover<FF>(
"combiner_quotient_" + std::to_string(idx + DeciderVerificationKeys::NUM));
}
Univariate<FF, DeciderVerificationKeys::BATCHED_EXTENDED_LENGTH, DeciderVerificationKeys::NUM> combiner_quotient(
combiner_quotient_evals);
FF combiner_challenge = transcript->template get_challenge<FF>("combiner_quotient_challenge");
auto combiner_quotient_at_challenge = combiner_quotient.evaluate(combiner_challenge); // fine recursive i think

auto vanishing_polynomial_at_challenge = combiner_challenge * (combiner_challenge - FF(1));
auto lagranges = std::vector<FF>{ FF(1) - combiner_challenge, combiner_challenge };

auto next_accumulator = std::make_shared<DeciderVK>(builder);

next_accumulator->verification_key = std::make_shared<VerificationKey>(
accumulator->verification_key->circuit_size, accumulator->verification_key->num_public_inputs);
next_accumulator->verification_key->pcs_verification_key = accumulator->verification_key->pcs_verification_key;
next_accumulator->verification_key->pub_inputs_offset = accumulator->verification_key->pub_inputs_offset;
next_accumulator->verification_key->contains_recursive_proof =
accumulator->verification_key->contains_recursive_proof;
next_accumulator->verification_key->recursive_proof_public_input_indices =
accumulator->verification_key->recursive_proof_public_input_indices;
if constexpr (IsGoblinFlavor<Flavor>) { // Databus commitment propagation data
next_accumulator->verification_key->databus_propagation_data =
accumulator->verification_key->databus_propagation_data;
const FF perturbator_evaluation = evaluate_perturbator(perturbator_coeffs, perturbator_challenge);

std::array<FF, COMBINER_LENGTH>
combiner_quotient_evals; // The degree of the combiner quotient (K in the paper) is dk - k - 1 = k(d - 1) - 1.
// Hence we need k(d - 1) evaluations to represent it.
for (size_t idx = 0; idx < COMBINER_LENGTH; idx++) {
combiner_quotient_evals[idx] =
transcript->template receive_from_prover<FF>("combiner_quotient_" + std::to_string(idx + NUM_KEYS));
}

next_accumulator->public_inputs = accumulator->public_inputs;
// Folding
const FF combiner_challenge = transcript->template get_challenge<FF>("combiner_quotient_challenge");
const Univariate<FF, BATCHED_EXTENDED_LENGTH, NUM_KEYS> combiner_quotient(combiner_quotient_evals);
const FF combiner_quotient_at_challenge = combiner_quotient.evaluate(combiner_challenge);

next_accumulator->is_accumulator = true;
const FF vanishing_polynomial_at_challenge = combiner_challenge * (combiner_challenge - FF(1));
const std::vector<FF> lagranges = { FF(1) - combiner_challenge, combiner_challenge };

// Compute next folding parameters
next_accumulator->target_sum =
perturbator_at_challenge * lagranges[0] + vanishing_polynomial_at_challenge * combiner_quotient_at_challenge;
next_accumulator->gate_challenges =
update_gate_challenges(perturbator_challenge, accumulator->gate_challenges, deltas);

// Compute ϕ
fold_commitments(lagranges, keys_to_fold, next_accumulator);

size_t alpha_idx = 0;
for (auto& alpha : next_accumulator->alphas) {
alpha = FF(0);
size_t vk_idx = 0;
for (auto& key : keys_to_fold) {
alpha += key->alphas[alpha_idx] * lagranges[vk_idx];
vk_idx++;
}
alpha_idx++;
accumulator->is_accumulator = true;
accumulator->target_sum =
perturbator_evaluation * lagranges[0] + vanishing_polynomial_at_challenge * combiner_quotient_at_challenge;
accumulator->gate_challenges = update_gate_challenges(perturbator_challenge, accumulator->gate_challenges, deltas);

// Fold the commitments
for (auto [combination, to_combine] :
zip_view(accumulator->verification_key->get_all(), keys_to_fold.get_precomputed_commitments())) {
combination = Commitment::batch_mul(
to_combine, lagranges, /*max_num_bits=*/0, /*with_edgecases=*/IsUltraBuilder<Builder>);
}

for (auto [combination, to_combine] :
zip_view(accumulator->witness_commitments.get_all(), keys_to_fold.get_witness_commitments())) {
combination = Commitment::batch_mul(
to_combine, lagranges, /*max_num_bits=*/0, /*with_edgecases=*/IsUltraBuilder<Builder>);
}

auto& expected_parameters = next_accumulator->relation_parameters;
for (size_t inst_idx = 0; inst_idx < DeciderVerificationKeys::NUM; inst_idx++) {
auto& key = keys_to_fold[inst_idx];
expected_parameters.eta += key->relation_parameters.eta * lagranges[inst_idx];
expected_parameters.eta_two += key->relation_parameters.eta_two * lagranges[inst_idx];
expected_parameters.eta_three += key->relation_parameters.eta_three * lagranges[inst_idx];
expected_parameters.beta += key->relation_parameters.beta * lagranges[inst_idx];
expected_parameters.gamma += key->relation_parameters.gamma * lagranges[inst_idx];
expected_parameters.public_input_delta += key->relation_parameters.public_input_delta * lagranges[inst_idx];
expected_parameters.lookup_grand_product_delta +=
key->relation_parameters.lookup_grand_product_delta * lagranges[inst_idx];
// Fold the relation parameters
for (auto [combination, to_combine] : zip_view(accumulator->alphas, keys_to_fold.get_alphas())) {
combination = linear_combination(to_combine, lagranges);
}
return next_accumulator;
for (auto [combination, to_combine] :
zip_view(accumulator->relation_parameters.get_to_fold(), keys_to_fold.get_relation_parameters())) {
combination = linear_combination(to_combine, lagranges);
}

return accumulator;
}

// Instantiate the template with specific flavors and builders
template class ProtogalaxyRecursiveVerifier_<
RecursiveDeciderVerificationKeys_<UltraRecursiveFlavor_<UltraCircuitBuilder>, 2>>;
template class ProtogalaxyRecursiveVerifier_<
Expand All @@ -144,4 +125,5 @@ template class ProtogalaxyRecursiveVerifier_<
RecursiveDeciderVerificationKeys_<UltraRecursiveFlavor_<CircuitSimulatorBN254>, 2>>;
template class ProtogalaxyRecursiveVerifier_<
RecursiveDeciderVerificationKeys_<MegaRecursiveFlavor_<CircuitSimulatorBN254>, 2>>;
} // namespace bb::stdlib::recursion::honk

} // namespace bb::stdlib::recursion::honk
Loading

0 comments on commit a4f61b3

Please sign in to comment.