-
Notifications
You must be signed in to change notification settings - Fork 244
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
601 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
barretenberg_module(aztec_ivc goblin) |
165 changes: 165 additions & 0 deletions
165
barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
105
barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.