Skip to content
5 changes: 0 additions & 5 deletions barretenberg/acir_tests/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,6 @@ function test_cmds_internal {
echo SYS=ultra_honk FLOW=prove_then_verify RECURSIVE=true $run_test double_verify_honk_proof
echo SYS=ultra_honk FLOW=prove_then_verify HASH=keccak $run_test assert_statement
echo SYS=ultra_honk FLOW=prove_then_verify ROLLUP=true $run_test verify_rollup_honk_proof

Copy link
Contributor Author

@ledwards2225 ledwards2225 Mar 11, 2025

Choose a reason for hiding this comment

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

These tests are going to be removed by this PR from Cody/Adam. They represent test-only functionality that we never had plans to support. (Creating a CIVC proof for a single circuit).

# barretenberg-acir-tests-bb-client-ivc:
echo FLOW=prove_then_verify_client_ivc $run_test 6_array
echo FLOW=prove_then_verify_client_ivc $run_test databus
echo FLOW=prove_then_verify_client_ivc $run_test databus_two_calldata
}

function ultra_honk_wasm_memory {
Expand Down
98 changes: 33 additions & 65 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ void ClientIVC::instantiate_stdlib_verification_queue(
}

size_t key_idx = 0;
for (auto& [proof, vkey, type] : verification_queue) {
for (auto& [proof, merge_proof, vkey, type] : verification_queue) {
// Construct stdlib proof directly from the internal native queue data
auto stdlib_proof = bb::convert_native_proof_to_stdlib(&circuit, proof);
auto stdlib_merge_proof = bb::convert_native_proof_to_stdlib(&circuit, merge_proof);

// Use the provided stdlib vkey if present, otherwise construct one from the internal native queue
auto stdlib_vkey =
vkeys_provided ? input_keys[key_idx++] : std::make_shared<RecursiveVerificationKey>(&circuit, vkey);

stdlib_verification_queue.push_back({ stdlib_proof, stdlib_vkey, type });
stdlib_verification_queue.push_back({ stdlib_proof, stdlib_merge_proof, stdlib_vkey, type });
}
verification_queue.clear(); // the native data is not needed beyond this point
}
Expand All @@ -43,28 +44,23 @@ void ClientIVC::instantiate_stdlib_verification_queue(
* 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 verification key associated with the proof
* @param type The type of the proof (equivalently, the type of the verifier)
* @param circuit
* @param verifier_inputs {proof, merge_proof, vkey, type (Oink/PG)} A set of inputs for recursive verification
*/
void ClientIVC::perform_recursive_verification_and_databus_consistency_checks(
ClientCircuit& circuit,
const StdlibProof<ClientCircuit>& proof,
const std::shared_ptr<RecursiveVerificationKey>& vkey,
const QUEUE_TYPE type)
ClientCircuit& circuit, const StdlibVerifierInputs& verifier_inputs)
{
// Store the decider vk for the incoming circuit; its data is used in the databus consistency checks below
std::shared_ptr<RecursiveDeciderVerificationKey> decider_vk;

switch (type) {
switch (verifier_inputs.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);
FoldingRecursiveVerifier verifier{ &circuit, stdlib_verifier_accum, { verifier_inputs.honk_verification_key } };
auto verifier_accum = verifier.verify_folding_proof(verifier_inputs.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());
Expand All @@ -75,11 +71,12 @@ void ClientIVC::perform_recursive_verification_and_databus_consistency_checks(
}
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);
auto verifier_accum =
std::make_shared<RecursiveDeciderVerificationKey>(&circuit, verifier_inputs.honk_verification_key);

// Perform oink recursive verification to complete the initial verifier accumulator
OinkRecursiveVerifier oink{ &circuit, verifier_accum };
oink.verify_proof(proof);
oink.verify_proof(verifier_inputs.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
Expand All @@ -93,6 +90,10 @@ void ClientIVC::perform_recursive_verification_and_databus_consistency_checks(
}
}

// Recursively verify the merge proof for the circuit being recursively verified
// TODO(https://github.com/AztecProtocol/barretenberg/issues/950): handle pairing point accumulation
[[maybe_unused]] auto pairing_points = GoblinVerifier::recursive_verify_merge(circuit, verifier_inputs.merge_proof);

// Set the return data commitment to be propagated on the public inputs of the present kernel and perform
// consistency checks between the calldata commitments and the return data commitments contained within the public
// inputs
Expand All @@ -104,20 +105,6 @@ void ClientIVC::perform_recursive_verification_and_databus_consistency_checks(
decider_vk->verification_key->databus_propagation_data);
}

/**
* @brief Perform recursive merge verification for each merge proof in the queue
*
* @param circuit
*/
void ClientIVC::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
Expand All @@ -135,17 +122,14 @@ void ClientIVC::complete_kernel_circuit_logic(ClientCircuit& circuit)
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);
// Perform recursive verification and databus consistency checks for each entry in the verification queue
for (auto& verifier_input : stdlib_verification_queue) {
perform_recursive_verification_and_databus_consistency_checks(circuit, verifier_input);
}
stdlib_verification_queue.clear();

