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
8 changes: 2 additions & 6 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -583,9 +583,7 @@ void prove_tube(const std::string& output_path)

// TODO(https://github.com/AztecProtocol/barretenberg/issues/911): These are pairing points extracted from a valid
// proof. This is a workaround because we can't represent the point at infinity in biggroup yet.
std::array<uint32_t, acir_format::HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> current_aggregation_object = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
AggregationObjectIndices current_aggregation_object = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
fq x0("0x031e97a575e9d05a107acb64952ecab75c020998797da7842ab5d6d1986846cf");
fq y0("0x178cbf4206471d722669117f9758a4c410db10a01750aebb5666547acf8bd5a4");
fq x1("0x0f94656a2ca489889939f81e9c74027fd51009034b3357f0e91b8a11e7842c38");
Expand All @@ -609,9 +607,7 @@ void prove_tube(const std::string& output_path)
}
// Make sure the verification key records the public input indices of the
// final recursion output.
std::vector<uint32_t> proof_output_witness_indices(current_aggregation_object.begin(),
current_aggregation_object.end());
builder->set_recursive_proof(proof_output_witness_indices);
builder->set_recursive_proof(current_aggregation_object);

info("num gates in tube circuit: ", builder->get_num_gates());
using Prover = UltraProver_<UltraFlavor>;
Expand Down
50 changes: 18 additions & 32 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,8 @@ 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 = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
AggregationObjectIndices current_input_aggregation_object = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
AggregationObjectIndices current_output_aggregation_object = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

// Get the size of proof with no public inputs prepended to it
// This is used while processing recursion constraints to determine whether
Expand All @@ -263,20 +259,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 = {};
AggregationObjectPubInputIndices 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 @@ -289,7 +284,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,
Expand All @@ -315,9 +310,7 @@ void build_constraints(Builder& builder,

// Make sure the verification key records the public input indices of the
// final recursion output.
std::vector<uint32_t> proof_output_witness_indices(current_output_aggregation_object.begin(),
current_output_aggregation_object.end());
builder.set_recursive_proof(proof_output_witness_indices);
builder.set_recursive_proof(current_output_aggregation_object);
}
}

Expand All @@ -334,20 +327,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
};
AggregationObjectIndices current_aggregation_object = {};

// Add recursion constraints

