Skip to content

Commit

Permalink
feat: oink recursive verifier (#8121)
Browse files Browse the repository at this point in the history
Introduce an Oink recursive verifier and use it to replace the
equivalent logic in Ultra and PG recursive verifiers.

Closes #949
  • Loading branch information
ledwards2225 authored and AztecBot committed Aug 23, 2024
1 parent d359d88 commit ac1e174
Show file tree
Hide file tree
Showing 10 changed files with 212 additions and 248 deletions.
16 changes: 10 additions & 6 deletions cpp/src/barretenberg/goblin/mock_circuits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class GoblinMockCircuits {
using RecursiveVerifier = bb::stdlib::recursion::honk::UltraRecursiveVerifier_<RecursiveFlavor>;
using VerifierInstance = bb::VerifierInstance_<Flavor>;
using RecursiveVerifierInstance = ::bb::stdlib::recursion::honk::RecursiveVerifierInstance_<RecursiveFlavor>;
using RecursiveVerificationKey = RecursiveVerifierInstance::VerificationKey;
using RecursiveVerifierAccumulator = std::shared_ptr<RecursiveVerifierInstance>;
using VerificationKey = Flavor::VerificationKey;
static constexpr size_t NUM_OP_QUEUE_COLUMNS = Flavor::NUM_WIRES;
Expand Down Expand Up @@ -195,17 +196,20 @@ class GoblinMockCircuits {
const KernelInput& prev_kernel_accum)
{
// Execute recursive aggregation of function proof
RecursiveVerifier verifier1{ &builder, function_accum.verification_key };
auto verification_key = std::make_shared<RecursiveVerificationKey>(&builder, function_accum.verification_key);
auto proof = bb::convert_proof_to_witness(&builder, function_accum.proof);
RecursiveVerifier verifier1{ &builder, verification_key };
verifier1.verify_proof(
function_accum.proof,
stdlib::recursion::init_default_aggregation_state<MegaBuilder, RecursiveFlavor::Curve>(builder));
proof, stdlib::recursion::init_default_aggregation_state<MegaBuilder, RecursiveFlavor::Curve>(builder));

// Execute recursive aggregation of previous kernel proof if one exists
if (!prev_kernel_accum.proof.empty()) {
RecursiveVerifier verifier2{ &builder, prev_kernel_accum.verification_key };
auto verification_key =
std::make_shared<RecursiveVerificationKey>(&builder, prev_kernel_accum.verification_key);
auto proof = bb::convert_proof_to_witness(&builder, prev_kernel_accum.proof);
RecursiveVerifier verifier2{ &builder, verification_key };
verifier2.verify_proof(
prev_kernel_accum.proof,
stdlib::recursion::init_default_aggregation_state<MegaBuilder, RecursiveFlavor::Curve>(builder));
proof, stdlib::recursion::init_default_aggregation_state<MegaBuilder, RecursiveFlavor::Curve>(builder));
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class ClientIVCRecursionTests : public testing::Test {
using ECCVMVK = GoblinVerifier::ECCVMVerificationKey;
using TranslatorVK = GoblinVerifier::TranslatorVerificationKey;
using Proof = ClientIVC::Proof;
using Flavor = UltraRecursiveFlavor_<Builder>;
using NativeFlavor = Flavor::NativeFlavor;
using UltraRecursiveVerifier = UltraRecursiveVerifier_<Flavor>;

static void SetUpTestSuite()
{
Expand Down Expand Up @@ -120,16 +123,16 @@ TEST_F(ClientIVCRecursionTests, ClientTubeBase)

// Construct and verify a proof for the ClientIVC Recursive Verifier circuit
auto inner_instance = std::make_shared<ProverInstance_<UltraFlavor>>(*tube_builder);
auto vk = std::make_shared<typename UltraFlavor::VerificationKey>(inner_instance->proving_key);
UltraProver_<UltraFlavor> tube_prover{ inner_instance };
auto tube_proof = tube_prover.construct_proof();
UltraProver tube_prover{ inner_instance };
auto native_tube_proof = tube_prover.construct_proof();

Builder base_builder;
UltraRecursiveVerifier_<UltraRecursiveFlavor_<Builder>> base_verifier{ &base_builder, vk };
base_verifier.verify_proof(
tube_proof,
stdlib::recursion::init_default_aggregation_state<Builder, UltraRecursiveFlavor_<Builder>::Curve>(
base_builder));
auto native_vk = std::make_shared<NativeFlavor::VerificationKey>(inner_instance->proving_key);
auto vk = std::make_shared<Flavor::VerificationKey>(&base_builder, native_vk);
auto tube_proof = bb::convert_proof_to_witness(&base_builder, native_tube_proof);
UltraRecursiveVerifier base_verifier{ &base_builder, vk };
base_verifier.verify_proof(tube_proof,
stdlib::recursion::init_default_aggregation_state<Builder, Flavor::Curve>(base_builder));
info("UH Recursive Verifier: num prefinalized gates = ", base_builder.num_gates);

EXPECT_EQ(base_builder.failed(), false) << base_builder.err();
Expand Down
117 changes: 117 additions & 0 deletions cpp/src/barretenberg/stdlib/honk_verifier/oink_recursive_verifier.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include "barretenberg/stdlib/honk_verifier/oink_recursive_verifier.hpp"

#include "barretenberg/numeric/bitop/get_msb.hpp"
#include "barretenberg/plonk_honk_shared/library/grand_product_delta.hpp"
#include "barretenberg/transcript/transcript.hpp"
#include <utility>

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

template <typename Flavor>
OinkRecursiveVerifier_<Flavor>::OinkRecursiveVerifier_(Builder* builder,
const std::shared_ptr<VerificationKey>& vkey,
std::shared_ptr<Transcript> transcript,
std::string domain_separator)
: key(vkey)
, builder(builder)
, transcript(transcript)
, domain_separator(std::move(domain_separator))
{}

/**
* @brief This function constructs a recursive verifier circuit for a native Ultra Honk proof of a given flavor.
* @return Output aggregation object
*/
template <typename Flavor> OinkRecursiveVerifier_<Flavor>::Output OinkRecursiveVerifier_<Flavor>::verify()
{
using CommitmentLabels = typename Flavor::CommitmentLabels;
using RelationParams = ::bb::RelationParameters<FF>;

RelationParams relation_parameters;
WitnessCommitments commitments;
CommitmentLabels labels;

FF circuit_size = transcript->template receive_from_prover<FF>(domain_separator + "circuit_size");
transcript->template receive_from_prover<FF>(domain_separator + "public_input_size");
transcript->template receive_from_prover<FF>(domain_separator + "pub_inputs_offset");

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1032): Uncomment these once it doesn't cause issues
// with the flows
// ASSERT(static_cast<uint32_t>(circuit_size.get_value()) == key->circuit_size);
// ASSERT(static_cast<uint32_t>(public_input_size.get_value()) == key->num_public_inputs);
// ASSERT(static_cast<uint32_t>(pub_inputs_offset.get_value()) == key->pub_inputs_offset);

std::vector<FF> public_inputs;
for (size_t i = 0; i < key->num_public_inputs; ++i) {
public_inputs.emplace_back(
transcript->template receive_from_prover<FF>(domain_separator + "public_input_" + std::to_string(i)));
}

// Get commitments to first three wire polynomials
commitments.w_l = transcript->template receive_from_prover<Commitment>(domain_separator + labels.w_l);
commitments.w_r = transcript->template receive_from_prover<Commitment>(domain_separator + labels.w_r);
commitments.w_o = transcript->template receive_from_prover<Commitment>(domain_separator + labels.w_o);

// If Goblin, get commitments to ECC op wire polynomials and DataBus columns
if constexpr (IsGoblinFlavor<Flavor>) {
// Receive ECC op wire commitments
for (auto [commitment, label] : zip_view(commitments.get_ecc_op_wires(), labels.get_ecc_op_wires())) {
commitment = transcript->template receive_from_prover<Commitment>(domain_separator + label);
}

// Receive DataBus related polynomial commitments
for (auto [commitment, label] : zip_view(commitments.get_databus_entities(), labels.get_databus_entities())) {
commitment = transcript->template receive_from_prover<Commitment>(domain_separator + label);
}
}

// Get eta challenges; used in RAM/ROM memory records and log derivative lookup argument
auto [eta, eta_two, eta_three] = transcript->template get_challenges<FF>(
domain_separator + "eta", domain_separator + "eta_two", domain_separator + "eta_three");

// Get commitments to lookup argument polynomials and fourth wire
commitments.lookup_read_counts =
transcript->template receive_from_prover<Commitment>(domain_separator + labels.lookup_read_counts);
commitments.lookup_read_tags =
transcript->template receive_from_prover<Commitment>(domain_separator + labels.lookup_read_tags);
commitments.w_4 = transcript->template receive_from_prover<Commitment>(domain_separator + labels.w_4);

// Get permutation challenges
auto [beta, gamma] = transcript->template get_challenges<FF>(domain_separator + "beta", domain_separator + "gamma");

commitments.lookup_inverses =
transcript->template receive_from_prover<Commitment>(domain_separator + labels.lookup_inverses);

// If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomials
if constexpr (IsGoblinFlavor<Flavor>) {
for (auto [commitment, label] : zip_view(commitments.get_databus_inverses(), labels.get_databus_inverses())) {
commitment = transcript->template receive_from_prover<Commitment>(domain_separator + label);
}
}

const FF public_input_delta = compute_public_input_delta<Flavor>(
public_inputs, beta, gamma, circuit_size, static_cast<uint32_t>(key->pub_inputs_offset));

relation_parameters = RelationParameters<FF>{ eta, eta_two, eta_three, beta, gamma, public_input_delta };

// Get commitment to permutation and lookup grand products
commitments.z_perm = transcript->template receive_from_prover<Commitment>(domain_separator + labels.z_perm);

RelationSeparator alphas;
for (size_t idx = 0; idx < alphas.size(); idx++) {
alphas[idx] = transcript->template get_challenge<FF>(domain_separator + "alpha_" + std::to_string(idx));
}

return { .relation_parameters = relation_parameters,
.commitments = std::move(commitments),
.public_inputs = public_inputs,
.alphas = alphas };
}

template class OinkRecursiveVerifier_<bb::UltraRecursiveFlavor_<UltraCircuitBuilder>>;
template class OinkRecursiveVerifier_<bb::UltraRecursiveFlavor_<MegaCircuitBuilder>>;
template class OinkRecursiveVerifier_<bb::MegaRecursiveFlavor_<UltraCircuitBuilder>>;
template class OinkRecursiveVerifier_<bb::MegaRecursiveFlavor_<MegaCircuitBuilder>>;
template class OinkRecursiveVerifier_<bb::UltraRecursiveFlavor_<CircuitSimulatorBN254>>;
template class OinkRecursiveVerifier_<bb::MegaRecursiveFlavor_<CircuitSimulatorBN254>>;
} // namespace bb::stdlib::recursion::honk
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once
#include "barretenberg/stdlib/transcript/transcript.hpp"
#include "barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp"
#include "barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp"

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

template <typename Flavor> class OinkRecursiveVerifier_ {
public:
using FF = typename Flavor::FF;
using Commitment = typename Flavor::Commitment;
using GroupElement = typename Flavor::GroupElement;
using VerificationKey = typename Flavor::VerificationKey;
using Builder = typename Flavor::CircuitBuilder;
using RelationSeparator = typename Flavor::RelationSeparator;
using Transcript = bb::BaseTranscript<bb::stdlib::recursion::honk::StdlibTranscriptParams<Builder>>;
using WitnessCommitments = typename Flavor::WitnessCommitments;

struct Output {
bb::RelationParameters<typename Flavor::FF> relation_parameters;
WitnessCommitments commitments;
std::vector<typename Flavor::FF> public_inputs;
typename Flavor::RelationSeparator alphas;
};

explicit OinkRecursiveVerifier_(Builder* builder,
const std::shared_ptr<VerificationKey>& vkey,
std::shared_ptr<Transcript> transcript,
std::string domain_separator = "");

Output verify();

std::shared_ptr<VerificationKey> key;
Builder* builder;
std::shared_ptr<Transcript> transcript;
std::string domain_separator; // used in PG to distinguish between instances in transcript
};

} // namespace bb::stdlib::recursion::honk
Original file line number Diff line number Diff line change
Expand Up @@ -44,30 +44,19 @@ UltraRecursiveVerifier_<Flavor>::AggregationObject UltraRecursiveVerifier_<Flavo
using Curve = typename Flavor::Curve;
using ZeroMorph = ::bb::ZeroMorphVerifier_<Curve>;
using VerifierCommitments = typename Flavor::VerifierCommitments;
using CommitmentLabels = typename Flavor::CommitmentLabels;
using RelationParams = ::bb::RelationParameters<FF>;
using Transcript = typename Flavor::Transcript;

transcript = std::make_shared<Transcript>(proof);
OinkVerifier oink_verifier{ builder, key, transcript };
auto [relation_parameters, witness_commitments, public_inputs, alphas] = oink_verifier.verify();

RelationParams relation_parameters;
VerifierCommitments commitments{ key };
CommitmentLabels commitment_labels;
VerifierCommitments commitments{ key, witness_commitments };

FF circuit_size = transcript->template receive_from_prover<FF>("circuit_size");
transcript->template receive_from_prover<FF>("public_input_size");
transcript->template receive_from_prover<FF>("pub_inputs_offset");

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1032): Uncomment these once it doesn't cause issues
// with the flows
// ASSERT(static_cast<uint32_t>(circuit_size.get_value()) == key->circuit_size);
// ASSERT(static_cast<uint32_t>(public_input_size.get_value()) == key->num_public_inputs);
// ASSERT(static_cast<uint32_t>(pub_inputs_offset.get_value()) == key->pub_inputs_offset);

