Skip to content

Commit

Permalink
Merge 13b0852 into 55a39ac
Browse files Browse the repository at this point in the history
  • Loading branch information
ledwards2225 authored Aug 1, 2024
2 parents 55a39ac + 13b0852 commit 3c97920
Show file tree
Hide file tree
Showing 9 changed files with 601 additions and 48 deletions.
1 change: 1 addition & 0 deletions barretenberg/cpp/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ else()
message(STATUS "Using optimized assembly for field arithmetic.")
endif()

add_subdirectory(barretenberg/aztec_ivc)
add_subdirectory(barretenberg/bb)
add_subdirectory(barretenberg/circuit_checker)
add_subdirectory(barretenberg/client_ivc)
Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/src/barretenberg/aztec_ivc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
barretenberg_module(aztec_ivc goblin)
165 changes: 165 additions & 0 deletions barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#include "barretenberg/aztec_ivc/aztec_ivc.hpp"

namespace bb {

/**
* @brief Accumulate a circuit into the IVC scheme
* @details If this is the first circuit being accumulated, initialize the prover and verifier accumulators. Otherwise,
* fold the instance for the provided circuit into the accumulator. When two fold proofs have been enqueued, two
* recursive folding verifications are appended to the next circuit that is accumulated, which must be a kernel.
* Similarly, if a merge proof exists, a recursive merge verifier is appended.
*
* @param circuit Circuit to be accumulated/folded
* @param precomputed_vk Optional precomputed VK (otherwise will be computed herein)
*/
void AztecIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr<VerificationKey>& precomputed_vk)
{
circuit_count++; // increment the count of circuits processed into the IVC

// When there are two fold proofs present, append two recursive verifiers to the kernel
if (verification_queue.size() == 2) {
BB_OP_COUNT_TIME_NAME("construct_circuits");
ASSERT(circuit_count % 2 == 0); // ensure this is a kernel

for (auto& [proof, vkey] : verification_queue) {
FoldingRecursiveVerifier verifier{ &circuit, { verifier_accumulator, { vkey } } };
auto verifier_accum = verifier.verify_folding_proof(proof);
verifier_accumulator = std::make_shared<VerifierInstance>(verifier_accum->get_value());
info("Num gates = ", circuit.get_num_gates());
}
verification_queue.clear();
}

// Construct a merge proof (and add a recursive merge verifier to the circuit if a previous merge proof exists)
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1063): update recursive merge verification to only
// occur in kernels, similar to folding recursive verification.
goblin.merge(circuit);

// Construct the prover instance for circuit
auto prover_instance = std::make_shared<ProverInstance>(circuit, trace_structure);

// Set the instance verification key from precomputed if available, else compute it
if (precomputed_vk) {
instance_vk = precomputed_vk;
} else {
instance_vk = std::make_shared<VerificationKey>(prover_instance->proving_key);
}

// If this is the first circuit simply initialize the prover and verifier accumulator instances
if (circuit_count == 1) {
fold_output.accumulator = prover_instance;
verifier_accumulator = std::make_shared<VerifierInstance>(instance_vk);
} else { // Otherwise, fold the new instance into the accumulator
FoldingProver folding_prover({ fold_output.accumulator, prover_instance });
fold_output = folding_prover.fold_instances();

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

// Track the maximum size of each block for all circuits porcessed (for debugging purposes only)
max_block_size_tracker.update(circuit);
}

/**
* @brief Construct a proof for the IVC, which, if verified, fully establishes its correctness
*
* @return Proof
*/
AztecIVC::Proof AztecIVC::prove()
{
max_block_size_tracker.print(); // print minimum structured sizes for each block
ASSERT(verification_queue.size() == 1); // ensure only a single fold proof remains in the queue
auto& fold_proof = verification_queue[0].proof;
return { fold_proof, decider_prove(), goblin.prove() };
};

bool AztecIVC::verify(const Proof& proof,
const std::shared_ptr<VerifierInstance>& accumulator,
const std::shared_ptr<VerifierInstance>& final_verifier_instance,
const std::shared_ptr<AztecIVC::ECCVMVerificationKey>& eccvm_vk,
const std::shared_ptr<AztecIVC::TranslatorVerificationKey>& translator_vk)
{
// Goblin verification (merge, eccvm, translator)
GoblinVerifier goblin_verifier{ eccvm_vk, translator_vk };
bool goblin_verified = goblin_verifier.verify(proof.goblin_proof);

// Decider verification
AztecIVC::FoldingVerifier folding_verifier({ accumulator, final_verifier_instance });
auto verifier_accumulator = folding_verifier.verify_folding_proof(proof.folding_proof);

AztecIVC::DeciderVerifier decider_verifier(verifier_accumulator);
bool decision = decider_verifier.verify_proof(proof.decider_proof);
return goblin_verified && decision;
}

/**
* @brief Verify a full proof of the IVC
*
* @param proof
* @return bool
*/
bool AztecIVC::verify(Proof& proof, const std::vector<std::shared_ptr<VerifierInstance>>& verifier_instances)
{
auto eccvm_vk = std::make_shared<ECCVMVerificationKey>(goblin.get_eccvm_proving_key());
auto translator_vk = std::make_shared<TranslatorVerificationKey>(goblin.get_translator_proving_key());
return verify(proof, verifier_instances[0], verifier_instances[1], eccvm_vk, translator_vk);
}

/**
* @brief Internal method for constructing a decider proof
*
* @return HonkProof
*/
HonkProof AztecIVC::decider_prove() const
{
MegaDeciderProver decider_prover(fold_output.accumulator);
return decider_prover.construct_proof();
}

