Skip to content

Commit

Permalink
feat: mock data for IVC (#9893)
Browse files Browse the repository at this point in the history
Incremental work towards a write_vk flow for kernel circuits. kernel
circuits are generated from acir via the method
`acir_format::create_kernel_circuit()` which takes as input the raw acir
data plus a ClientIvc instance containing the proofs to be recursively
verified in that circuit (Oink/PG + merge). In the context of VK
generation, those proofs are not yet known, so the IVC state has to be
mocked. This PR adds such functionality but only for the oink case (i.e.
the state of the IVC after accumulating the first app which only calls
the pink prover). Equivalent logic for mocking the state after
subsequent accumulations will be handled in a follow on.
  • Loading branch information
ledwards2225 authored Nov 12, 2024
1 parent a6c1160 commit 9325f6f
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "acir_format.hpp"
#include "barretenberg/common/log.hpp"
#include "barretenberg/common/throw_or_abort.hpp"
#include "barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp"
#include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp"
#include "barretenberg/stdlib/primitives/field/field_conversion.hpp"
#include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp"
Expand Down Expand Up @@ -490,14 +491,24 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system,
ASSERT(false);
}

// If no witness is provided, populate the VK and public inputs in the recursion constraint with dummy values so
// that the present kernel circuit is constructed correctly. (Used for constructing VKs without witnesses).
if (witness.empty()) {
// Create stdlib representations of each {proof, vkey} pair to be recursively verified
for (auto [constraint, queue_entry] :
zip_view(constraint_system.ivc_recursion_constraints, ivc.verification_queue)) {

populate_dummy_vk_in_constraint(circuit, queue_entry.honk_verification_key, constraint.key);
}
}

// Construct a stdlib verification key for each constraint based on the verification key witness indices therein
std::vector<std::shared_ptr<StdlibVerificationKey>> stdlib_verification_keys;
stdlib_verification_keys.reserve(constraint_system.ivc_recursion_constraints.size());
for (const auto& constraint : constraint_system.ivc_recursion_constraints) {
stdlib_verification_keys.push_back(std::make_shared<StdlibVerificationKey>(
StdlibVerificationKey::from_witness_indices(circuit, constraint.key)));
}

// Create stdlib representations of each {proof, vkey} pair to be recursively verified
ivc.instantiate_stdlib_verification_queue(circuit, stdlib_verification_keys);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#include "ivc_recursion_constraint.hpp"
#include "barretenberg/flavor/flavor.hpp"
#include "barretenberg/plonk_honk_shared/types/aggregation_object_type.hpp"
#include "barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.hpp"
#include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp"
#include "barretenberg/stdlib/primitives/bigfield/constants.hpp"
#include "barretenberg/stdlib/primitives/curves/bn254.hpp"
#include "barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp"
#include "proof_surgeon.hpp"
#include "recursion_constraint.hpp"