std::vector<FF> public_inputs;
for (size_t i = 0; i < key->num_public_inputs; ++i) {
public_inputs.emplace_back(transcript->template receive_from_prover<FF>("public_input_" + std::to_string(i)));
auto gate_challenges = std::vector<FF>(CONST_PROOF_SIZE_LOG_N);
for (size_t idx = 0; idx < CONST_PROOF_SIZE_LOG_N; idx++) {
gate_challenges[idx] = transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
}

// Parse out the aggregation object using the key->recursive_proof_public_inputs_indices
aggregation_state<typename Flavor::Curve> nested_agg_obj;
size_t idx = 0;
Expand All @@ -93,82 +82,16 @@ UltraRecursiveVerifier_<Flavor>::AggregationObject UltraRecursiveVerifier_<Flavo
Curve::ScalarField::from_witness_index(builder, builder->add_variable(42));
agg_obj.aggregate(nested_agg_obj, recursion_separator);

// Get commitments to first three wire polynomials
commitments.w_l = transcript->template receive_from_prover<Commitment>(commitment_labels.w_l);
commitments.w_r = transcript->template receive_from_prover<Commitment>(commitment_labels.w_r);
commitments.w_o = transcript->template receive_from_prover<Commitment>(commitment_labels.w_o);

// If Goblin, get commitments to ECC op wire polynomials and DataBus columns
if constexpr (IsGoblinFlavor<Flavor>) {
// Receive ECC op wire commitments
for (auto [commitment, label] :
zip_view(commitments.get_ecc_op_wires(), commitment_labels.get_ecc_op_wires())) {
commitment = transcript->template receive_from_prover<Commitment>(label);
}

// Receive DataBus related polynomial commitments
for (auto [commitment, label] :
zip_view(commitments.get_databus_entities(), commitment_labels.get_databus_entities())) {
commitment = transcript->template receive_from_prover<Commitment>(label);
}
}

// Get eta challenges; used in RAM/ROM memory records and log derivative lookup argument
auto [eta, eta_two, eta_three] = transcript->template get_challenges<FF>("eta", "eta_two", "eta_three");
relation_parameters.eta = eta;
relation_parameters.eta_two = eta_two;
relation_parameters.eta_three = eta_three;

// Get commitments to lookup argument polynomials and fourth wire
commitments.lookup_read_counts =
transcript->template receive_from_prover<Commitment>(commitment_labels.lookup_read_counts);
commitments.lookup_read_tags =
transcript->template receive_from_prover<Commitment>(commitment_labels.lookup_read_tags);
commitments.w_4 = transcript->template receive_from_prover<Commitment>(commitment_labels.w_4);

// Get permutation challenges
auto [beta, gamma] = transcript->template get_challenges<FF>("beta", "gamma");

commitments.lookup_inverses =
transcript->template receive_from_prover<Commitment>(commitment_labels.lookup_inverses);

// If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomials
if constexpr (IsGoblinFlavor<Flavor>) {
for (auto [commitment, label] :
zip_view(commitments.get_databus_inverses(), commitment_labels.get_databus_inverses())) {
commitment = transcript->template receive_from_prover<Commitment>(label);
}
}

const FF public_input_delta = compute_public_input_delta<Flavor>(
public_inputs, beta, gamma, circuit_size, static_cast<uint32_t>(key->pub_inputs_offset));

relation_parameters.beta = beta;
relation_parameters.gamma = gamma;
relation_parameters.public_input_delta = public_input_delta;

// Get commitment to permutation and lookup grand products
commitments.z_perm = transcript->template receive_from_prover<Commitment>(commitment_labels.z_perm);

// Execute Sumcheck Verifier and extract multivariate opening point u = (u_0, ..., u_{d-1}) and purported
// multivariate evaluations at u
const size_t log_circuit_size = numeric::get_msb(static_cast<uint32_t>(key->circuit_size));
auto sumcheck = Sumcheck(log_circuit_size, transcript);

RelationSeparator alpha;
for (size_t idx = 0; idx < alpha.size(); idx++) {
alpha[idx] = transcript->template get_challenge<FF>("alpha_" + std::to_string(idx));
}

auto gate_challenges = std::vector<FF>(CONST_PROOF_SIZE_LOG_N);
for (size_t idx = 0; idx < CONST_PROOF_SIZE_LOG_N; idx++) {
gate_challenges[idx] = transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
}
auto [multivariate_challenge, claimed_evaluations, sumcheck_verified] =
sumcheck.verify(relation_parameters, alpha, gate_challenges);
sumcheck.verify(relation_parameters, alphas, gate_challenges);

// Execute ZeroMorph to produce an opening claim subsequently verified by a univariate PCS
auto opening_claim = ZeroMorph::verify(circuit_size,
auto opening_claim = ZeroMorph::verify(key->circuit_size,
commitments.get_unshifted(),
commitments.get_to_be_shifted(),
claimed_evaluations.get_unshifted(),
Expand All @@ -180,8 +103,7 @@ UltraRecursiveVerifier_<Flavor>::AggregationObject UltraRecursiveVerifier_<Flavo

pairing_points[0] = pairing_points[0].normalize();
pairing_points[1] = pairing_points[1].normalize();
// TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate recursion separator challenge
// properly.
// TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate recursion separator challenge properly.
agg_obj.aggregate(pairing_points, recursion_separator);
return agg_obj;
}
Expand Down
Loading

0 comments on commit ac1e174

Please sign in to comment.