/**
* @brief Given a set of circuits, compute the verification keys that will be required by the IVC scheme
* @details The verification keys computed here are in general not the same as the verification keys for the
* raw input circuits because recursive verifier circuits (merge and/or folding) may be appended to the incoming
* circuits as part accumulation.
* @note This method exists for convenience and is not not meant to be used in practice for IVC. Given a set of
* circuits, it could be run once and for all to compute then save the required VKs. It also provides a convenient
* (albeit innefficient) way of separating out the cost of computing VKs from a benchmark.
*
* @param circuits A copy of the circuits to be accumulated (passing by reference would alter the original circuits)
* @return std::vector<std::shared_ptr<AztecIVC::VerificationKey>>
*/
std::vector<std::shared_ptr<AztecIVC::VerificationKey>> AztecIVC::precompute_folding_verification_keys(
std::vector<ClientCircuit> circuits)
{
std::vector<std::shared_ptr<VerificationKey>> vkeys;

for (auto& circuit : circuits) {
accumulate(circuit);
vkeys.emplace_back(instance_vk);
}

// Reset the scheme so it can be reused for actual accumulation, maintaining the trace structure setting as is
TraceStructure structure = trace_structure;
*this = AztecIVC();
this->trace_structure = structure;

return vkeys;
}

/**
* @brief Construct and verify a proof for the IVC
* @note Use of this method only makes sense when the prover and verifier are the same entity, e.g. in
* development/testing.
*
*/
bool AztecIVC::prove_and_verify()
{
auto proof = prove();

ASSERT(verification_queue.size() == 1); // ensure only a single fold proof remains in the queue
auto verifier_inst = std::make_shared<VerifierInstance>(this->verification_queue[0].instance_vk);
return verify(proof, { this->verifier_accumulator, verifier_inst });
}

} // namespace bb
105 changes: 105 additions & 0 deletions barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#pragma once

#include "barretenberg/goblin/goblin.hpp"
#include "barretenberg/goblin/mock_circuits.hpp"
#include "barretenberg/plonk_honk_shared/arithmetization/max_block_size_tracker.hpp"
#include "barretenberg/protogalaxy/decider_verifier.hpp"
#include "barretenberg/protogalaxy/protogalaxy_prover.hpp"
#include "barretenberg/protogalaxy/protogalaxy_verifier.hpp"
#include "barretenberg/sumcheck/instance/instances.hpp"
#include "barretenberg/ultra_honk/decider_prover.hpp"
#include <algorithm>

namespace bb {

/**
* @brief The IVC scheme used by the aztec client for private function execution
* @details Combines Protogalaxy with Goblin to accumulate one circuit instance at a time with efficient EC group
* operations. It is assumed that the circuits being accumulated correspond alternatingly to an app and a kernel, as is
* the case in Aztec. Two recursive folding verifiers are appended to each kernel (except the first one) to verify the
* folding of a previous kernel and an app/function circuit. Due to this structure it is enforced that the total number
* of circuits being accumulated is even.
*
*/
class AztecIVC {

public:
using Flavor = MegaFlavor;
using VerificationKey = Flavor::VerificationKey;
using FF = Flavor::FF;
using FoldProof = std::vector<FF>;
using ProverInstance = ProverInstance_<Flavor>;
using VerifierInstance = VerifierInstance_<Flavor>;
using ClientCircuit = MegaCircuitBuilder; // can only be Mega
using DeciderProver = DeciderProver_<Flavor>;
using DeciderVerifier = DeciderVerifier_<Flavor>;
using ProverInstances = ProverInstances_<Flavor>;
using FoldingProver = ProtoGalaxyProver_<ProverInstances>;
using VerifierInstances = VerifierInstances_<Flavor>;
using FoldingVerifier = ProtoGalaxyVerifier_<VerifierInstances>;
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 FoldingRecursiveVerifier =
bb::stdlib::recursion::honk::ProtoGalaxyRecursiveVerifier_<RecursiveVerifierInstances>;

// A full proof for the IVC scheme
struct Proof {
FoldProof folding_proof; // final fold proof
HonkProof decider_proof;
GoblinProof goblin_proof;

size_t size() const { return folding_proof.size() + decider_proof.size() + goblin_proof.size(); }

MSGPACK_FIELDS(folding_proof, decider_proof, goblin_proof);
};

struct FoldingVerifierInputs {
FoldProof proof;
std::shared_ptr<VerificationKey> instance_vk;
};

// Utility for tracking the max size of each block across the full IVC
MaxBlockSizeTracker max_block_size_tracker;

private:
using ProverFoldOutput = FoldingResult<Flavor>;

public:
GoblinProver goblin;

ProverFoldOutput fold_output; // prover accumulator instance and fold proof

std::shared_ptr<VerifierInstance> verifier_accumulator; // verifier accumulator instance
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;

// A flag indicating whether or not to construct a structured trace in the ProverInstance
TraceStructure trace_structure = TraceStructure::NONE;

// The number of circuits processed into the IVC
size_t circuit_count = 0;

void accumulate(ClientCircuit& circuit, const std::shared_ptr<VerificationKey>& precomputed_vk = nullptr);

Proof prove();

static bool verify(const Proof& proof,
const std::shared_ptr<VerifierInstance>& accumulator,
const std::shared_ptr<VerifierInstance>& final_verifier_instance,
const std::shared_ptr<AztecIVC::ECCVMVerificationKey>& eccvm_vk,
const std::shared_ptr<AztecIVC::TranslatorVerificationKey>& translator_vk);

bool verify(Proof& proof, const std::vector<std::shared_ptr<VerifierInstance>>& verifier_instances);

bool prove_and_verify();

HonkProof decider_prove() const;

std::vector<std::shared_ptr<VerificationKey>> precompute_folding_verification_keys(std::vector<ClientCircuit>);
};
} // namespace bb
Loading

0 comments on commit 3c97920

Please sign in to comment.