Skip to content

Commit c51bd8f

Browse files
ledwards2225AztecBot
authored andcommitted
feat: Verification key stuff (#8431)
The previous [PR](AztecProtocol/aztec-packages#8230) in this series set up the basic infrastructure for completing kernel circuits from acir RecursionConstraints, including connecting the dummy proof in the constraint to the genuine witnesses known internally in AztecIvc. This PR completes this line work by using the (genuine) verification key witnesses in the constraint to construct the stdlib verification keys from which the recursive verifiers are instantiated. Doing this properly involved correcting/implementing/testing various serialization/deserialization/construction methods for native and stdlib verification keys - this accounts for most of the changes in this PR. The corrections mostly applied to Mega and were related to not accounting for `databus_propagation_data`. I also updated some of the corresponding Ultra methods in an attempt to make them a bit more readable and more easily maintainable. closes #1090
1 parent 5e23c86 commit c51bd8f

File tree

14 files changed

+286
-133
lines changed

14 files changed

+286
-133
lines changed

cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp

+20-4
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,32 @@
44
namespace bb {
55

66
/**
7-
* @brief Instantiate a stdlib verification queue corresponding to the native counterpart
7+
* @brief Instantiate a stdlib verification queue for use in the kernel completion logic
8+
* @details Construct a stdlib proof/verification_key for each entry in the native verification queue. By default, both
9+
* are constructed from their counterpart in the native queue. Alternatively, Stdlib verification keys can be provided
10+
* directly as input to this method. (The later option is used, for example, when constructing recursive verifiers based
11+
* on the verification key witnesses from an acir recursion constraint. This option is not provided for proofs since
12+
* valid proof witnesses are in general not known at the time of acir constraint generation).
813
*
914
* @param circuit
1015
*/
11-
void AztecIVC::instantiate_stdlib_verification_queue(ClientCircuit& circuit)
16+
void AztecIVC::instantiate_stdlib_verification_queue(
17+
ClientCircuit& circuit, const std::vector<std::shared_ptr<RecursiveVerificationKey>>& input_keys)
1218
{
19+
bool vkeys_provided = !input_keys.empty();
20+
if (vkeys_provided && verification_queue.size() != input_keys.size()) {
21+
info("Warning: Incorrect number of verification keys provided in stdlib verification queue instantiation.");
22+
ASSERT(false);
23+
}
24+
25+
size_t key_idx = 0;
1326
for (auto& [proof, vkey, type] : verification_queue) {
14-
// Construct stdlib verification key and proof
27+
// Construct stdlib proof directly from the internal native queue data
1528
auto stdlib_proof = bb::convert_proof_to_witness(&circuit, proof);
16-
auto stdlib_vkey = std::make_shared<RecursiveVerificationKey>(&circuit, vkey);
29+
30+
// Use the provided stdlib vkey if present, otherwise construct one from the internal native queue
31+
auto stdlib_vkey =
32+
vkeys_provided ? input_keys[key_idx++] : std::make_shared<RecursiveVerificationKey>(&circuit, vkey);
1733

1834
stdlib_verification_queue.push_back({ stdlib_proof, stdlib_vkey, type });
1935
}

cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ class AztecIVC {
111111

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

114-
void instantiate_stdlib_verification_queue(ClientCircuit& circuit);
114+
void instantiate_stdlib_verification_queue(
115+
ClientCircuit& circuit, const std::vector<std::shared_ptr<RecursiveVerificationKey>>& input_keys = {});
115116

116117
void perform_recursive_verification_and_databus_consistency_checks(
117118
ClientCircuit& circuit,

cpp/src/barretenberg/commitment_schemes/verification_key.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ template <> class VerifierCommitmentKey<curve::BN254> {
3434
using Commitment = typename Curve::AffineElement;
3535

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

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

cpp/src/barretenberg/dsl/acir_format/acir_format.cpp

+23-9
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,12 @@ MegaCircuitBuilder create_circuit(AcirFormat& constraint_system,
479479

480480
/**
481481
* @brief Create a kernel circuit from a constraint system and an IVC instance
482+
* @details This method processes ivc_recursion_constraints using the kernel completion logic contained in AztecIvc.
483+
* Since verification keys are known at the time of acir generation, the verification key witnesses contained in the
484+
* constraints are used directly to instantiate the recursive verifiers. On the other hand, the proof witnesses
485+
* contained in the constraints are generally 'dummy' values since proofs are not known during acir generation (with the
486+
* exception of public inputs). This is remedied by connecting the dummy proof witnesses to the genuine proof witnesses,
487+
* known internally to the IVC class, via copy constraints.
482488
*
483489
* @param constraint_system AcirFormat constraint system possibly containing IVC recursion constraints
484490
* @param ivc An IVC instance containing internal data about proofs to be verified
@@ -491,6 +497,8 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system,
491497
const WitnessVector& witness,
492498
const size_t size_hint)
493499
{
500+
using StdlibVerificationKey = AztecIVC::RecursiveVerificationKey;
501+
494502
// Construct the main kernel circuit logic excluding recursive verifiers
495503
auto circuit = create_circuit<MegaCircuitBuilder>(constraint_system,
496504
size_hint,
@@ -499,18 +507,26 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system,
499507
ivc.goblin.op_queue,
500508
/*collect_gates_per_opcode=*/false);
501509

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

508-
// Create stdlib representations of each {proof, vkey} pair in the queue based on their native counterparts
509-
ivc.instantiate_stdlib_verification_queue(circuit);
516+
// Construct a stdlib verification key for each constraint based on the verification key witness indices therein
517+
std::vector<std::shared_ptr<StdlibVerificationKey>> stdlib_verification_keys;
518+
stdlib_verification_keys.reserve(constraint_system.ivc_recursion_constraints.size());
519+
for (const auto& constraint : constraint_system.ivc_recursion_constraints) {
520+
stdlib_verification_keys.push_back(std::make_shared<StdlibVerificationKey>(
521+
StdlibVerificationKey::from_witness_indices(circuit, constraint.key)));
522+
}
523+
524+
// Create stdlib representations of each {proof, vkey} pair to be recursively verified
525+
ivc.instantiate_stdlib_verification_queue(circuit, stdlib_verification_keys);
510526

511-
// Connect each {proof, vkey} pair from the constraint to the corresponding entry in the internal verification
512-
// queue. This ensures that the witnesses utlized in constraints generated based on acir are properly connected to
513-
// the constraints generated herein via the ivc scheme (e.g. recursive verifications).
527+
// Connect the proof/public_input witness indices from each constraint to the corresponding proof witnesses in the
528+
// internal verification queue. This ensures that the witnesses utlized in constraints generated based on acir are
529+
// properly connected to the constraints generated herein via the ivc scheme (e.g. recursive verifications).
514530
for (auto [constraint, queue_entry] :
515531
zip_view(constraint_system.ivc_recursion_constraints, ivc.stdlib_verification_queue)) {
516532

@@ -520,11 +536,9 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system,
520536
ASSERT(complete_proof_indices.size() == queue_entry.proof.size());
521537

522538
// Assert equality between the proof indices from the constraint data and those of the internal proof
523-
for (auto [proof_idx, proof_value] : zip_view(complete_proof_indices, queue_entry.proof)) {
539+
for (auto [proof_value, proof_idx] : zip_view(queue_entry.proof, complete_proof_indices)) {
524540
circuit.assert_equal(proof_value.get_witness_index(), proof_idx);
525541
}
526-
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1090): assert equality between the internal vkey
527-
// and the constaint vkey, or simply use the constraint vkey directly to construct the stdlib vkey used in IVC.
528542
}
529543

530544
// Complete the kernel circuit with all required recursive verifications, databus consistency checks etc.

cpp/src/barretenberg/ecc/fields/field_conversion.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ std::vector<bb::fr> convert_grumpkin_fr_to_bn254_frs(const grumpkin::fr& val);
9393
*/
9494
template <typename T> std::vector<bb::fr> convert_to_bn254_frs(const T& val)
9595
{
96-
if constexpr (IsAnyOf<T, bool, uint32_t, uint64_t, bb::fr>) {
96+
if constexpr (IsAnyOf<T, bool, uint32_t, uint64_t, size_t, bb::fr>) {
9797
std::vector<bb::fr> fr_vec{ val };
9898
return fr_vec;
9999
} else if constexpr (IsAnyOf<T, grumpkin::fr>) {

cpp/src/barretenberg/flavor/flavor.hpp

+19-25
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@ namespace bb {
9393
*/
9494
class PrecomputedEntitiesBase {
9595
public:
96+
bool operator==(const PrecomputedEntitiesBase& other) const = default;
9697
uint64_t circuit_size;
9798
uint64_t log_circuit_size;
9899
uint64_t num_public_inputs;
99-
CircuitType circuit_type; // TODO(#392)
100100
};
101101

102102
/**
@@ -158,6 +158,7 @@ class VerificationKey_ : public PrecomputedCommitments {
158158
AggregationObjectPubInputIndices recursive_proof_public_input_indices = {};
159159
uint64_t pub_inputs_offset = 0;
160160

161+
bool operator==(const VerificationKey_&) const = default;
161162
VerificationKey_() = default;
162163
VerificationKey_(const size_t circuit_size, const size_t num_public_inputs)
163164
{
@@ -173,32 +174,25 @@ class VerificationKey_ : public PrecomputedCommitments {
173174
*/
174175
std::vector<FF> to_field_elements()
175176
{
177+
using namespace bb::field_conversion;
178+
179+
auto serialize_to_field_buffer = [](const auto& input, std::vector<FF>& buffer) {
180+
std::vector<FF> input_fields = convert_to_bn254_frs(input);
181+
buffer.insert(buffer.end(), input_fields.begin(), input_fields.end());
182+
};
183+
176184
std::vector<FF> elements;
177-
std::vector<FF> circuit_size_elements = bb::field_conversion::convert_to_bn254_frs(this->circuit_size);
178-
elements.insert(elements.end(), circuit_size_elements.begin(), circuit_size_elements.end());
179-
// do the same for the rest of the fields
180-
std::vector<FF> num_public_inputs_elements =
181-
bb::field_conversion::convert_to_bn254_frs(this->num_public_inputs);
182-
elements.insert(elements.end(), num_public_inputs_elements.begin(), num_public_inputs_elements.end());
183-
std::vector<FF> pub_inputs_offset_elements =
184-
bb::field_conversion::convert_to_bn254_frs(this->pub_inputs_offset);
185-
elements.insert(elements.end(), pub_inputs_offset_elements.begin(), pub_inputs_offset_elements.end());
186-
187-
std::vector<FF> contains_recursive_proof_elements =
188-
bb::field_conversion::convert_to_bn254_frs(this->contains_recursive_proof);
189-
elements.insert(
190-
elements.end(), contains_recursive_proof_elements.begin(), contains_recursive_proof_elements.end());
191-
192-
std::vector<FF> recursive_proof_public_input_indices_elements =
193-
bb::field_conversion::convert_to_bn254_frs(this->recursive_proof_public_input_indices);
194-
elements.insert(elements.end(),
195-
recursive_proof_public_input_indices_elements.begin(),
196-
recursive_proof_public_input_indices_elements.end());
197-
198-
for (Commitment& comm : this->get_all()) {
199-
std::vector<FF> comm_elements = bb::field_conversion::convert_to_bn254_frs(comm);
200-
elements.insert(elements.end(), comm_elements.begin(), comm_elements.end());
185+
186+
serialize_to_field_buffer(this->circuit_size, elements);
187+
serialize_to_field_buffer(this->num_public_inputs, elements);
188+
serialize_to_field_buffer(this->pub_inputs_offset, elements);
189+
serialize_to_field_buffer(this->contains_recursive_proof, elements);
190+
serialize_to_field_buffer(this->recursive_proof_public_input_indices, elements);
191+
192+
for (Commitment& commitment : this->get_all()) {
193+
serialize_to_field_buffer(commitment, elements);
201194
}
195+
202196
return elements;
203197
}
204198

cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp

+18
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,22 @@ template <typename Builder, typename T> std::vector<fr<Builder>> convert_to_bn25
161161
}
162162
}
163163

164+
/**
165+
* @brief Deserialize an object of specified type from a buffer of field elements; update provided read count in place
166+
*
167+
* @tparam TargetType Type to reconstruct from buffer of field elements
168+
* @param builder
169+
* @param elements Buffer of field elements
170+
* @param num_frs_read Index at which to read into buffer
171+
*/
172+
template <typename TargetType, typename Builder>
173+
TargetType deserialize_from_frs(Builder& builder, std::span<fr<Builder>> elements, size_t& num_frs_read)
174+
{
175+
size_t num_frs = calc_num_bn254_frs<Builder, TargetType>();
176+
ASSERT(elements.size() >= num_frs_read + num_frs);
177+
TargetType result = convert_from_bn254_frs<Builder, TargetType>(builder, elements.subspan(num_frs_read, num_frs));
178+
num_frs_read += num_frs;
179+
return result;
180+
}
181+
164182
} // namespace bb::stdlib::field_conversion

cpp/src/barretenberg/stdlib/protogalaxy_verifier/recursive_decider_verification_key.hpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ template <IsRecursiveFlavor Flavor> class RecursiveDeciderVerificationKey_ {
2222
using Builder = typename Flavor::CircuitBuilder;
2323
using NativeFlavor = typename Flavor::NativeFlavor;
2424
using DeciderVerificationKey = bb::DeciderVerificationKey_<NativeFlavor>;
25+
using VerifierCommitmentKey = typename NativeFlavor::VerifierCommitmentKey;
2526

2627
Builder* builder;
2728

@@ -101,7 +102,9 @@ template <IsRecursiveFlavor Flavor> class RecursiveDeciderVerificationKey_ {
101102
{
102103
auto native_honk_vk = std::make_shared<NativeVerificationKey>(verification_key->circuit_size,
103104
verification_key->num_public_inputs);
104-
native_honk_vk->pcs_verification_key = verification_key->pcs_verification_key;
105+
native_honk_vk->pcs_verification_key = verification_key->pcs_verification_key == nullptr
106+
? std::make_shared<VerifierCommitmentKey>()
107+
: verification_key->pcs_verification_key;
105108
native_honk_vk->pub_inputs_offset = verification_key->pub_inputs_offset;
106109
native_honk_vk->contains_recursive_proof = verification_key->contains_recursive_proof;
107110
native_honk_vk->recursive_proof_public_input_indices = verification_key->recursive_proof_public_input_indices;

cpp/src/barretenberg/stdlib_circuit_builders/databus.hpp

+8
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ enum class BusId { CALLDATA, SECONDARY_CALLDATA, RETURNDATA };
6868
*
6969
*/
7070
struct DatabusPropagationData {
71+
bool operator==(const DatabusPropagationData&) const = default;
72+
7173
// Flags indicating whether the public inputs contain commitment(s) to app/kernel return data
7274
bool contains_app_return_data_commitment = false;
7375
bool contains_kernel_return_data_commitment = false;
@@ -80,6 +82,12 @@ struct DatabusPropagationData {
8082

8183
// Is this a kernel circuit (used to determine when databus consistency checks can be appended to a circuit in IVC)
8284
bool is_kernel = false;
85+
86+
MSGPACK_FIELDS(contains_app_return_data_commitment,
87+
contains_kernel_return_data_commitment,
88+
app_return_data_public_input_idx,
89+
kernel_return_data_public_input_idx,
90+
is_kernel);
8391
};
8492

8593
} // namespace bb
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include "barretenberg/common/serialize.hpp"
2+
#include "barretenberg/ecc/curves/bn254/fr.hpp"
3+
#include "barretenberg/numeric/uint256/uint256.hpp"
4+
#include "barretenberg/plonk_honk_shared/library/grand_product_delta.hpp"
5+
#include "barretenberg/relations/permutation_relation.hpp"
6+
#include "barretenberg/relations/relation_parameters.hpp"
7+
#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp"
8+
#include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp"
9+
#include "barretenberg/stdlib_circuit_builders/plookup_tables/types.hpp"
10+
#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp"
11+
#include "barretenberg/sumcheck/sumcheck_round.hpp"
12+
#include "barretenberg/ultra_honk/ultra_prover.hpp"
13+
#include "barretenberg/ultra_honk/ultra_verifier.hpp"
14+
15+
#include <gtest/gtest.h>
16+
17+
using namespace bb;
18+
19+
template <typename Flavor> class FlavorSerializationTests : public ::testing::Test {
20+
public:
21+
using Builder = typename Flavor::CircuitBuilder;
22+
using DeciderProvingKey = DeciderProvingKey_<Flavor>;
23+
using VerificationKey = typename Flavor::VerificationKey;
24+
25+
protected:
26+
static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); }
27+
};
28+
29+
using FlavorTypes = testing::Types<UltraFlavor, UltraKeccakFlavor, MegaFlavor>;
30+
TYPED_TEST_SUITE(FlavorSerializationTests, FlavorTypes);
31+
32+
// Test msgpack serialization/deserialization of verification keys
33+
TYPED_TEST(FlavorSerializationTests, VerificationKeySerialization)
34+
{
35+
using Builder = typename TestFixture::Builder;
36+
using DeciderProvingKey = typename TestFixture::DeciderProvingKey;
37+
using VerificationKey = typename TestFixture::VerificationKey;
38+
39+
Builder builder;
40+
41+
// Add some arbitrary arithmetic gates that utilize public inputs
42+
MockCircuits::add_arithmetic_gates_with_public_inputs(builder, /*num_gates=*/100);
43+
44+
auto proving_key = std::make_shared<DeciderProvingKey>(builder);
45+
VerificationKey original_vkey{ proving_key->proving_key };
46+
47+
// Set the pcs ptr to null since this will not be reconstructed correctly from buffer
48+
original_vkey.pcs_verification_key = nullptr;
49+
50+
// Populate some non-zero values in the databus_propagation_data to ensure its being handled
51+
if constexpr (IsMegaBuilder<Builder>) {
52+
original_vkey.databus_propagation_data.contains_app_return_data_commitment = 1;
53+
original_vkey.databus_propagation_data.contains_kernel_return_data_commitment = 1;
54+
original_vkey.databus_propagation_data.app_return_data_public_input_idx = 2;
55+
original_vkey.databus_propagation_data.kernel_return_data_public_input_idx = 4;
56+
original_vkey.databus_propagation_data.is_kernel = 1;
57+
}
58+
59+
// Serialize and deserialize the verification key
60+
std::vector<uint8_t> vkey_buffer = to_buffer(original_vkey);
61+
VerificationKey deserialized_vkey = from_buffer<VerificationKey>(vkey_buffer);
62+
63+
// Ensure the original is equal to the reconstructed
64+
EXPECT_EQ(original_vkey, deserialized_vkey);
65+
}

0 commit comments

Comments
 (0)