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: oink recursive verifier #8121

Merged
merged 10 commits into from
Aug 22, 2024
16 changes: 10 additions & 6 deletions barretenberg/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
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
Loading