namespace acir_format {

using namespace bb;
using field_ct = stdlib::field_t<Builder>;

ClientIVC create_mock_ivc_from_constraints(const std::vector<RecursionConstraint>& constraints)
{
ClientIVC ivc;
ivc.trace_settings.structure = TraceStructure::SMALL_TEST;

for (const auto& constraint : constraints) {
if (static_cast<uint32_t>(PROOF_TYPE::OINK) == constraint.proof_type) {
mock_ivc_oink_accumulation(ivc, constraint.public_inputs.size());
} else if (static_cast<uint32_t>(PROOF_TYPE::PG) == constraint.proof_type) {
// perform equivalent mocking for PG accumulation
}
}

return ivc;
}

/**
* @brief Populate an IVC instance with data that mimics the state after accumulating the first app (which runs the oink
* prover)
*@details Mock state consists a mock verification queue entry of type OINK (proof, VK) and a mocked merge proof
*
* @param ivc
* @param num_public_inputs_app num pub inputs in accumulated app, excluding fixed components, e.g. pairing points
*/
void mock_ivc_oink_accumulation(ClientIVC& ivc, size_t num_public_inputs_app)
{
ClientIVC::VerifierInputs oink_entry =
acir_format::create_dummy_vkey_and_proof_oink(ivc.trace_settings, num_public_inputs_app);
ivc.verification_queue.emplace_back(oink_entry);
ivc.merge_verification_queue.emplace_back(acir_format::create_dummy_merge_proof());
ivc.initialized = true;
}

/**
* @brief Create a mock oink proof and VK that have the correct structure but are not necessarily valid
*
*/
ClientIVC::VerifierInputs create_dummy_vkey_and_proof_oink(const TraceSettings& trace_settings,
const size_t num_public_inputs = 0)
{
using Flavor = MegaFlavor;
using VerificationKey = ClientIVC::VerificationKey;
using FF = bb::fr;

MegaArith<FF>::TraceBlocks blocks;
blocks.set_fixed_block_sizes(trace_settings);
blocks.compute_offsets(/*is_structured=*/true);
size_t structured_dyadic_size = blocks.get_structured_dyadic_size();
size_t pub_inputs_offset = blocks.pub_inputs.trace_offset;

ClientIVC::VerifierInputs verifier_inputs;
verifier_inputs.type = ClientIVC::QUEUE_TYPE::OINK;

FF mock_val(5);

auto mock_commitment = curve::BN254::AffineElement::one() * mock_val;
std::vector<FF> mock_commitment_frs = field_conversion::convert_to_bn254_frs(mock_commitment);

// Set proof preamble (metadata plus public inputs)
size_t total_num_public_inputs = num_public_inputs + bb::PAIRING_POINT_ACCUMULATOR_SIZE;
verifier_inputs.proof.emplace_back(structured_dyadic_size);
verifier_inputs.proof.emplace_back(total_num_public_inputs);
verifier_inputs.proof.emplace_back(pub_inputs_offset);
for (size_t i = 0; i < total_num_public_inputs; ++i) {
verifier_inputs.proof.emplace_back(0);
}

// Witness polynomial commitments
for (size_t i = 0; i < Flavor::NUM_WITNESS_ENTITIES; ++i) {
for (const FF& val : mock_commitment_frs) {
verifier_inputs.proof.emplace_back(val);
}
}

// Set relevant VK metadata and commitments
verifier_inputs.honk_verification_key = std::make_shared<VerificationKey>();
verifier_inputs.honk_verification_key->circuit_size = structured_dyadic_size;
verifier_inputs.honk_verification_key->num_public_inputs = total_num_public_inputs;
verifier_inputs.honk_verification_key->pub_inputs_offset = blocks.pub_inputs.trace_offset; // must be set correctly
verifier_inputs.honk_verification_key->contains_pairing_point_accumulator = true;
for (auto& commitment : verifier_inputs.honk_verification_key->get_all()) {
commitment = mock_commitment;
}

return verifier_inputs;
}

/**
* @brief Create a mock merge proof which has the correct structure but is not necessarily valid
*
* @return ClientIVC::MergeProof
*/
ClientIVC::MergeProof create_dummy_merge_proof()
{
using FF = bb::fr;

std::vector<FF> proof;

FF mock_val(5);
auto mock_commitment = curve::BN254::AffineElement::one() * mock_val;
std::vector<FF> mock_commitment_frs = field_conversion::convert_to_bn254_frs(mock_commitment);

// There are 12 entities in the merge protocol (4 columns x 3 components; aggregate transcript, previous aggregate
// transcript, current transcript contribution)
const size_t NUM_TRANSCRIPT_ENTITIES = 12;

// Transcript poly commitments
for (size_t i = 0; i < NUM_TRANSCRIPT_ENTITIES; ++i) {
for (const FF& val : mock_commitment_frs) {
proof.emplace_back(val);
}
}
// Transcript poly evaluations
for (size_t i = 0; i < NUM_TRANSCRIPT_ENTITIES; ++i) {
proof.emplace_back(mock_val);
}
// Batched KZG quotient commitment
for (const FF& val : mock_commitment_frs) {
proof.emplace_back(val);
}

return proof;
}

/**
* @brief Populate VK witness fields from a recursion constraint from a provided VerificationKey
*
* @param builder
* @param mock_verification_key
* @param key_witness_indices
*/
void populate_dummy_vk_in_constraint(MegaCircuitBuilder& builder,
const std::shared_ptr<ClientIVC::VerificationKey>& mock_verification_key,
std::vector<uint32_t>& key_witness_indices)
{
using Flavor = MegaFlavor;
using FF = Flavor::FF;

// Convert the VerificationKey to fields
std::vector<FF> mock_vk_fields = mock_verification_key->to_field_elements();
ASSERT(mock_vk_fields.size() == key_witness_indices.size());

// Add the fields to the witness and set the key witness indices accordingly
for (auto [witness_idx, value] : zip_view(key_witness_indices, mock_vk_fields)) {
witness_idx = builder.add_variable(value);
}
}

} // namespace acir_format
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once
#include "barretenberg/client_ivc/client_ivc.hpp"
#include "barretenberg/dsl/acir_format/recursion_constraint.hpp"
#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp"
#include <vector>

namespace acir_format {

using namespace bb;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1148): logic in this file is incomplete. See issue for
// details.

ClientIVC create_mock_ivc_from_constraints(const std::vector<RecursionConstraint>& constraints);

void mock_ivc_oink_accumulation(ClientIVC& ivc, size_t num_public_inputs_app = 0);

ClientIVC::VerifierInputs create_dummy_vkey_and_proof_oink(const TraceSettings& trace_settings,
const size_t num_public_inputs);

ClientIVC::MergeProof create_dummy_merge_proof();

void populate_dummy_vk_in_constraint(MegaCircuitBuilder& builder,
const std::shared_ptr<ClientIVC::VerificationKey>& mock_verification_key,
std::vector<uint32_t>& key_witness_indices);

} // namespace acir_format
Loading

0 comments on commit 9325f6f

Please sign in to comment.