Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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) {
Copy link
Contributor

Choose a reason for hiding this comment

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

seems like we still have this for the cases where we don't actually get stdlib vkeys. Will this eventually go away?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess tests might not use stdlib vkeys

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah exactly, I left this pathway for tests

// 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>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

where does this come into use?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

some of the databus_propagation_data components are size_t (which I'm only now handling properly in this PR hence why we didn't need it before)

Copy link
Contributor

Choose a reason for hiding this comment

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

size_t is pretty scary for serde though, so I expect this to lead to trouble when we try to do any sort of mega flavor verification key serde through wasm.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

gotcha - I'll make a TODO to try to change the databus data to use uint32_t so we can get rid of this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

actually was able to make this change pretty easily so its done

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)
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this was just unused huh

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah was pretty surprised myself - not a single thing broke when I deleted it

};

/**
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;
Copy link
Contributor

Choose a reason for hiding this comment

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

odd this worked before...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah it was indeed setting it to null in the previous usage but we just never ended up using it. It only came up for me in the verification of the AztecIvc proof

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