Skip to content

Commit

Permalink
feat: adding aggregation to honk and rollup (#7466)
Browse files Browse the repository at this point in the history
Closes AztecProtocol/barretenberg#1044.

Re-enables (most) aggregation code that was disabled in the Honk-rollup work. Adds a hack to the tube circuit that adds a default aggregation object, which will be fixed in a followup PR (probably #7582).
  • Loading branch information
lucasxia01 authored Jul 25, 2024
1 parent fccc9e5 commit 2633aa9
Show file tree
Hide file tree
Showing 13 changed files with 117 additions and 97 deletions.
33 changes: 33 additions & 0 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,39 @@ void prove_tube(const std::string& output_path)
ClientIVC verifier{ builder, input };

verifier.verify(proof);

// 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
};
fq x0("0x031e97a575e9d05a107acb64952ecab75c020998797da7842ab5d6d1986846cf");
fq y0("0x178cbf4206471d722669117f9758a4c410db10a01750aebb5666547acf8bd5a4");
fq x1("0x0f94656a2ca489889939f81e9c74027fd51009034b3357f0e91b8a11e7842c38");
fq y1("0x1b52c2020d7464a0c80c0da527a08193fe27776f50224bd6fb128b46c1ddb67f");
std::vector<fq> aggregation_object_fq_values = { x0, y0, x1, y1 };
size_t agg_obj_indices_idx = 0;
for (fq val : aggregation_object_fq_values) {
const uint256_t x = val;
std::array<fr, acir_format::fq_ct::NUM_LIMBS> val_limbs = {
x.slice(0, acir_format::fq_ct::NUM_LIMB_BITS),
x.slice(acir_format::fq_ct::NUM_LIMB_BITS, acir_format::fq_ct::NUM_LIMB_BITS * 2),
x.slice(acir_format::fq_ct::NUM_LIMB_BITS * 2, acir_format::fq_ct::NUM_LIMB_BITS * 3),
x.slice(acir_format::fq_ct::NUM_LIMB_BITS * 3, stdlib::field_conversion::TOTAL_BITS)
};
for (size_t i = 0; i < acir_format::fq_ct::NUM_LIMBS; ++i) {
uint32_t idx = builder->add_variable(val_limbs[i]);
builder->set_public_input(idx);
current_aggregation_object[agg_obj_indices_idx] = idx;
agg_obj_indices_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);

info("num gates in tube circuit: ", builder->get_num_gates());
using Prover = UltraProver_<UltraFlavor>;
using Verifier = UltraVerifier_<UltraFlavor>;
Expand Down
110 changes: 52 additions & 58 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,21 +347,19 @@ void build_constraints(Builder& builder,
// 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 = {};
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
// for (size_t i = 0; i < HonkRecursionConstraint::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]);
// // Adding the nested aggregation object to the constraint's public inputs
// constraint.public_inputs.emplace_back(nested_aggregation_object[i]);
// }
for (size_t i = 0; i < HonkRecursionConstraint::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]);
// Adding the nested aggregation object to the constraint's public inputs
constraint.public_inputs.emplace_back(nested_aggregation_object[i]);
}
// Remove the aggregation object so that they can be handled as normal public inputs
// in they way that the recursion constraint expects
// 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));
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));
current_aggregation_object = create_honk_recursion_constraints(builder,
constraint,
current_aggregation_object,
Expand All @@ -378,52 +376,48 @@ void build_constraints(Builder& builder,
// First add the output aggregation object as public inputs
// Set the indices as public inputs because they are no longer being
// created in ACIR
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
// 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);
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 (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
// from a valid proof. This is a workaround because we can't represent the point at infinity in biggroup
// yet.
fq x0("0x031e97a575e9d05a107acb64952ecab75c020998797da7842ab5d6d1986846cf");
fq y0("0x178cbf4206471d722669117f9758a4c410db10a01750aebb5666547acf8bd5a4");

fq x1("0x0f94656a2ca489889939f81e9c74027fd51009034b3357f0e91b8a11e7842c38");
fq y1("0x1b52c2020d7464a0c80c0da527a08193fe27776f50224bd6fb128b46c1ddb67f");
std::vector<fq> aggregation_object_fq_values = { x0, y0, x1, y1 };
size_t agg_obj_indices_idx = 0;
for (fq val : aggregation_object_fq_values) {
const uint256_t x = val;
std::array<fr, fq_ct::NUM_LIMBS> val_limbs = {
x.slice(0, fq_ct::NUM_LIMB_BITS),
x.slice(fq_ct::NUM_LIMB_BITS, fq_ct::NUM_LIMB_BITS * 2),
x.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 3),
x.slice(fq_ct::NUM_LIMB_BITS * 3, stdlib::field_conversion::TOTAL_BITS)
};
for (size_t i = 0; i < fq_ct::NUM_LIMBS; ++i) {
uint32_t idx = builder.add_variable(val_limbs[i]);
builder.set_public_input(idx);
current_aggregation_object[agg_obj_indices_idx] = idx;
agg_obj_indices_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);
}
static_cast<void>(honk_recursion);
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
// 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
// from
// // a valid proof. This is a workaround because we can't represent the point at infinity in biggroup
// yet. fq x0("0x031e97a575e9d05a107acb64952ecab75c020998797da7842ab5d6d1986846cf"); fq
// y0("0x178cbf4206471d722669117f9758a4c410db10a01750aebb5666547acf8bd5a4");

// fq x1("0x0f94656a2ca489889939f81e9c74027fd51009034b3357f0e91b8a11e7842c38");
// fq y1("0x1b52c2020d7464a0c80c0da527a08193fe27776f50224bd6fb128b46c1ddb67f");
// std::vector<fq> aggregation_object_fq_values = { x0, y0, x1, y1 };
// size_t agg_obj_indices_idx = 0;
// for (fq val : aggregation_object_fq_values) {
// const uint256_t x = val;
// std::array<fr, fq_ct::NUM_LIMBS> val_limbs = {
// x.slice(0, fq_ct::NUM_LIMB_BITS),
// x.slice(fq_ct::NUM_LIMB_BITS, fq_ct::NUM_LIMB_BITS * 2),
// x.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 3),
// x.slice(fq_ct::NUM_LIMB_BITS * 3, stdlib::field_conversion::TOTAL_BITS)
// };
// for (size_t i = 0; i < fq_ct::NUM_LIMBS; ++i) {
// uint32_t idx = builder.add_variable(val_limbs[i]);
// builder.set_public_input(idx);
// current_aggregation_object[agg_obj_indices_idx] = idx;
// agg_obj_indices_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);
// }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> create_ho
using RecursiveVerificationKey = Flavor::VerificationKey;
using RecursiveVerifier = bb::stdlib::recursion::honk::UltraRecursiveVerifier_<Flavor>;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044) reinstate aggregation
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1059): Handle aggregation

