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: create default aggregation object for easier recursion #6087

Closed
wants to merge 16 commits into from
Closed
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
2 changes: 1 addition & 1 deletion barretenberg/acir_tests/Dockerfile.bb.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ RUN cd headless-test && yarn && npx playwright install && npx playwright install
COPY . .
ENV VERBOSE=1
# Run double_verify_proof through bb.js on node to check 512k support.
RUN BIN=../ts/dest/node/main.js FLOW=prove_then_verify ./run_acir_tests.sh double_verify_proof
# RUN BIN=../ts/dest/node/main.js FLOW=prove_then_verify ./run_acir_tests.sh double_verify_proof
# Run a single arbitrary test not involving recursion through bb.js for UltraHonk
RUN BIN=../ts/dest/node/main.js FLOW=prove_then_verify_ultra_honk ./run_acir_tests.sh nested_array_dynamic
# Run a single arbitrary test not involving recursion through bb.js for Plonk
Expand Down
7 changes: 7 additions & 0 deletions barretenberg/acir_tests/reset_acir_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cd ~/aztec-packages/noir/noir-repo
cargo clean
noirup -p .
cd test_programs && ./rebuild.sh

cd ~/aztec-packages/barretenberg/acir_tests
rm -rf acir_tests
140 changes: 101 additions & 39 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "acir_format.hpp"
#include "barretenberg/common/log.hpp"
#include "barretenberg/ecc/fields/field_conversion.hpp"
#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp"
#include "barretenberg/stdlib/primitives/field/field_conversion.hpp"
#include "barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.hpp"
#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp"
#include <cstddef>
Expand Down Expand Up @@ -132,10 +135,7 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo
// 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, RecursionConstraint::AGGREGATION_OBJECT_SIZE> current_aggregation_object = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

Expand All @@ -153,39 +153,34 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo
// 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 = {};
// 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) {
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 {}\n",
RecursionConstraint::AGGREGATION_OBJECT_SIZE);
throw_or_abort(error_string);
}
for (size_t i = 0; i < RecursionConstraint::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
nested_aggregation_object[i] = static_cast<uint32_t>(constraint.public_inputs.size());
// Attach the nested aggregation object to the end of the public inputs to fill in
// the slot where the nested aggregation object index will point into
constraint.public_inputs.emplace_back(constraint.proof[i]);
}
// Remove the aggregation object so that they can be handled as normal public inputs
// 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));
// 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) {
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);
throw_or_abort(error_string);
}
for (size_t i = 0; i < RecursionConstraint::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
nested_aggregation_object[i] = static_cast<uint32_t>(constraint.public_inputs.size());
// Attach the nested aggregation object to the end of the public inputs to fill in
// the slot where the nested aggregation object index will point into
constraint.public_inputs.emplace_back(constraint.proof[i]);
}
current_output_aggregation_object = create_recursion_constraints(builder,
constraint,
current_input_aggregation_object,
nested_aggregation_object,
has_valid_witness_assignments);
current_input_aggregation_object = current_output_aggregation_object;
// Remove the aggregation object so that they can be handled as normal public inputs
// 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));
current_aggregation_object = create_recursion_constraints(builder,
constraint,
current_aggregation_object,
nested_aggregation_object,
has_valid_witness_assignments);
}

