Skip to content

Commit

Permalink
feat: update honk ultra_recursive_verifier to do aggregation (#7582)
Browse files Browse the repository at this point in the history
Resolves AztecProtocol/barretenberg#1059.

This work updates the recursive verifier to now take in an pairing
aggregation object and also produce one. This object aggregates with the
nested aggregation object (parsed through the public inputs of the inner
proof) and the pairing points produced by KZG. This change is
**inefficient** as every circuit will now also produce an aggregation
object, regardless of whether the circuit does any recursive
aggregation. This adds gates presumably through only the
public_input_delta, but this needs to be double checked.
This also means that for circuits that do k recursive aggregations, we
will be doing 2*k biggroup mul-adds for aggregation, because every inner
proof contains an agg obj. Each mul-add was seemingly costing 130k
gates, so this likely needs to be optimized heavily.

Note that the native honk verifier was **not** updated in this PR. This
will be updated in a future PR, and will likely force many tests (any
test that has a native verify call) to call
`builder.add_recursive_proof(default_agg_obj)`.
  • Loading branch information
lucasxia01 authored Aug 13, 2024
1 parent 0d0646d commit a96a5ad
Show file tree
Hide file tree
Showing 22 changed files with 421 additions and 363 deletions.
4 changes: 4 additions & 0 deletions barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ void AztecIVC::execute_accumulation_prover(ClientCircuit& circuit,
MergeProof merge_proof = goblin.prove_merge(circuit);
merge_verification_queue.emplace_back(merge_proof);

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): Do proper aggregation with merge recursive
// verifier.
circuit.add_recursive_proof(stdlib::recursion::init_default_agg_obj_indices<ClientCircuit>(circuit));

// Construct the prover instance for circuit
auto prover_instance = std::make_shared<ProverInstance>(circuit, trace_structure);

Expand Down
37 changes: 9 additions & 28 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "barretenberg/dsl/acir_proofs/honk_contract.hpp"
#include "barretenberg/honk/proof_system/types/proof.hpp"
#include "barretenberg/plonk/proof_system/proving_key/serialize.hpp"
#include "barretenberg/plonk_honk_shared/types/aggregation_object_type.hpp"
#include "barretenberg/serialize/cbind.hpp"
#include "barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.hpp"
#include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp"
Expand Down Expand Up @@ -573,41 +574,20 @@ void prove_tube(const std::string& output_path)
// these public inputs by turning proof into witnesses and call
// set_public on each witness
auto num_public_inputs = static_cast<size_t>(static_cast<uint256_t>(proof.folding_proof[1]));
for (size_t i = 0; i < num_public_inputs; i++) {
for (size_t i = 0; i < num_public_inputs - bb::AGGREGATION_OBJECT_SIZE; i++) {
// We offset 3
builder->add_public_variable(proof.folding_proof[i + 3]);
}
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.
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");
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.
builder->set_recursive_proof(current_aggregation_object);
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): Add aggregation to goblin recursive verifiers.
// This is currently just setting the aggregation object to the default one.
AggregationObjectIndices current_aggregation_object =
stdlib::recursion::init_default_agg_obj_indices<Builder>(*builder);

builder->add_recursive_proof(current_aggregation_object);

info("num gates in tube circuit: ", builder->get_num_gates());
using Prover = UltraProver_<UltraFlavor>;
Expand Down Expand Up @@ -1346,6 +1326,7 @@ int main(int argc, char* argv[])
}

std::string command = args[0];
vinfo("bb command is: ", command);
std::string bytecode_path = get_option(args, "-b", "./target/program.json");
std::string witness_path = get_option(args, "-w", "./target/witness.gz");
std::string proof_path = get_option(args, "-p", "./proofs/proof");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ BENCHMARK_TEMPLATE_F(SimulatorFixture, GoblinSimulated, bb::MegaRecursiveFlavor_
for (auto _ : state) {
CircuitSimulator simulator;
SimulatingVerifier ultra_verifier{ &simulator, verifier_input.verification_key };
ultra_verifier.verify_proof((verifier_input.proof));
ultra_verifier.verify_proof(verifier_input.proof,
stdlib::recursion::init_default_aggregation_state<
CircuitSimulator,
bb::MegaRecursiveFlavor_<bb::CircuitSimulatorBN254>::Curve>(simulator));
}
}