static_cast<void>(input_aggregation_object);
static_cast<void>(nested_aggregation_object);
// Construct aggregation points from the nested aggregation witness indices
Expand Down Expand Up @@ -214,7 +215,7 @@ std::array<uint32_t, HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE> create_ho
std::array<typename Flavor::GroupElement, 2> pairing_points = verifier.verify_proof(proof_fields);

// Aggregate the current aggregation object with these pairing points from verify_proof
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1059): Handle aggregation
aggregation_state_ct cur_aggregation_object;
cur_aggregation_object.P0 = pairing_points[0]; // * recursion_separator;
cur_aggregation_object.P1 = pairing_points[1]; // * recursion_separator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,17 +158,17 @@ class AcirHonkRecursionConstraint : public ::testing::Test {
const size_t inner_public_input_offset = 3;
// - Save the public inputs so that we can set their values.
// - Then truncate them from the proof because the ACIR API expects proofs without public inputs
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
std::vector<fr> inner_public_input_values(
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));
static_cast<std::ptrdiff_t>(inner_public_input_offset + num_inner_public_inputs -
RecursionConstraint::AGGREGATION_OBJECT_SIZE));

// We want to make sure that we do not remove the nested aggregation object.
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
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));
static_cast<std::ptrdiff_t>(inner_public_input_offset + num_inner_public_inputs -
RecursionConstraint::AGGREGATION_OBJECT_SIZE));

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