// Propagate return data commitments via the public inputs for use in databus consistency checks
bus_depot.propagate_return_data_commitments(circuit);

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

/**
Expand All @@ -160,13 +144,12 @@ void ClientIVC::complete_kernel_circuit_logic(ClientCircuit& circuit)
* @param precomputed_vk
*/
void ClientIVC::accumulate(ClientCircuit& circuit,
const bool _one_circuit,
[[maybe_unused]] const bool _one_circuit,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The last remnants of this mechanism will be cleaned out by the aforementioned PR from Cody/Adam

const std::shared_ptr<MegaVerificationKey>& precomputed_vk,
const bool mock_vk)
{
// Construct merge proof for the present circuit and add to merge verification queue
MergeProof merge_proof = goblin.prove_merge(circuit);
merge_verification_queue.emplace_back(merge_proof);

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): Do proper aggregation with merge recursive
// verifier.
Expand Down Expand Up @@ -194,25 +177,12 @@ void ClientIVC::accumulate(ClientCircuit& circuit,
vinfo("set honk vk metadata");
}

if (_one_circuit) {
// The initial stack consisted of only one circuit, so construct a proof for it.
one_circuit = _one_circuit;
MegaProver prover{ proving_key };
vinfo("computing mega proof...");
mega_proof = prover.prove();
vinfo("mega proof computed");

proving_key->is_accumulator = true; // indicate to PG that it should not run oink on this key
// Initialize the gate challenges to zero for use in first round of folding
proving_key->gate_challenges = std::vector<FF>(CONST_PG_LOG_N, 0);

fold_output.accumulator = proving_key;
} else if (!initialized) {
if (!initialized) {
// If this is the first circuit in the IVC, use oink to complete the decider proving key and generate an oink
// proof
MegaOinkProver oink_prover{ proving_key };
vinfo("computing oink proof...");
oink_prover.prove();
HonkProof oink_proof = oink_prover.prove();
vinfo("oink proof constructed");
proving_key->is_accumulator = true; // indicate to PG that it should not run oink on this key
// Initialize the gate challenges to zero for use in first round of folding
Expand All @@ -221,8 +191,7 @@ void ClientIVC::accumulate(ClientCircuit& circuit,
fold_output.accumulator = proving_key; // initialize the prover accum with the completed key

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

initialized = true;
} else { // Otherwise, fold the new key into the accumulator
Expand All @@ -232,7 +201,7 @@ void ClientIVC::accumulate(ClientCircuit& circuit,
vinfo("constructed folding proof");

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

Expand All @@ -242,12 +211,12 @@ void ClientIVC::accumulate(ClientCircuit& circuit,
*
* @details The aim of this intermediate stage is to reduce the cost of producing a zero-knowledge ClientIVCProof.
* @return HonkProof - a Mega proof
* @return MergeProof - a Merge proof
*/
HonkProof ClientIVC::construct_and_prove_hiding_circuit()
std::pair<HonkProof, ClientIVC::MergeProof> ClientIVC::construct_and_prove_hiding_circuit()
{
trace_usage_tracker.print(); // print minimum structured sizes for each block
ASSERT(verification_queue.size() == 1);
ASSERT(merge_verification_queue.size() == 1); // ensure only a single merge proof remains in the queue

FoldProof& fold_proof = verification_queue[0].proof;
HonkProof decider_proof = decider_prove();
Expand All @@ -267,7 +236,11 @@ HonkProof ClientIVC::construct_and_prove_hiding_circuit()
builder.add_public_variable(fold_proof[i + offset]);
}

process_recursive_merge_verification_queue(builder);
const StdlibProof<ClientCircuit> stdlib_merge_proof =
bb::convert_native_proof_to_stdlib(&builder, verification_queue[0].merge_proof);

// TODO(https://github.com/AztecProtocol/barretenberg/issues/950): handle pairing point accumulation
[[maybe_unused]] auto pairing_points = GoblinVerifier::recursive_verify_merge(builder, stdlib_merge_proof);

// Construct stdlib accumulator, decider vkey and folding proof
auto stdlib_verifier_accumulator =
Expand All @@ -291,15 +264,14 @@ HonkProof ClientIVC::construct_and_prove_hiding_circuit()

// Construct the last merge proof for the present circuit and add to merge verification queue
MergeProof merge_proof = goblin.prove_merge(builder);
merge_verification_queue.emplace_back(merge_proof);

auto decider_pk = std::make_shared<DeciderProvingKey>(builder, TraceSettings(), bn254_commitment_key);
honk_vk = std::make_shared<MegaVerificationKey>(decider_pk->proving_key);
MegaProver prover(decider_pk);

HonkProof proof = prover.construct_proof();

return proof;
return { proof, merge_proof };
}

/**
Expand All @@ -309,12 +281,8 @@ HonkProof ClientIVC::construct_and_prove_hiding_circuit()
*/
ClientIVC::Proof ClientIVC::prove()
{
if (!one_circuit) {
mega_proof = construct_and_prove_hiding_circuit();
ASSERT(merge_verification_queue.size() == 1); // ensure only a single merge proof remains in the queue
}
auto [mega_proof, merge_proof] = construct_and_prove_hiding_circuit();

MergeProof& merge_proof = merge_verification_queue[0];
return { mega_proof, goblin.prove(merge_proof) };
};

Expand Down
19 changes: 5 additions & 14 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class ClientIVC {
// An entry in the native verification queue
struct VerifierInputs {
std::vector<FF> proof; // oink or PG
std::vector<FF> merge_proof;
std::shared_ptr<MegaVerificationKey> honk_verification_key;
QUEUE_TYPE type;
};
Expand All @@ -94,6 +95,7 @@ class ClientIVC {
// An entry in the stdlib verification queue
struct StdlibVerifierInputs {
StdlibProof<ClientCircuit> proof; // oink or PG
StdlibProof<ClientCircuit> merge_proof;
std::shared_ptr<RecursiveVerificationKey> honk_verification_key;
QUEUE_TYPE type;
};
Expand All @@ -117,8 +119,6 @@ class ClientIVC {
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;

// Management of linking databus commitments between circuits in the IVC
DataBusDepot bus_depot;
Expand All @@ -130,10 +130,6 @@ class ClientIVC {

GoblinProver goblin;

// We dynamically detect whether the input stack consists of one circuit, in which case we do not construct the
// hiding circuit and instead simply prove the single input circuit.
bool one_circuit = false;

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

ClientIVC(TraceSettings trace_settings = {})
Expand All @@ -148,13 +144,8 @@ class ClientIVC {
void instantiate_stdlib_verification_queue(
ClientCircuit& circuit, const std::vector<std::shared_ptr<RecursiveVerificationKey>>& input_keys = {});

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);
void perform_recursive_verification_and_databus_consistency_checks(ClientCircuit& circuit,
const StdlibVerifierInputs& verifier_inputs);

// 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 @@ -174,7 +165,7 @@ class ClientIVC {

Proof prove();

HonkProof construct_and_prove_hiding_circuit();
std::pair<HonkProof, MergeProof> construct_and_prove_hiding_circuit();

static bool verify(const Proof& proof, const VerificationKey& vk);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ void mock_ivc_accumulation(const std::shared_ptr<ClientIVC>& ivc, ClientIVC::QUE
ClientIVC::VerifierInputs entry =
acir_format::create_mock_verification_queue_entry(type, ivc->trace_settings, is_kernel);
ivc->verification_queue.emplace_back(entry);
ivc->merge_verification_queue.emplace_back(acir_format::create_dummy_merge_proof());
ivc->initialized = true;
}

Expand Down Expand Up @@ -123,7 +122,9 @@ ClientIVC::VerifierInputs create_mock_verification_queue_entry(const ClientIVC::
verification_key->databus_propagation_data = bb::DatabusPropagationData::kernel_default();
}

return ClientIVC::VerifierInputs{ proof, verification_key, verification_type };
std::vector<FF> merge_proof = create_dummy_merge_proof();

return ClientIVC::VerifierInputs{ proof, merge_proof, verification_key, verification_type };
}

/**
Expand Down
Loading