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: update AztecIvc interface to facilitate acir-ivc #8230

Merged
merged 23 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1302137
breaking out methods to be more conducive for acir interface
ledwards2225 Aug 26, 2024
861fc3b
WiP notes on test structure
ledwards2225 Aug 28, 2024
8ba79e4
initial sketch of the test; kernel 0 and oink only
ledwards2225 Sep 3, 2024
e011252
two kernels in test, seems to be working as expected
ledwards2225 Sep 3, 2024
6def609
add pathway for instantiating stdlib verifier inputs
ledwards2225 Sep 4, 2024
4327e23
WiP trying to fix build
ledwards2225 Sep 4, 2024
32c1a54
Merge branch 'master' into lde/interface_updates
ledwards2225 Sep 4, 2024
2d951e9
fix linker
ledwards2225 Sep 4, 2024
6a4f3f4
fix wasm build maybe
ledwards2225 Sep 4, 2024
d1d5c9d
simplify; single and double kernel tests pass but not when run in seq…
ledwards2225 Sep 4, 2024
b979961
reorg an comment the test suite
ledwards2225 Sep 4, 2024
f884582
add assert equal on proofs and todo with issue for vkey
ledwards2225 Sep 4, 2024
20b43a0
comments and cleanup
ledwards2225 Sep 4, 2024
bdf6b3b
reconstruct complete proof before asserting equality
ledwards2225 Sep 4, 2024
5640052
added business logic constraint; WiP on failure test
ledwards2225 Sep 5, 2024
6dd7312
complete failure test in place
ledwards2225 Sep 5, 2024
217cabd
Merge branch 'master' into lde/interface_updates
ledwards2225 Sep 5, 2024
ad0055d
missed deletions
ledwards2225 Sep 5, 2024
213f436
cleanup and minor reorg
ledwards2225 Sep 5, 2024
4189f50
comment
ledwards2225 Sep 5, 2024
45e6ac5
Merge branch 'master' into lde/interface_updates
ledwards2225 Sep 5, 2024
bfc36b0
constify some stuff
ledwards2225 Sep 5, 2024
bb71b75
more consts and reorg inputs for clarity
ledwards2225 Sep 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion barretenberg/cpp/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ include(GNUInstallDirs)
message(STATUS "Compiling all-in-one barretenberg archive")

