Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add recursive aggregation object to proving/verification keys #6770

Merged
merged 10 commits into from
Jul 29, 2024
29 changes: 13 additions & 16 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,9 @@ void build_constraints(Builder& builder,
// TODO(maxim): input_aggregation_object to be non-zero.
// TODO(maxim): if not, we can add input_aggregation_object to the proof too for all recursive proofs
// TODO(maxim): This might be the case for proof trees where the proofs are created on different machines
std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> current_input_aggregation_object = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> current_output_aggregation_object = {
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> current_input_aggregation_object = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> current_output_aggregation_object = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

Expand All @@ -156,20 +155,19 @@ void build_constraints(Builder& builder,
// The user tells us they how they want these constants set by keeping the nested aggregation object
// attached to the proof as public inputs. As this is the only object that can prepended to the proof if the
// proof is above the expected size (with public inputs stripped)
std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> nested_aggregation_object = {};
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> nested_aggregation_object = {};
// If the proof has public inputs attached to it, we should handle setting the nested aggregation object
if (constraint.proof.size() > proof_size_no_pub_inputs) {
// The public inputs attached to a proof should match the aggregation object in size
if (constraint.proof.size() - proof_size_no_pub_inputs !=
RecursionConstraint::AGGREGATION_OBJECT_SIZE) {
if (constraint.proof.size() - proof_size_no_pub_inputs != bb::AGGREGATION_OBJECT_SIZE) {
auto error_string = format(
"Public inputs are always stripped from proofs unless we have a recursive proof.\n"
"Thus, public inputs attached to a proof must match the recursive aggregation object in size "
"which is ",
RecursionConstraint::AGGREGATION_OBJECT_SIZE);
bb::AGGREGATION_OBJECT_SIZE);
throw_or_abort(error_string);
}
for (size_t i = 0; i < RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) {
for (size_t i = 0; i < bb::AGGREGATION_OBJECT_SIZE; ++i) {
// Set the nested aggregation object indices to the current size of the public inputs
// This way we know that the nested aggregation object indices will always be the last
// indices of the public inputs
Expand All @@ -182,7 +180,7 @@ void build_constraints(Builder& builder,
// in they way taht the recursion constraint expects
constraint.proof.erase(constraint.proof.begin(),
constraint.proof.begin() +
static_cast<std::ptrdiff_t>(RecursionConstraint::AGGREGATION_OBJECT_SIZE));
static_cast<std::ptrdiff_t>(bb::AGGREGATION_OBJECT_SIZE));
}
current_output_aggregation_object = create_recursion_constraints(builder,
constraint,
Expand Down Expand Up @@ -224,18 +222,17 @@ void build_constraints(Builder& builder,
// These should not be set by the caller
// TODO(https://github.com/AztecProtocol/barretenberg/issues/996): this usage of all zeros is a hack and could
// use types or enums to properly fix.
std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> current_aggregation_object = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> current_aggregation_object = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };

// Add recursion constraints
for (auto constraint : constraint_system.honk_recursion_constraints) {
// A proof passed into the constraint should be stripped of its inner public inputs, but not the nested
// aggregation object itself. The verifier circuit requires that the indices to a nested proof aggregation
// state are a circuit constant. The user tells us they how they want these constants set by keeping the
// nested aggregation object attached to the proof as public inputs.
std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> nested_aggregation_object = {};
for (size_t i = 0; i < HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) {
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> nested_aggregation_object = {};
for (size_t i = 0; i < bb::AGGREGATION_OBJECT_SIZE; ++i) {
// Set the nested aggregation object indices to witness indices from the proof
nested_aggregation_object[i] =
static_cast<uint32_t>(constraint.proof[HonkRecursionConstraint::inner_public_input_offset + i]);
Expand All @@ -247,7 +244,7 @@ void build_constraints(Builder& builder,
constraint.proof.erase(constraint.proof.begin() + HonkRecursionConstraint::inner_public_input_offset,
constraint.proof.begin() +
static_cast<std::ptrdiff_t>(HonkRecursionConstraint::inner_public_input_offset +
HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE));
bb::AGGREGATION_OBJECT_SIZE));
current_aggregation_object = create_honk_recursion_constraints(builder,
constraint,
current_aggregation_object,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ using namespace bb;
using namespace bb::stdlib::recursion::honk;

std::array<bn254::Group, 2> agg_points_from_witness_indicies(
Builder& builder, const std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE>& obj_witness_indices)
Builder& builder, const std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE>& obj_witness_indices)
{
std::array<bn254::BaseField, 4> aggregation_elements;
for (size_t i = 0; i < 4; ++i) {
Expand Down Expand Up @@ -37,11 +37,11 @@ std::array<bn254::Group, 2> agg_points_from_witness_indicies(
* We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true,
* or we need non-witness data to be provided as metadata in the ACIR opcode
*/
std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> create_honk_recursion_constraints(
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> create_honk_recursion_constraints(
Builder& builder,
const HonkRecursionConstraint& input,
std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> input_aggregation_object,
std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> nested_aggregation_object,
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> input_aggregation_object,
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> nested_aggregation_object,
bool has_valid_witness_assignments)
{
using Flavor = UltraRecursiveFlavor_<Builder>;
Expand Down Expand Up @@ -147,10 +147,10 @@ std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> create_ho
// is important, like what the plonk recursion constraint does.

// We want to return an array, so just copy the vector into the array
ASSERT(result.proof_witness_indices.size() == HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE);
std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> resulting_output_aggregation_object;
ASSERT(result.proof_witness_indices.size() == bb::AGGREGATION_OBJECT_SIZE);
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> resulting_output_aggregation_object;
std::copy(result.proof_witness_indices.begin(),
result.proof_witness_indices.begin() + HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE,
result.proof_witness_indices.begin() + bb::AGGREGATION_OBJECT_SIZE,
resulting_output_aggregation_object.begin());

return resulting_output_aggregation_object;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ struct HonkRecursionConstraint {
friend bool operator==(HonkRecursionConstraint const& lhs, HonkRecursionConstraint const& rhs) = default;
};

std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> create_honk_recursion_constraints(
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> create_honk_recursion_constraints(
Builder& builder,
const HonkRecursionConstraint& input,
std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> input_aggregation_object,
std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> nested_aggregation_object,
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> input_aggregation_object,
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> nested_aggregation_object,
bool has_valid_witness_assignments = false);

} // namespace acir_format
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,13 @@ class AcirHonkRecursionConstraint : public ::testing::Test {
proof_witnesses.begin() + static_cast<std::ptrdiff_t>(inner_public_input_offset),
proof_witnesses.begin() +
static_cast<std::ptrdiff_t>(inner_public_input_offset + num_inner_public_inputs -
RecursionConstraint::AGGREGATION_OBJECT_SIZE));
bb::AGGREGATION_OBJECT_SIZE));

// We want to make sure that we do not remove the nested aggregation object.
proof_witnesses.erase(proof_witnesses.begin() + static_cast<std::ptrdiff_t>(inner_public_input_offset),
proof_witnesses.begin() +
static_cast<std::ptrdiff_t>(inner_public_input_offset + num_inner_public_inputs -
RecursionConstraint::AGGREGATION_OBJECT_SIZE));
bb::AGGREGATION_OBJECT_SIZE));

std::vector<bb::fr> key_witnesses = verification_key->to_field_elements();

Expand All @@ -170,9 +170,8 @@ class AcirHonkRecursionConstraint : public ::testing::Test {
// agg_obj_1, ..., agg_obj_15, rest of proof..., vkey_0, vkey_1, vkey_2, vkey_3...]
const uint32_t public_input_start_idx =
static_cast<uint32_t>(inner_public_input_offset + witness_offset); // points to public_input_0
const uint32_t proof_indices_start_idx =
static_cast<uint32_t>(public_input_start_idx + num_inner_public_inputs -
RecursionConstraint::AGGREGATION_OBJECT_SIZE); // points to agg_obj_0
const uint32_t proof_indices_start_idx = static_cast<uint32_t>(
public_input_start_idx + num_inner_public_inputs - bb::AGGREGATION_OBJECT_SIZE); // points to agg_obj_0
const uint32_t key_indices_start_idx =
static_cast<uint32_t>(proof_indices_start_idx + proof_witnesses.size() -
inner_public_input_offset); // would point to vkey_3 without the -
Expand All @@ -195,7 +194,7 @@ class AcirHonkRecursionConstraint : public ::testing::Test {
// We keep the nested aggregation object attached to the proof,
// thus we do not explicitly have to keep the public inputs while setting up the initial recursion
// constraint. They will later be attached as public inputs when creating the circuit.
for (size_t i = 0; i < num_inner_public_inputs - RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) {
for (size_t i = 0; i < num_inner_public_inputs - bb::AGGREGATION_OBJECT_SIZE; ++i) {
inner_public_inputs.push_back(static_cast<uint32_t>(i + public_input_start_idx));
}

Expand Down Expand Up @@ -234,7 +233,7 @@ class AcirHonkRecursionConstraint : public ::testing::Test {
//
// We once again have to check whether we have a nested proof, because if we do have one
// then we could get a segmentation fault as `inner_public_inputs` was never filled with values.
for (size_t i = 0; i < num_inner_public_inputs - RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) {
for (size_t i = 0; i < num_inner_public_inputs - bb::AGGREGATION_OBJECT_SIZE; ++i) {
witness[inner_public_inputs[i]] = inner_public_input_values[i];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ void generate_dummy_proof() {}
* We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true,
* or we need non-witness data to be provided as metadata in the ACIR opcode
*/
std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> create_recursion_constraints(
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> create_recursion_constraints(
Builder& builder,
const RecursionConstraint& input,
std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> input_aggregation_object,
std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> nested_aggregation_object,
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> input_aggregation_object,
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> nested_aggregation_object,
bool has_valid_witness_assignments)
{
const auto& nested_aggregation_indices = nested_aggregation_object;
Expand Down Expand Up @@ -162,10 +162,10 @@ std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> create_recurs
}

// We want to return an array, so just copy the vector into the array
ASSERT(result.proof_witness_indices.size() == RecursionConstraint::AGGREGATION_OBJECT_SIZE);
std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> resulting_output_aggregation_object;
ASSERT(result.proof_witness_indices.size() == bb::AGGREGATION_OBJECT_SIZE);
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> resulting_output_aggregation_object;
std::copy(result.proof_witness_indices.begin(),
result.proof_witness_indices.begin() + RecursionConstraint::AGGREGATION_OBJECT_SIZE,
result.proof_witness_indices.begin() + bb::AGGREGATION_OBJECT_SIZE,
resulting_output_aggregation_object.begin());

return resulting_output_aggregation_object;
Expand All @@ -187,12 +187,11 @@ std::vector<bb::fr> export_key_in_recursion_format(std::shared_ptr<verification_
output.emplace_back(vkey->circuit_size);
output.emplace_back(vkey->num_public_inputs);
output.emplace_back(vkey->contains_recursive_proof);
for (size_t i = 0; i < RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) {
if (vkey->recursive_proof_public_input_indices.size() > i) {
for (size_t i = 0; i < bb::AGGREGATION_OBJECT_SIZE; ++i) {
if (vkey->contains_recursive_proof) {
output.emplace_back(vkey->recursive_proof_public_input_indices[i]);
} else {
output.emplace_back(0);
ASSERT(vkey->contains_recursive_proof == false);
}
}
for (const auto& descriptor : vkey->polynomial_manifest.get()) {
Expand Down Expand Up @@ -240,7 +239,7 @@ std::vector<bb::fr> export_dummy_key_in_recursion_format(const PolynomialManifes
output.emplace_back(1); // num public inputs

output.emplace_back(contains_recursive_proof); // contains_recursive_proof
for (size_t i = 0; i < RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) {
for (size_t i = 0; i < bb::AGGREGATION_OBJECT_SIZE; ++i) {
output.emplace_back(0); // recursive_proof_public_input_indices
}

Expand Down Expand Up @@ -344,7 +343,7 @@ std::vector<bb::fr> export_dummy_transcript_in_recursion_format(const transcript
// When setting up the ACIR we emplace back the nested aggregation object
// fetched from the proof onto the public inputs. Thus, we can expect the
// nested aggregation object to always be at the end of the public inputs.
for (size_t k = 0; k < num_public_inputs - RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++k) {
for (size_t k = 0; k < num_public_inputs - bb::AGGREGATION_OBJECT_SIZE; ++k) {
fields.emplace_back(0);
}
for (size_t k = 0; k < RecursionConstraint::NUM_AGGREGATION_ELEMENTS; ++k) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ struct RecursionConstraint {
// An aggregation state is represented by two G1 affine elements. Each G1 point has
// two field element coordinates (x, y). Thus, four field elements
static constexpr size_t NUM_AGGREGATION_ELEMENTS = 4;
// Four limbs are used when simulating a non-native field using the bigfield class
static constexpr size_t AGGREGATION_OBJECT_SIZE =
NUM_AGGREGATION_ELEMENTS * NUM_QUOTIENT_PARTS; // 16 field elements
std::vector<uint32_t> key;
std::vector<uint32_t> proof;
std::vector<uint32_t> public_inputs;
Expand All @@ -57,11 +54,11 @@ struct RecursionConstraint {
friend bool operator==(RecursionConstraint const& lhs, RecursionConstraint const& rhs) = default;
};

std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> create_recursion_constraints(
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> create_recursion_constraints(
Builder& builder,
const RecursionConstraint& input,
std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> input_aggregation_object,
std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> nested_aggregation_object,
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> input_aggregation_object,
std::array<uint32_t, bb::AGGREGATION_OBJECT_SIZE> nested_aggregation_object,
bool has_valid_witness_assignments = false);

std::vector<bb::fr> export_key_in_recursion_format(std::shared_ptr<verification_key> const& vkey);
Expand Down
Loading
Loading