Skip to content

Commit

Permalink
Merge 29366ac into 8ac81b1
Browse files Browse the repository at this point in the history
  • Loading branch information
ledwards2225 authored Sep 10, 2024
2 parents 8ac81b1 + 29366ac commit 998ec12
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 133 deletions.
24 changes: 20 additions & 4 deletions barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,32 @@
namespace bb {

/**
* @brief Instantiate a stdlib verification queue corresponding to the native counterpart
* @brief Instantiate a stdlib verification queue for use in the kernel completion logic
* @details Construct a stdlib proof/verification_key for each entry in the native verification queue. By default, both
* are constructed from their counterpart in the native queue. Alternatively, Stdlib verification keys can be provided
* directly as input to this method. (The later option is used, for example, when constructing recursive verifiers based
* on the verification key witnesses from an acir recursion constraint. This option is not provided for proofs since
* valid proof witnesses are in general not known at the time of acir constraint generation).
*
* @param circuit
*/
void AztecIVC::instantiate_stdlib_verification_queue(ClientCircuit& circuit)
void AztecIVC::instantiate_stdlib_verification_queue(
ClientCircuit& circuit, const std::vector<std::shared_ptr<RecursiveVerificationKey>>& input_keys)
{
bool vkeys_provided = !input_keys.empty();
if (vkeys_provided && verification_queue.size() != input_keys.size()) {
info("Warning: Incorrect number of verification keys provided in stdlib verification queue instantiation.");
ASSERT(false);
}

size_t key_idx = 0;
for (auto& [proof, vkey, type] : verification_queue) {
// Construct stdlib verification key and proof
// Construct stdlib proof directly from the internal native queue data
auto stdlib_proof = bb::convert_proof_to_witness(&circuit, proof);
auto stdlib_vkey = std::make_shared<RecursiveVerificationKey>(&circuit, vkey);

// 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 });
}
Expand Down
3 changes: 2 additions & 1 deletion barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ class AztecIVC {

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

void instantiate_stdlib_verification_queue(ClientCircuit& circuit);
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ template <> class VerifierCommitmentKey<curve::BN254> {
using Commitment = typename Curve::AffineElement;

VerifierCommitmentKey() { srs = srs::get_crs_factory<Curve>()->get_verifier_crs(); };
bool operator==(const VerifierCommitmentKey&) const = default;

Commitment get_g1_identity() { return srs->get_g1_identity(); }

Expand Down
32 changes: 23 additions & 9 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,12 @@ MegaCircuitBuilder create_circuit(AcirFormat& constraint_system,

/**
* @brief Create a kernel circuit from a constraint system and an IVC instance
* @details This method processes ivc_recursion_constraints using the kernel completion logic contained in AztecIvc.
* Since verification keys are known at the time of acir generation, the verification key witnesses contained in the
* constraints are used directly to instantiate the recursive verifiers. On the other hand, the proof witnesses
* contained in the constraints are generally 'dummy' values since proofs are not known during acir generation (with the
* exception of public inputs). This is remedied by connecting the dummy proof witnesses to the genuine proof witnesses,
* known internally to the IVC class, via copy constraints.
*
* @param constraint_system AcirFormat constraint system possibly containing IVC recursion constraints
* @param ivc An IVC instance containing internal data about proofs to be verified
Expand All @@ -491,6 +497,8 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system,
const WitnessVector& witness,
const size_t size_hint)
{
using StdlibVerificationKey = AztecIVC::RecursiveVerificationKey;

// Construct the main kernel circuit logic excluding recursive verifiers
auto circuit = create_circuit<MegaCircuitBuilder>(constraint_system,
size_hint,
Expand All @@ -499,18 +507,26 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system,
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
// We expect the length of the internal verification queue to match 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);
// 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);

// 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).
// Connect the proof/public_input witness indices from each constraint to the corresponding proof witnesses 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)) {

Expand All @@ -520,11 +536,9 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system,
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)) {
for (auto [proof_value, proof_idx] : zip_view(queue_entry.proof, complete_proof_indices)) {
circuit.assert_equal(proof_value.get_witness_index(), proof_idx);
}
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1090): assert equality between the internal vkey
// 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ std::vector<bb::fr> convert_grumpkin_fr_to_bn254_frs(const grumpkin::fr& val);
*/
template <typename T> std::vector<bb::fr> convert_to_bn254_frs(const T& val)
{
if constexpr (IsAnyOf<T, bool, uint32_t, uint64_t, bb::fr>) {
if constexpr (IsAnyOf<T, bool, uint32_t, uint64_t, size_t, bb::fr>) {
std::vector<bb::fr> fr_vec{ val };
return fr_vec;
} else if constexpr (IsAnyOf<T, grumpkin::fr>) {
Expand Down
44 changes: 19 additions & 25 deletions barretenberg/cpp/src/barretenberg/flavor/flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ namespace bb {
*/
class PrecomputedEntitiesBase {
public:
bool operator==(const PrecomputedEntitiesBase& other) const = default;
uint64_t circuit_size;
uint64_t log_circuit_size;
uint64_t num_public_inputs;
CircuitType circuit_type; // TODO(#392)
};

/**
Expand Down Expand Up @@ -158,6 +158,7 @@ class VerificationKey_ : public PrecomputedCommitments {
AggregationObjectPubInputIndices recursive_proof_public_input_indices = {};
uint64_t pub_inputs_offset = 0;

bool operator==(const VerificationKey_&) const = default;
VerificationKey_() = default;
VerificationKey_(const size_t circuit_size, const size_t num_public_inputs)
{
Expand All @@ -173,32 +174,25 @@ class VerificationKey_ : public PrecomputedCommitments {
*/
std::vector<FF> to_field_elements()
{
using namespace bb::field_conversion;

auto serialize_to_field_buffer = [](const auto& input, std::vector<FF>& buffer) {
std::vector<FF> input_fields = convert_to_bn254_frs(input);
buffer.insert(buffer.end(), input_fields.begin(), input_fields.end());
};

std::vector<FF> elements;
std::vector<FF> circuit_size_elements = bb::field_conversion::convert_to_bn254_frs(this->circuit_size);
elements.insert(elements.end(), circuit_size_elements.begin(), circuit_size_elements.end());
// do the same for the rest of the fields
std::vector<FF> num_public_inputs_elements =
bb::field_conversion::convert_to_bn254_frs(this->num_public_inputs);
elements.insert(elements.end(), num_public_inputs_elements.begin(), num_public_inputs_elements.end());
std::vector<FF> pub_inputs_offset_elements =
bb::field_conversion::convert_to_bn254_frs(this->pub_inputs_offset);
elements.insert(elements.end(), pub_inputs_offset_elements.begin(), pub_inputs_offset_elements.end());

std::vector<FF> contains_recursive_proof_elements =
bb::field_conversion::convert_to_bn254_frs(this->contains_recursive_proof);
elements.insert(
elements.end(), contains_recursive_proof_elements.begin(), contains_recursive_proof_elements.end());

std::vector<FF> recursive_proof_public_input_indices_elements =
bb::field_conversion::convert_to_bn254_frs(this->recursive_proof_public_input_indices);
elements.insert(elements.end(),
recursive_proof_public_input_indices_elements.begin(),
recursive_proof_public_input_indices_elements.end());

for (Commitment& comm : this->get_all()) {
std::vector<FF> comm_elements = bb::field_conversion::convert_to_bn254_frs(comm);
elements.insert(elements.end(), comm_elements.begin(), comm_elements.end());

serialize_to_field_buffer(this->circuit_size, elements);
serialize_to_field_buffer(this->num_public_inputs, elements);
serialize_to_field_buffer(this->pub_inputs_offset, elements);
serialize_to_field_buffer(this->contains_recursive_proof, elements);
serialize_to_field_buffer(this->recursive_proof_public_input_indices, elements);

for (Commitment& commitment : this->get_all()) {
serialize_to_field_buffer(commitment, elements);
}

return elements;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,22 @@ template <typename Builder, typename T> std::vector<fr<Builder>> convert_to_bn25
}
}

/**
* @brief Deserialize an object of specified type from a buffer of field elements; update provided read count in place
*
* @tparam TargetType Type to reconstruct from buffer of field elements
* @param builder
* @param elements Buffer of field elements
* @param num_frs_read Index at which to read into buffer
*/
template <typename TargetType, typename Builder>
TargetType deserialize_from_frs(Builder& builder, std::span<fr<Builder>> elements, size_t& num_frs_read)
{
size_t num_frs = calc_num_bn254_frs<Builder, TargetType>();
ASSERT(elements.size() >= num_frs_read + num_frs);
TargetType result = convert_from_bn254_frs<Builder, TargetType>(builder, elements.subspan(num_frs_read, num_frs));
num_frs_read += num_frs;
return result;
}

} // namespace bb::stdlib::field_conversion
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ template <IsRecursiveFlavor Flavor> class RecursiveDeciderVerificationKey_ {
using Builder = typename Flavor::CircuitBuilder;
using NativeFlavor = typename Flavor::NativeFlavor;
using DeciderVerificationKey = bb::DeciderVerificationKey_<NativeFlavor>;
using VerifierCommitmentKey = typename NativeFlavor::VerifierCommitmentKey;

Builder* builder;

Expand Down Expand Up @@ -101,7 +102,9 @@ template <IsRecursiveFlavor Flavor> class RecursiveDeciderVerificationKey_ {
{
auto native_honk_vk = std::make_shared<NativeVerificationKey>(verification_key->circuit_size,
verification_key->num_public_inputs);
native_honk_vk->pcs_verification_key = verification_key->pcs_verification_key;
native_honk_vk->pcs_verification_key = verification_key->pcs_verification_key == nullptr
? std::make_shared<VerifierCommitmentKey>()
: verification_key->pcs_verification_key;
native_honk_vk->pub_inputs_offset = verification_key->pub_inputs_offset;
native_honk_vk->contains_recursive_proof = verification_key->contains_recursive_proof;
native_honk_vk->recursive_proof_public_input_indices = verification_key->recursive_proof_public_input_indices;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ enum class BusId { CALLDATA, SECONDARY_CALLDATA, RETURNDATA };
*
*/
struct DatabusPropagationData {
bool operator==(const DatabusPropagationData&) const = default;

// Flags indicating whether the public inputs contain commitment(s) to app/kernel return data
bool contains_app_return_data_commitment = false;
bool contains_kernel_return_data_commitment = false;
Expand All @@ -80,6 +82,12 @@ struct DatabusPropagationData {

// Is this a kernel circuit (used to determine when databus consistency checks can be appended to a circuit in IVC)
bool is_kernel = false;

MSGPACK_FIELDS(contains_app_return_data_commitment,
contains_kernel_return_data_commitment,
app_return_data_public_input_idx,
kernel_return_data_public_input_idx,
is_kernel);
};

} // namespace bb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "barretenberg/common/serialize.hpp"
#include "barretenberg/ecc/curves/bn254/fr.hpp"
#include "barretenberg/numeric/uint256/uint256.hpp"
#include "barretenberg/plonk_honk_shared/library/grand_product_delta.hpp"
#include "barretenberg/relations/permutation_relation.hpp"
#include "barretenberg/relations/relation_parameters.hpp"
#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp"
#include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp"
#include "barretenberg/stdlib_circuit_builders/plookup_tables/types.hpp"
#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp"
#include "barretenberg/sumcheck/sumcheck_round.hpp"
#include "barretenberg/ultra_honk/ultra_prover.hpp"
#include "barretenberg/ultra_honk/ultra_verifier.hpp"

#include <gtest/gtest.h>

using namespace bb;

template <typename Flavor> class FlavorSerializationTests : public ::testing::Test {
public:
using Builder = typename Flavor::CircuitBuilder;
using DeciderProvingKey = DeciderProvingKey_<Flavor>;
using VerificationKey = typename Flavor::VerificationKey;

protected:
static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); }
};

using FlavorTypes = testing::Types<UltraFlavor, UltraKeccakFlavor, MegaFlavor>;
TYPED_TEST_SUITE(FlavorSerializationTests, FlavorTypes);

// Test msgpack serialization/deserialization of verification keys
TYPED_TEST(FlavorSerializationTests, VerificationKeySerialization)
{
using Builder = typename TestFixture::Builder;
using DeciderProvingKey = typename TestFixture::DeciderProvingKey;
using VerificationKey = typename TestFixture::VerificationKey;

Builder builder;

// Add some arbitrary arithmetic gates that utilize public inputs
MockCircuits::add_arithmetic_gates_with_public_inputs(builder, /*num_gates=*/100);

auto proving_key = std::make_shared<DeciderProvingKey>(builder);
VerificationKey original_vkey{ proving_key->proving_key };

// Set the pcs ptr to null since this will not be reconstructed correctly from buffer
original_vkey.pcs_verification_key = nullptr;

// Populate some non-zero values in the databus_propagation_data to ensure its being handled
if constexpr (IsMegaBuilder<Builder>) {
original_vkey.databus_propagation_data.contains_app_return_data_commitment = 1;
original_vkey.databus_propagation_data.contains_kernel_return_data_commitment = 1;
original_vkey.databus_propagation_data.app_return_data_public_input_idx = 2;
original_vkey.databus_propagation_data.kernel_return_data_public_input_idx = 4;
original_vkey.databus_propagation_data.is_kernel = 1;
}

// Serialize and deserialize the verification key
std::vector<uint8_t> vkey_buffer = to_buffer(original_vkey);
VerificationKey deserialized_vkey = from_buffer<VerificationKey>(vkey_buffer);

// Ensure the original is equal to the reconstructed
EXPECT_EQ(original_vkey, deserialized_vkey);
}
Loading

0 comments on commit 998ec12

Please sign in to comment.