set(BARRETENBERG_TARGET_OBJECTS
$<TARGET_OBJECTS:client_ivc_objects>
$<TARGET_OBJECTS:aztec_ivc_objects>
ledwards2225 marked this conversation as resolved.
Show resolved Hide resolved
$<TARGET_OBJECTS:commitment_schemes_objects>
$<TARGET_OBJECTS:common_objects>
$<TARGET_OBJECTS:client_ivc_objects>
Expand Down
157 changes: 103 additions & 54 deletions barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,74 +4,124 @@
namespace bb {

/**
* @brief Append logic to complete a kernel circuit
* @details A kernel circuit may contain some combination of PG recursive verification, merge recursive verification,
* and databus commitment consistency checks. This method appends this logic to a provided kernel circuit.
* @brief Instantiate a stdlib verification queue corresponding to the native counterpart
*
* @param circuit
*/
void AztecIVC::complete_kernel_circuit_logic(ClientCircuit& circuit)
void AztecIVC::instantiate_stdlib_verification_queue(ClientCircuit& circuit)
{
circuit.databus_propagation_data.is_kernel = true;

// Perform 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);

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<RecursiveDeciderVerificationKey>(&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<DeciderVerificationKey>(verifier_accum->get_value());

// Perform databus commitment consistency checks and propagate return data commitments via public inputs
bus_depot.execute(verifier.keys_to_fold[1]->witness_commitments,
verifier.keys_to_fold[1]->public_inputs,
verifier.keys_to_fold[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<RecursiveDeciderVerificationKey>(&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 key

// Extract native verifier accumulator from the stdlib accum for use on the next round
verifier_accumulator = std::make_shared<DeciderVerificationKey>(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;
}
}
stdlib_verification_queue.emplace_back(stdlib_proof, stdlib_vkey, type);
}
verification_queue.clear(); // the native data is not needed beyond this point
}

/**
* @brief Populate the provided circuit with constraints for (1) recursive verification of the provided accumulation
* proof and (2) the associated databus commitment consistency checks.
* @details The recursive verifier will be either Oink or Protogalaxy depending on the specified proof type. In either
* case, the verifier accumulator is updated in place via the verification algorithm. Databus commitment consistency
* checks are performed on the witness commitments and public inputs extracted from the proof by the verifier.
*
* @param circuit The circuit to which the constraints are appended
* @param proof A stdlib proof to be recursively verified (either oink or PG)
* @param vkey The stdlib verfication key associated with the proof
* @param type The type of the proof (equivalently, the type of the verifier)
*/
void AztecIVC::perform_recursive_verification_and_databus_consistency_checks(
ledwards2225 marked this conversation as resolved.
Show resolved Hide resolved
ClientCircuit& circuit,
const StdlibProof<ClientCircuit>& proof,
const std::shared_ptr<RecursiveVerificationKey>& vkey,
const QUEUE_TYPE type)
{
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<RecursiveDeciderVerificationKey>(&circuit, verifier_accumulator);

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

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

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

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

// Extract native verifier accumulator from the stdlib accum for use on the next round
verifier_accumulator = std::make_shared<DeciderVerificationKey>(verifier_accum->get_value());
// Initialize the gate challenges to zero for use in first round of folding
auto log_circuit_size = static_cast<size_t>(verifier_accum->verification_key->log_circuit_size);
verifier_accumulator->gate_challenges = std::vector<FF>(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;
}
}
}

/**
* @brief Perform recursive merge verification for each merge proof in the queue
*
* @param circuit
*/
void AztecIVC::process_recursive_merge_verification_queue(ClientCircuit& circuit)
{
// Recusively verify all merge proofs in queue
for (auto& proof : merge_verification_queue) {
goblin.verify_merge(circuit, proof);
}
merge_verification_queue.clear();
}

/**
* @brief Append logic to complete a kernel circuit
* @details A kernel circuit may contain some combination of PG recursive verification, merge recursive
* verification, and databus commitment consistency checks. This method appends this logic to a provided kernel
* circuit.
*
* @param circuit
*/
void AztecIVC::complete_kernel_circuit_logic(ClientCircuit& circuit)
ledwards2225 marked this conversation as resolved.
Show resolved Hide resolved
{
circuit.databus_propagation_data.is_kernel = true;

// Instantiate stdlib verifier inputs from their native counterparts
if (stdlib_verification_queue.empty()) {
instantiate_stdlib_verification_queue(circuit);
}

// Peform recursive verification and databus consistency checks for each entry in the verification queue
for (auto& [proof, vkey, type] : stdlib_verification_queue) {
perform_recursive_verification_and_databus_consistency_checks(circuit, proof, vkey, type);
}
stdlib_verification_queue.clear();

// Perform recursive merge verification for every merge proof in the queue
process_recursive_merge_verification_queue(circuit);
}

/**
* @brief Execute prover work for accumulation
* @details Construct an proving key for the provided circuit. If this is the first step in the IVC, simply initialize
Expand Down Expand Up @@ -115,16 +165,15 @@ void AztecIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr<Verifica

// Add oink proof and corresponding verification key to the verification queue
verification_queue.push_back(
bb::AztecIVC::RecursiveVerifierInputs{ oink_prover.transcript->proof_data, honk_vk, QUEUE_TYPE::OINK });
bb::AztecIVC::VerifierInputs{ oink_prover.transcript->proof_data, honk_vk, QUEUE_TYPE::OINK });

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

// Add fold proof and corresponding verification key to the verification queue
verification_queue.push_back(
bb::AztecIVC::RecursiveVerifierInputs{ fold_output.proof, honk_vk, QUEUE_TYPE::PG });
verification_queue.push_back(bb::AztecIVC::VerifierInputs{ fold_output.proof, honk_vk, QUEUE_TYPE::PG });
}

// Track the maximum size of each block for all circuits porcessed (for debugging purposes only)
Expand Down Expand Up @@ -171,7 +220,7 @@ bool AztecIVC::verify(const Proof& proof,
* @param proof
* @return bool
*/
bool AztecIVC::verify(Proof& proof, const std::vector<std::shared_ptr<DeciderVerificationKey>>& vk_stack)
bool AztecIVC::verify(const Proof& proof, const std::vector<std::shared_ptr<DeciderVerificationKey>>& vk_stack)
{
auto eccvm_vk = std::make_shared<ECCVMVerificationKey>(goblin.get_eccvm_proving_key());
auto translator_vk = std::make_shared<TranslatorVerificationKey>(goblin.get_translator_proving_key());
Expand Down
33 changes: 28 additions & 5 deletions barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,23 @@ class AztecIVC {
MSGPACK_FIELDS(folding_proof, decider_proof, goblin_proof);
};

enum class QUEUE_TYPE { OINK, PG };
struct RecursiveVerifierInputs {
enum class QUEUE_TYPE { OINK, PG }; // for specifying type of proof in the verification queue

// An entry in the native verification queue
struct VerifierInputs {
std::vector<FF> proof; // oink or PG
std::shared_ptr<VerificationKey> honk_verification_key;
QUEUE_TYPE type;
};
using VerificationQueue = std::vector<VerifierInputs>;

// An entry in the stdlib verification queue
struct StdlibVerifierInputs {
StdlibProof<ClientCircuit> proof; // oink or PG
std::shared_ptr<RecursiveVerificationKey> honk_verification_key;
QUEUE_TYPE type;
};
using StdlibVerificationQueue = std::vector<StdlibVerifierInputs>;

// Utility for tracking the max size of each block across the full IVC
MaxBlockSizeTracker max_block_size_tracker;
Expand All @@ -85,8 +96,10 @@ class AztecIVC {
std::shared_ptr<DeciderVerificationKey> verifier_accumulator; // verifier accumulator
std::shared_ptr<VerificationKey> honk_vk; // honk vk to be completed and folded into the accumulator

// Set of pairs of {fold_proof, verification_key} to be recursively verified
std::vector<RecursiveVerifierInputs> verification_queue;
// Set of tuples {proof, verification_key, type} to be recursively verified
VerificationQueue verification_queue;
// Set of tuples {stdlib_proof, stdlib_verification_key, type} corresponding to the native verification queue
StdlibVerificationQueue stdlib_verification_queue;
// Set of merge proofs to be recursively verified
std::vector<MergeProof> merge_verification_queue;

Expand All @@ -98,6 +111,16 @@ class AztecIVC {

bool initialized = false; // Is the IVC accumulator initialized

void instantiate_stdlib_verification_queue(ClientCircuit& circuit);

void perform_recursive_verification_and_databus_consistency_checks(
ClientCircuit& circuit,
const StdlibProof<ClientCircuit>& proof,
const std::shared_ptr<RecursiveVerificationKey>& vkey,
const QUEUE_TYPE type);

void process_recursive_merge_verification_queue(ClientCircuit& circuit);

// Complete the logic of a kernel circuit (e.g. PG/merge recursive verification, databus consistency checks)
void complete_kernel_circuit_logic(ClientCircuit& circuit);

Expand All @@ -112,7 +135,7 @@ class AztecIVC {
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<DeciderVerificationKey>>& vk_stack);
bool verify(const Proof& proof, const std::vector<std::shared_ptr<DeciderVerificationKey>>& vk_stack);

bool prove_and_verify();

Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# We do not need to bloat barretenberg.wasm with gzip functionality in a browser context as the browser can do this

set(DSL_DEPENDENCIES
aztec_ivc
plonk
ultra_honk
client_ivc
Expand Down
64 changes: 60 additions & 4 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,8 @@ void process_honk_recursion_constraints(Builder& builder,
*/
template <>
UltraCircuitBuilder create_circuit(AcirFormat& constraint_system,
size_t size_hint,
WitnessVector const& witness,
const size_t size_hint,
const WitnessVector& witness,
bool honk_recursion,
[[maybe_unused]] std::shared_ptr<ECCOpQueue>,
bool collect_gates_per_opcode)
Expand All @@ -411,8 +411,8 @@ UltraCircuitBuilder create_circuit(AcirFormat& constraint_system,
*/
template <>
MegaCircuitBuilder create_circuit(AcirFormat& constraint_system,
[[maybe_unused]] size_t size_hint,
WitnessVector const& witness,
[[maybe_unused]] const size_t size_hint,
const WitnessVector& witness,
bool honk_recursion,
std::shared_ptr<ECCOpQueue> op_queue,
bool collect_gates_per_opcode)
Expand All @@ -428,6 +428,62 @@ MegaCircuitBuilder create_circuit(AcirFormat& constraint_system,
return builder;
};

/**
* @brief Create a kernel circuit from a constraint system and an IVC instance
*
* @param constraint_system AcirFormat constraint system possibly containing IVC recursion constraints
* @param ivc An IVC instance containing internal data about proofs to be verified
* @param size_hint
* @param witness
* @return MegaCircuitBuilder
*/
MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system,
Copy link
Contributor

Choose a reason for hiding this comment

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

Everything but IVC is const? (also in header)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

constified everything but AcirFormat which unfortunately gets altered in some stupid way in a deeper method

AztecIVC& ivc,
const WitnessVector& witness,
const size_t size_hint)
{
// Construct the main kernel circuit logic excluding recursive verifiers
auto circuit = create_circuit<MegaCircuitBuilder>(constraint_system,
size_hint,
witness,
/*honk_recursion=*/false,
ivc.goblin.op_queue,
/*collect_gates_per_opcode=*/false);

// We expect the length of the internal verification queue to matche the number of ivc recursion constraints
if (constraint_system.ivc_recursion_constraints.size() != ivc.verification_queue.size()) {
info("WARNING: Mismatch in number of recursive verifications during kernel creation!");
ASSERT(false);
}

// Create stdlib representations of each {proof, vkey} pair in the queue based on their native counterparts
ivc.instantiate_stdlib_verification_queue(circuit);

// Connect each {proof, vkey} pair from the constraint to the corresponding entry in the internal verification
// queue. This ensures that the witnesses utlized in constraints generated based on acir are properly connected to
// the constraints generated herein via the ivc scheme (e.g. recursive verifications).
for (auto [constraint, queue_entry] :
zip_view(constraint_system.ivc_recursion_constraints, ivc.stdlib_verification_queue)) {

// Reconstruct complete proof indices from acir constraint data (in which proof is stripped of public inputs)
std::vector<uint32_t> complete_proof_indices =
ProofSurgeon::create_indices_for_reconstructed_proof(constraint.proof, constraint.public_inputs);
ASSERT(complete_proof_indices.size() == queue_entry.proof.size());

// Assert equality between the proof indices from the constraint data and those of the internal proof
for (auto [proof_idx, proof_value] : zip_view(complete_proof_indices, queue_entry.proof)) {
circuit.assert_equal(proof_value.get_witness_index(), proof_idx);
}
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1090): assert equality between the internal vkey
ledwards2225 marked this conversation as resolved.
Show resolved Hide resolved
// and the constaint vkey, or simply use the constraint vkey directly to construct the stdlib vkey used in IVC.
}

// Complete the kernel circuit with all required recursive verifications, databus consistency checks etc.
ivc.complete_kernel_circuit_logic(circuit);

return circuit;
};

template void build_constraints<MegaCircuitBuilder>(MegaCircuitBuilder&, AcirFormat&, bool, bool, bool);

} // namespace acir_format
Loading
Loading