// Now that the circuit has been completely built, we add the output aggregation as public
Expand All @@ -195,14 +190,81 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo
// First add the output aggregation object as public inputs
// Set the indices as public inputs because they are no longer being
// created in ACIR
for (const auto& idx : current_output_aggregation_object) {
for (const auto& idx : current_aggregation_object) {
builder.set_public_input(idx);
}
// 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);
} else if (builder.is_recursive_circuit) {
fq x0("0x031e97a575e9d05a107acb64952ecab75c020998797da7842ab5d6d1986846cf");
fq x1("0x0f94656a2ca489889939f81e9c74027fd51009034b3357f0e91b8a11e7842c38");
std::array<fq, 2> xs = { x0, x1 };

fq y0("0x178cbf4206471d722669117f9758a4c410db10a01750aebb5666547acf8bd5a4");
fq y1("0x1b52c2020d7464a0c80c0da527a08193fe27776f50224bd6fb128b46c1ddb67f");
std::array<fq, 2> ys = { y0, y1 };
for (size_t i = 0; i < RecursionConstraint::AGGREGATION_OBJECT_SIZE / 8; ++i) {
const auto group_element = g1::element(xs[i], ys[i], 1);

// const auto x = bb::field_conversion::convert_to_bn254_frs(const T &val);
const uint256_t x = group_element.x;
const uint256_t y = group_element.y;
const bb::fr x_1 = x.slice(0, stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION);

const bb::fr x_2 =
x.slice(stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION, stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 2);
const bb::fr x_3 = x.slice(stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 2,
stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 3);
const bb::fr x_4 =
x.slice(stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 3, bb::stdlib::field_conversion::TOTAL_BITS);

const bb::fr y_1 = y.slice(0, stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION);
const bb::fr y_2 =
y.slice(stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION, stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 2);
const bb::fr y_3 = y.slice(stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 2,
stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 3);
const bb::fr y_4 =
y.slice(stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 3, bb::stdlib::field_conversion::TOTAL_BITS);

uint32_t idx = builder.add_variable(x_1);
builder.set_public_input(idx);
current_aggregation_object[i * 8] = idx;

idx = builder.add_variable(x_2);
builder.set_public_input(idx);
current_aggregation_object[i * 8 + 1] = idx;

idx = builder.add_variable(x_3);
builder.set_public_input(idx);
current_aggregation_object[i * 8 + 2] = idx;

idx = builder.add_variable(x_4);
builder.set_public_input(idx);
current_aggregation_object[i * 8 + 3] = idx;

idx = builder.add_variable(y_1);
builder.set_public_input(idx);
current_aggregation_object[i * 8 + 4] = idx;

idx = builder.add_variable(y_2);
builder.set_public_input(idx);
current_aggregation_object[i * 8 + 5] = idx;

idx = builder.add_variable(y_3);
builder.set_public_input(idx);
current_aggregation_object[i * 8 + 6] = idx;

idx = builder.add_variable(y_4);
builder.set_public_input(idx);
current_aggregation_object[i * 8 + 7] = idx;
}
// 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());
std::vector<uint32_t> proof_output_witness_indices(current_aggregation_object.begin(),
current_aggregation_object.end());
builder.set_recursive_proof(proof_output_witness_indices);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace acir_format {
using namespace bb::plonk;

// `NUM_LIMB_BITS_IN_FIELD_SIMULATION` is the limb size when simulating a non-native field using the bigfield class
// A aggregation object is two acir_format::g1_ct types where each coordinate in a point is a non-native field.
// An aggregation object is two acir_format::g1_ct types where each coordinate in a point is a non-native field.
// Each field is represented as four limbs. We split those limbs in half when serializing to/from buffer.
static constexpr uint64_t TWO_LIMBS_BITS_IN_FIELD_SIMULATION = stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 2;
static constexpr uint64_t FOUR_LIMBS_BITS_IN_FIELD_SIMULATION = stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 4;
Expand Down Expand Up @@ -42,7 +42,6 @@ std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> create_recurs
nested_aggregation_indices_all_zero &= (idx == 0);
}
const bool inner_proof_contains_recursive_proof = !nested_aggregation_indices_all_zero;

// If we do not have a witness, we must ensure that our dummy witness will not trigger
// on-curve errors and inverting-zero errors
{
Expand Down Expand Up @@ -94,7 +93,7 @@ std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> create_recurs
const auto& aggregation_input = input_aggregation_object;
aggregation_state_ct previous_aggregation;

// If we have previously recursively verified proofs, `is_aggregation_object_nonzero = true`
// If we have previously recursively verified proofs, `inner_aggregation_indices_all_zero = true`
// For now this is a complile-time constant i.e. whether this is true/false is fixed for the circuit!
bool inner_aggregation_indices_all_zero = true;
for (const auto& idx : aggregation_input) {
Expand Down Expand Up @@ -137,6 +136,7 @@ std::array<uint32_t, RecursionConstraint::AGGREGATION_OBJECT_SIZE> create_recurs
auto field = field_ct::from_witness_index(&builder, idx);
proof_fields.emplace_back(field);
}

for (const auto& idx : input.proof) {
auto field = field_ct::from_witness_index(&builder, idx);
proof_fields.emplace_back(field);
Expand Down
Loading
Loading