for (size_t i = 0; i < constraint_system.honk_recursion_constraints.size(); ++i) {
auto& constraint = constraint_system.honk_recursion_constraints.at(i);
// 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) {
// 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.
AggregationObjectIndices 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 @@ -359,7 +349,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 All @@ -382,9 +372,7 @@ void build_constraints(Builder& builder,

// Make sure the verification key records the public input indices of the
// final recursion output.
std::vector<uint32_t> proof_output_witness_indices(current_aggregation_object.begin(),
current_aggregation_object.end());
builder.set_recursive_proof(proof_output_witness_indices);
builder.set_recursive_proof(current_aggregation_object);
} else if (honk_recursion &&
builder.is_recursive_circuit) { // Set a default aggregation object if we don't have one.
// TODO(https://github.com/AztecProtocol/barretenberg/issues/911): These are pairing points extracted
Expand Down Expand Up @@ -414,9 +402,7 @@ void build_constraints(Builder& builder,
}
// Make sure the verification key records the public input indices of the
// final recursion output.
std::vector<uint32_t> proof_output_witness_indices(current_aggregation_object.begin(),
current_aggregation_object.end());
builder.set_recursive_proof(proof_output_witness_indices);
builder.set_recursive_proof(current_aggregation_object);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "honk_recursion_constraint.hpp"
#include "barretenberg/flavor/flavor.hpp"
#include "barretenberg/plonk_honk_shared/types/aggregation_object_type.hpp"
#include "barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.hpp"
#include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp"
#include "barretenberg/stdlib/primitives/bigfield/constants.hpp"
Expand All @@ -14,8 +15,8 @@ using field_ct = stdlib::field_t<Builder>;
using bn254 = stdlib::bn254<Builder>;
using aggregation_state_ct = bb::stdlib::recursion::aggregation_state<bn254>;

std::array<bn254::Group, 2> agg_points_from_witness_indicies(
Builder& builder, const std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE>& obj_witness_indices)
std::array<bn254::Group, 2> agg_points_from_witness_indicies(Builder& builder,
const AggregationObjectIndices& obj_witness_indices)
{
std::array<bn254::BaseField, 4> aggregation_elements;
for (size_t i = 0; i < 4; ++i) {
Expand Down Expand Up @@ -44,12 +45,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(
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,
bool has_valid_witness_assignments)
AggregationObjectIndices create_honk_recursion_constraints(Builder& builder,
const HonkRecursionConstraint& input,
AggregationObjectIndices input_aggregation_object,
AggregationObjectIndices nested_aggregation_object,
bool has_valid_witness_assignments)
{
using Flavor = UltraRecursiveFlavor_<Builder>;
using RecursiveVerificationKey = Flavor::VerificationKey;
Expand Down Expand Up @@ -139,7 +139,14 @@ std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> create_ho
builder.assert_equal(builder.add_variable(1 << log_circuit_size), key_fields[0].witness_index);
builder.assert_equal(builder.add_variable(input.public_inputs.size()), key_fields[1].witness_index);
builder.assert_equal(builder.add_variable(UltraFlavor::has_zero_row ? 1 : 0), key_fields[2].witness_index);
uint32_t offset = 3;
builder.assert_equal(builder.add_variable(0), key_fields[4].witness_index);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

updating the dummy vk to include the two new fields

uint32_t offset = 4;
// do the aggregation object next
for (size_t i = 0; i < bb::AGGREGATION_OBJECT_SIZE; i++) {
builder.assert_equal(builder.add_variable(0),
key_fields[offset].witness_index); // WORKTODO: check if 0 is fine here
lucasxia01 marked this conversation as resolved.
Show resolved Hide resolved
offset++;
}

for (size_t i = 0; i < Flavor::NUM_PRECOMPUTED_ENTITIES; ++i) {
auto comm = curve::BN254::AffineElement::one() * fr::random_element();
Expand Down Expand Up @@ -220,7 +227,7 @@ std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> create_ho
cur_aggregation_object.P0 = pairing_points[0]; // * recursion_separator;
cur_aggregation_object.P1 = pairing_points[1]; // * recursion_separator;

std::vector<uint32_t> proof_witness_indices = {
AggregationObjectIndices proof_witness_indices = {
cur_aggregation_object.P0.x.binary_basis_limbs[0].element.normalize().witness_index,
cur_aggregation_object.P0.x.binary_basis_limbs[1].element.normalize().witness_index,
cur_aggregation_object.P0.x.binary_basis_limbs[2].element.normalize().witness_index,
Expand All @@ -244,14 +251,8 @@ std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> create_ho
// TODO(https://github.com/AztecProtocol/barretenberg/issues/996): investigate whether assert_equal on public inputs
// 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;
std::copy(result.proof_witness_indices.begin(),
result.proof_witness_indices.begin() + HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE,
resulting_output_aggregation_object.begin());

return resulting_output_aggregation_object;
ASSERT(result.proof_witness_indices.size() == bb::AGGREGATION_OBJECT_SIZE);
return result.proof_witness_indices;
}

} // namespace acir_format
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,17 @@ struct HonkRecursionConstraint {
// In Honk, the proof starts with circuit_size, num_public_inputs, and pub_input_offset. We use this offset to keep
// track of where the public inputs start.
static constexpr size_t inner_public_input_offset = 3;
// 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 * fq_ct::NUM_LIMBS; // 16 field elements
std::vector<uint32_t> key;
std::vector<uint32_t> proof;
std::vector<uint32_t> public_inputs;

friend bool operator==(HonkRecursionConstraint const& lhs, HonkRecursionConstraint const& rhs) = default;
};

std::array<uint32_t, HonkRecursionConstraint::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,
bool has_valid_witness_assignments = false);
AggregationObjectIndices create_honk_recursion_constraints(Builder& builder,
const HonkRecursionConstraint& input,
AggregationObjectIndices input_aggregation_object,
AggregationObjectIndices nested_aggregation_object,
bool has_valid_witness_assignments = false);

} // namespace acir_format
Loading
Loading