Expand All @@ -121,7 +124,10 @@ BENCHMARK_TEMPLATE_F(SimulatorFixture, UltraSimulated, bb::UltraRecursiveFlavor_
for (auto _ : state) {
CircuitSimulator simulator;
SimulatingVerifier ultra_verifier{ &simulator, verifier_input.verification_key };
ultra_verifier.verify_proof((verifier_input.proof));
ultra_verifier.verify_proof(verifier_input.proof,
stdlib::recursion::init_default_aggregation_state<
CircuitSimulator,
bb::UltraRecursiveFlavor_<bb::CircuitSimulatorBN254>::Curve>(simulator));
}
}

Expand Down
4 changes: 4 additions & 0 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr<Verific
// Construct a merge proof (and add a recursive merge verifier to the circuit if a previous merge proof exists)
goblin.merge(circuit);

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): Do proper aggregation with merge recursive
// verifier.
circuit.add_recursive_proof(stdlib::recursion::init_default_agg_obj_indices<ClientCircuit>(circuit));

// Construct the prover instance for circuit
prover_instance = std::make_shared<ProverInstance>(circuit, trace_structure);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class ClientIVCTests : public ::testing::Test {
* polynomials will bump size to next power of 2)
*
*/
static Builder create_mock_circuit(ClientIVC& ivc, size_t log2_num_gates = 15)
static Builder create_mock_circuit(ClientIVC& ivc, size_t log2_num_gates = 16)
{
Builder circuit{ ivc.goblin.op_queue };
MockCircuits::construct_arithmetic_circuit(circuit, log2_num_gates);
Expand Down
52 changes: 10 additions & 42 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "acir_format.hpp"
#include "barretenberg/common/log.hpp"
#include "barretenberg/common/throw_or_abort.hpp"
#include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp"
#include "barretenberg/stdlib/primitives/field/field_conversion.hpp"
#include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp"
#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp"
Expand Down Expand Up @@ -322,12 +323,8 @@ void build_constraints(Builder& builder,
info("WARNING: this circuit contains honk_recursion_constraints!");
}
} else {
// These are set and modified whenever we encounter a recursion opcode
//
// 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.
AggregationObjectIndices current_aggregation_object = {};
AggregationObjectIndices current_aggregation_object =
stdlib::recursion::init_default_agg_obj_indices<Builder>(builder);

// Add recursion constraints
for (size_t i = 0; i < constraint_system.honk_recursion_constraints.size(); ++i) {
Expand All @@ -336,25 +333,19 @@ void build_constraints(Builder& builder,
// 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]);
// Adding the nested aggregation object to the constraint's public inputs
constraint.public_inputs.emplace_back(nested_aggregation_object[i]);
constraint.public_inputs.emplace_back(
constraint.proof[HonkRecursionConstraint::inner_public_input_offset + 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 +
bb::AGGREGATION_OBJECT_SIZE));
current_aggregation_object = create_honk_recursion_constraints(builder,
constraint,
current_aggregation_object,
nested_aggregation_object,
has_valid_witness_assignments);
current_aggregation_object = create_honk_recursion_constraints(
builder, constraint, current_aggregation_object, has_valid_witness_assignments);
track_gate_diff(constraint_system.gates_per_opcode,
constraint_system.original_opcode_indices.honk_recursion_constraints.at(i));
}
Expand All @@ -375,34 +366,11 @@ void build_constraints(Builder& builder,
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
// 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++;
}
}
AggregationObjectIndices current_aggregation_object =
stdlib::recursion::init_default_agg_obj_indices<Builder>(builder);
// Make sure the verification key records the public input indices of the
// final recursion output.
builder.set_recursive_proof(current_aggregation_object);
builder.add_recursive_proof(current_aggregation_object);
}
}
}
Expand Down
Loading

0 comments on commit a96a5ad

Please sign in to comment.