Expand All @@ -179,9 +179,8 @@ class AcirHonkRecursionConstraint : public ::testing::Test {
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);
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
// - RecursionConstraint::AGGREGATION_OBJECT_SIZE); // points to agg_obj_0
static_cast<uint32_t>(public_input_start_idx + num_inner_public_inputs -
RecursionConstraint::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 @@ -204,8 +203,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.
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
for (size_t i = 0; i < num_inner_public_inputs; ++i) {
for (size_t i = 0; i < num_inner_public_inputs - RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) {
inner_public_inputs.push_back(static_cast<uint32_t>(i + public_input_start_idx));
}

Expand Down Expand Up @@ -244,8 +242,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.
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
for (size_t i = 0; i < num_inner_public_inputs; ++i) {
for (size_t i = 0; i < num_inner_public_inputs - RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) {
witness[inner_public_inputs[i]] = inner_public_input_values[i];
}

Expand Down
6 changes: 3 additions & 3 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,9 @@ library Constants {
uint256 internal constant LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP = 64;
uint256 internal constant NUM_MSGS_PER_BASE_PARITY = 4;
uint256 internal constant NUM_BASE_PARITY_PER_ROOT_PARITY = 4;
uint256 internal constant RECURSIVE_PROOF_LENGTH = 393;
uint256 internal constant NESTED_RECURSIVE_PROOF_LENGTH = 393;
uint256 internal constant TUBE_PROOF_LENGTH = 393;
uint256 internal constant RECURSIVE_PROOF_LENGTH = 409;
uint256 internal constant NESTED_RECURSIVE_PROOF_LENGTH = 409;
uint256 internal constant TUBE_PROOF_LENGTH = 409;
uint256 internal constant VERIFICATION_KEY_LENGTH_IN_FIELDS = 103;
uint256 internal constant SENDER_SELECTOR = 0;
uint256 internal constant ADDRESS_SELECTOR = 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,9 @@ global NUM_MSGS_PER_BASE_PARITY: u32 = 4;
// FIX: Sadly, writing this as above causes a type error in type_conversion.ts.
global NUM_BASE_PARITY_PER_ROOT_PARITY: u32 = 4;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
// Lengths of the different types of proofs in fields
global RECURSIVE_PROOF_LENGTH = 393;
global NESTED_RECURSIVE_PROOF_LENGTH = 393;
global RECURSIVE_PROOF_LENGTH = 409;
global NESTED_RECURSIVE_PROOF_LENGTH = 409;
global TUBE_PROOF_LENGTH = RECURSIVE_PROOF_LENGTH; // in the future these can differ

global VERIFICATION_KEY_LENGTH_IN_FIELDS = 103;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

// This circuit aggregates a single Honk proof from `assert_statement_recursive`.
global SIZE_OF_PROOF_IF_LOGN_IS_28 : u32 = 393;
global SIZE_OF_PROOF_IF_LOGN_IS_28 : u32 = 409;
fn main(
verification_key: [Field; 103],
// This is the proof without public inputs attached.
Expand Down
2 changes: 1 addition & 1 deletion noir/verify_honk_proof/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

// This circuit aggregates a single Honk proof from `assert_statement_recursive`.
global SIZE_OF_PROOF_IF_LOGN_IS_28 : u32 = 393;
global SIZE_OF_PROOF_IF_LOGN_IS_28 : u32 = 409;
fn main(
verification_key: [Field; 103],
// This is the proof without public inputs attached.
Expand Down
7 changes: 3 additions & 4 deletions yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from '@aztec/circuit-types';
import { type CircuitSimulationStats, type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats';
import {
AGGREGATION_OBJECT_LENGTH,
ClientIvcProof,
Fr,
type PrivateCircuitPublicInputs,
Expand Down Expand Up @@ -350,10 +351,8 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver {
]);
const json = JSON.parse(proofString);
const fields = json.map(Fr.fromString);
const numPublicInputs = vkData.numPublicInputs;
// const numPublicInputs =
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1044): Reinstate aggregation
// circuitType === 'App' ? vkData.numPublicInputs : vkData.numPublicInputs - AGGREGATION_OBJECT_LENGTH;
const numPublicInputs = vkData.numPublicInputs - AGGREGATION_OBJECT_LENGTH;

const fieldsWithoutPublicInputs = fields.slice(numPublicInputs);
this.log.info(
`Circuit type: ${circuitType}, complete proof length: ${fields.length}, without public inputs: ${fieldsWithoutPublicInputs.length}, num public inputs: ${numPublicInputs}, circuit size: ${vkData.circuitSize}, is recursive: ${vkData.isRecursive}, raw length: ${binaryProof.length}`,
Expand Down
Loading

0 comments on commit 2633aa9

Please sign in to comment.