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: Use oink in IVC #8161

Merged
merged 11 commits into from
Aug 26, 2024
78 changes: 59 additions & 19 deletions barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "barretenberg/aztec_ivc/aztec_ivc.hpp"
#include "barretenberg/ultra_honk/oink_prover.hpp"

namespace bb {

Expand All @@ -13,23 +14,53 @@ void AztecIVC::complete_kernel_circuit_logic(ClientCircuit& circuit)
{
circuit.databus_propagation_data.is_kernel = true;

// The folding verification queue should be either empty or contain two fold proofs
ASSERT(verification_queue.empty() || verification_queue.size() == 2);

for (auto& [proof, vkey] : verification_queue) {

// Construct stdlib accumulator, vkey and proof
auto stdlib_verifier_accum = std::make_shared<RecursiveVerifierInstance>(&circuit, verifier_accumulator);
auto stdlib_vkey = std::make_shared<RecursiveVerificationKey>(&circuit, vkey);
// Peform recursive verification and databus consistency checks for each entry in the verification queue
for (auto& [proof, vkey, type] : verification_queue) {
// Construct stdlib verification key and proof
auto stdlib_proof = bb::convert_proof_to_witness(&circuit, proof);
auto stdlib_vkey = std::make_shared<RecursiveVerificationKey>(&circuit, vkey);

// Perform folding recursive verification
FoldingRecursiveVerifier verifier{ &circuit, stdlib_verifier_accum, { stdlib_vkey } };
auto verifier_accum = verifier.verify_folding_proof(stdlib_proof);
verifier_accumulator = std::make_shared<VerifierInstance>(verifier_accum->get_value());

// Perform databus commitment consistency checks and propagate return data commitments via public inputs
bus_depot.execute(verifier.instances);
switch (type) {
case QUEUE_TYPE::PG: {
// Construct stdlib verifier accumulator from the native counterpart computed on a previous round
auto stdlib_verifier_accum = std::make_shared<RecursiveVerifierInstance>(&circuit, verifier_accumulator);

// Perform folding recursive verification to update the verifier accumulator
FoldingRecursiveVerifier verifier{ &circuit, stdlib_verifier_accum, { stdlib_vkey } };
auto verifier_accum = verifier.verify_folding_proof(stdlib_proof);

// Extract native verifier accumulator from the stdlib accum for use on the next round
verifier_accumulator = std::make_shared<VerifierInstance>(verifier_accum->get_value());

// Perform databus commitment consistency checks and propagate return data commitments via public inputs
bus_depot.execute(verifier.instances[1]->witness_commitments,
verifier.instances[1]->public_inputs,
verifier.instances[1]->verification_key->databus_propagation_data);
break;
}
case QUEUE_TYPE::OINK: {
// Construct an incomplete stdlib verifier accumulator from the corresponding stdlib verification key
auto verifier_accum = std::make_shared<RecursiveVerifierInstance>(&circuit, stdlib_vkey);

// Perform oink recursive verification to complete the initial verifier accumulator
OinkRecursiveVerifier oink{ &circuit, verifier_accum };
oink.verify_proof(stdlib_proof);
verifier_accum->is_accumulator = true; // indicate to PG that it should not run oink on this instance

// Extract native verifier accumulator from the stdlib accum for use on the next round
verifier_accumulator = std::make_shared<VerifierInstance>(verifier_accum->get_value());
// Initialize the gate challenges to zero for use in first round of folding
verifier_accumulator->gate_challenges =
std::vector<FF>(verifier_accum->verification_key->log_circuit_size, 0);

// Perform databus commitment consistency checks and propagate return data commitments via public inputs
bus_depot.execute(verifier_accum->witness_commitments,
verifier_accum->public_inputs,
verifier_accum->verification_key->databus_propagation_data);

break;
}
}
}
verification_queue.clear();

Expand Down Expand Up @@ -71,17 +102,26 @@ void AztecIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr<Verifica
// Set the instance verification key from precomputed if available, else compute it
instance_vk = precomputed_vk ? precomputed_vk : std::make_shared<VerificationKey>(prover_instance->proving_key);

// If this is the first circuit simply initialize the prover and verifier accumulator instances
// If this is the first circuit in the IVC, use oink to compute the completed instance and generate an oink proof
if (!initialized) {
fold_output.accumulator = prover_instance;
verifier_accumulator = std::make_shared<VerifierInstance>(instance_vk);
OinkProver<Flavor> oink_prover{ prover_instance };
oink_prover.prove();
prover_instance->is_accumulator = true; // indicate to PG that it should not run oink on this instance
// Initialize the gate challenges to zero for use in first round of folding
prover_instance->gate_challenges = std::vector<FF>(prover_instance->proving_key.log_circuit_size, 0);

fold_output.accumulator = prover_instance; // initialize the prover accum with the completed instance

// Add oink proof and corresponding verification key to the verification queue
verification_queue.emplace_back(oink_prover.transcript->proof_data, instance_vk, QUEUE_TYPE::OINK);

initialized = true;
} else { // Otherwise, fold the new instance into the accumulator
FoldingProver folding_prover({ fold_output.accumulator, prover_instance });
fold_output = folding_prover.prove();

// Add fold proof and corresponding verification key to the verification queue
verification_queue.emplace_back(fold_output.proof, instance_vk);
verification_queue.emplace_back(fold_output.proof, instance_vk, QUEUE_TYPE::PG);
}

// Track the maximum size of each block for all circuits porcessed (for debugging purposes only)
Expand Down
15 changes: 9 additions & 6 deletions barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ class AztecIVC {
using ECCVMVerificationKey = bb::ECCVMFlavor::VerificationKey;
using TranslatorVerificationKey = bb::TranslatorFlavor::VerificationKey;

using GURecursiveFlavor = MegaRecursiveFlavor_<bb::MegaCircuitBuilder>;
using RecursiveVerifierInstances = bb::stdlib::recursion::honk::RecursiveVerifierInstances_<GURecursiveFlavor, 2>;
using RecursiveFlavor = MegaRecursiveFlavor_<bb::MegaCircuitBuilder>;
using RecursiveVerifierInstances = bb::stdlib::recursion::honk::RecursiveVerifierInstances_<RecursiveFlavor, 2>;
using RecursiveVerifierInstance = RecursiveVerifierInstances::Instance;
using RecursiveVerificationKey = RecursiveVerifierInstances::VerificationKey;
using RecursiveVerificationKey = RecursiveFlavor::VerificationKey;
using FoldingRecursiveVerifier =
bb::stdlib::recursion::honk::ProtoGalaxyRecursiveVerifier_<RecursiveVerifierInstances>;
using OinkRecursiveVerifier = stdlib::recursion::honk::OinkRecursiveVerifier_<RecursiveFlavor>;

using DataBusDepot = stdlib::DataBusDepot<ClientCircuit>;

Expand All @@ -62,9 +63,11 @@ class AztecIVC {
MSGPACK_FIELDS(folding_proof, decider_proof, goblin_proof);
};

struct FoldingVerifierInputs {
FoldProof proof;
enum class QUEUE_TYPE { OINK, PG };
struct RecursiveVerifierInputs {
std::vector<FF> proof; // oink or PG
std::shared_ptr<VerificationKey> instance_vk;
QUEUE_TYPE type;
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like this struct and the proof type inside should be renamed now because it's not about folding in the Oink case, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, why not handle the merge steps using this queue as well?

Copy link
Contributor Author

@ledwards2225 ledwards2225 Aug 26, 2024

Choose a reason for hiding this comment

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

Thanks, updated the naming. You're right that the merge stuff could be handled in the same way. I didn't do it because there are no verification keys for the merge protocol and also because I'm hoping that the merge recursive verifier goes away altogether. If it doesn't tho, you're right that it should probably be made to conform

};

// Utility for tracking the max size of each block across the full IVC
Expand All @@ -82,7 +85,7 @@ class AztecIVC {
std::shared_ptr<VerificationKey> instance_vk; // verification key for instance to be folded

// Set of pairs of {fold_proof, verification_key} to be recursively verified
std::vector<FoldingVerifierInputs> verification_queue;
std::vector<RecursiveVerifierInputs> verification_queue;
// Set of merge proofs to be recursively verified
std::vector<MergeProof> merge_verification_queue;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,21 @@ OinkRecursiveVerifier_<Flavor>::OinkRecursiveVerifier_(Builder* builder,
, 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>::OinkRecursiveVerifier_(Builder* builder,
const std::shared_ptr<Instance>& instance,
std::string domain_separator)
: instance(instance)
, builder(builder)
, domain_separator(std::move(domain_separator))
{}

template <typename Flavor> void OinkRecursiveVerifier_<Flavor>::verify_proof(OinkProof& proof)
{
transcript = std::make_shared<Transcript>(proof);
verify();
}

template <typename Flavor> void OinkRecursiveVerifier_<Flavor>::verify()
{
using CommitmentLabels = typename Flavor::CommitmentLabels;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,46 @@ template <typename Flavor> class OinkRecursiveVerifier_ {
using RelationSeparator = typename Flavor::RelationSeparator;
using Transcript = bb::BaseTranscript<bb::stdlib::recursion::honk::StdlibTranscriptParams<Builder>>;
using WitnessCommitments = typename Flavor::WitnessCommitments;
using OinkProof = std::vector<FF>;

/**
* @brief Constructs an Oink Recursive Verifier with a transcript that has been instantiated externally.
* @details Used when oink recursive verification is part of a larger protocol for which a transcript already
* exists, e.g. Honk recursive verification.
*
* @param builder
* @param instance Incomplete verifier instance to be completed during verification
* @param transcript Transcript instantiated with an Oink proof (or a proof that contains an Oink proof).
* @param domain_separator string used for differentiating instances in the transcript (PG only)
*/
explicit OinkRecursiveVerifier_(Builder* builder,
const std::shared_ptr<Instance>& instance,
std::shared_ptr<Transcript> transcript,
std::string domain_separator = "");

/**
* @brief Constructs an Oink Recursive Verifier
*
* @param builder
* @param instance Incomplete verifier instance to be completed during verification
* @param domain_separator string used for differentiating instances in the transcript (PG only)
*/
explicit OinkRecursiveVerifier_(Builder* builder,
const std::shared_ptr<Instance>& instance,
std::string domain_separator = "");

/**
* @brief Constructs an oink recursive verifier circuit for an oink proof assumed to be contained in the transcript.
*
*/
void verify();

/**
* @brief Constructs an oink recursive verifier circuit for a provided oink proof.
*
*/
void verify_proof(OinkProof& proof);

std::shared_ptr<Instance> instance;
Builder* builder;
std::shared_ptr<Transcript> transcript;
Expand Down
Loading
Loading