Skip to content

Commit a204d1b

Browse files
authored
feat: modify HonkRecursionConstraint to handle IPA claims (#10469)
This PR was split out of the IPA-in-rollup integration work. It modifies the recursion constraint to be able to recursively verify UltraRollupFlavor properly by either forwarding or accumulating IPA claims.
1 parent 015ec0e commit a204d1b

9 files changed

+285
-73
lines changed

barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp

+34-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "barretenberg/common/throw_or_abort.hpp"
44
#include "barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp"
55
#include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp"
6+
#include "barretenberg/stdlib/primitives/curves/grumpkin.hpp"
67
#include "barretenberg/stdlib/primitives/field/field_conversion.hpp"
78
#include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp"
89
#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp"
@@ -350,13 +351,44 @@ PairingPointAccumulatorIndices process_honk_recursion_constraints(
350351
{
351352
// Add recursion constraints
352353
size_t idx = 0;
354+
std::vector<OpeningClaim<stdlib::grumpkin<Builder>>> nested_ipa_claims;
355+
std::vector<StdlibProof<Builder>> nested_ipa_proofs;
353356
for (auto& constraint : constraint_system.honk_recursion_constraints) {
354-
current_aggregation_object = create_honk_recursion_constraints(
355-
builder, constraint, current_aggregation_object, has_valid_witness_assignments);
357+
if (constraint.proof_type == HONK) {
358+
auto [next_aggregation_object, _ipa_claim, _ipa_proof] =
359+
create_honk_recursion_constraints<UltraRecursiveFlavor_<Builder>>(
360+
builder, constraint, current_aggregation_object, has_valid_witness_assignments);
361+
current_aggregation_object = next_aggregation_object;
362+
} else if (constraint.proof_type == ROLLUP_HONK || constraint.proof_type == ROLLUP_ROOT_HONK) {
363+
auto [next_aggregation_object, ipa_claim, ipa_proof] =
364+
create_honk_recursion_constraints<UltraRollupRecursiveFlavor_<Builder>>(
365+
builder, constraint, current_aggregation_object, has_valid_witness_assignments);
366+
current_aggregation_object = next_aggregation_object;
367+
368+
nested_ipa_claims.push_back(ipa_claim);
369+
nested_ipa_proofs.push_back(ipa_proof);
370+
} else {
371+
throw_or_abort("Invalid Honk proof type");
372+
}
356373

357374
gate_counter.track_diff(constraint_system.gates_per_opcode,
358375
constraint_system.original_opcode_indices.honk_recursion_constraints.at(idx++));
359376
}
377+
// Accumulate the claims
378+
if (nested_ipa_claims.size() == 2) {
379+
auto commitment_key = std::make_shared<CommitmentKey<curve::Grumpkin>>(1 << CONST_ECCVM_LOG_N);
380+
using StdlibTranscript = bb::stdlib::recursion::honk::UltraStdlibTranscript;
381+
382+
auto ipa_transcript_1 = std::make_shared<StdlibTranscript>(nested_ipa_proofs[0]);
383+
auto ipa_transcript_2 = std::make_shared<StdlibTranscript>(nested_ipa_proofs[1]);
384+
IPA<stdlib::grumpkin<Builder>>::accumulate(
385+
commitment_key, ipa_transcript_1, nested_ipa_claims[0], ipa_transcript_2, nested_ipa_claims[1]);
386+
} else if (nested_ipa_claims.size() == 1) {
387+
builder.add_ipa_claim(nested_ipa_claims[0].get_witness_indices());
388+
// This conversion looks suspicious but there's no need to make this an output of the circuit since its a proof
389+
// that will be checked anyway.
390+
builder.ipa_proof = convert_stdlib_proof_to_native(nested_ipa_proofs[0]);
391+
}
360392
return current_aggregation_object;
361393
}
362394

barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,8 @@ void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg,
634634
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1074): Eventually arg.proof_type will
635635
// be the only means for setting the proof type. use of honk_recursion flag in this context can go
636636
// away once all noir programs (e.g. protocol circuits) are updated to use the new pattern.
637-
if (honk_recursion && proof_type_in != HONK && proof_type_in != AVM) {
637+
if (honk_recursion && proof_type_in != HONK && proof_type_in != AVM && proof_type_in != ROLLUP_HONK &&
638+
proof_type_in != ROLLUP_ROOT_HONK) {
638639
proof_type_in = HONK;
639640
}
640641

@@ -653,6 +654,8 @@ void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg,
653654
af.original_opcode_indices.recursion_constraints.push_back(opcode_index);
654655
break;
655656
case HONK:
657+
case ROLLUP_HONK:
658+
case ROLLUP_ROOT_HONK:
656659
af.honk_recursion_constraints.push_back(c);
657660
af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index);
658661
break;

barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp

+82-14
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,18 @@ namespace {
3131
* @param key_fields
3232
* @param proof_fields
3333
*/
34+
template <typename Flavor>
3435
void create_dummy_vkey_and_proof(Builder& builder,
3536
size_t proof_size,
3637
size_t public_inputs_size,
3738
const std::vector<field_ct>& key_fields,
38-
const std::vector<field_ct>& proof_fields)
39+
const std::vector<field_ct>& proof_fields,
40+
bool is_rollup_honk_recursion_constraint)
3941
{
40-
using Flavor = UltraFlavor;
41-
4242
// Set vkey->circuit_size correctly based on the proof size
43-
size_t num_frs_comm = bb::field_conversion::calc_num_bn254_frs<Flavor::Commitment>();
44-
size_t num_frs_fr = bb::field_conversion::calc_num_bn254_frs<Flavor::FF>();
43+
size_t num_frs_comm = bb::field_conversion::calc_num_bn254_frs<typename Flavor::Commitment>();
44+
size_t num_frs_fr = bb::field_conversion::calc_num_bn254_frs<typename Flavor::FF>();
45+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1168): Add formula to flavor
4546
assert((proof_size - bb::HONK_PROOF_PUBLIC_INPUT_OFFSET - Flavor::NUM_WITNESS_ENTITIES * num_frs_comm -
4647
Flavor::NUM_ALL_ENTITIES * num_frs_fr - num_frs_comm) %
4748
(num_frs_comm + num_frs_fr * (Flavor::BATCHED_RELATION_PARTIAL_LENGTH + 1)) ==
@@ -61,13 +62,26 @@ void create_dummy_vkey_and_proof(Builder& builder,
6162
builder.assert_equal(builder.add_variable(1), key_fields[3].witness_index);
6263
uint32_t offset = 4;
6364
size_t num_inner_public_inputs = public_inputs_size - bb::PAIRING_POINT_ACCUMULATOR_SIZE;
65+
if (is_rollup_honk_recursion_constraint) {
66+
num_inner_public_inputs -= bb::IPA_CLAIM_SIZE;
67+
}
6468

65-
// We are making the assumption that the aggregation object are behind all the inner public inputs
69+
// We are making the assumption that the pairing point object is behind all the inner public inputs
6670
for (size_t i = 0; i < bb::PAIRING_POINT_ACCUMULATOR_SIZE; i++) {
6771
builder.assert_equal(builder.add_variable(num_inner_public_inputs + i), key_fields[offset].witness_index);
6872
offset++;
6973
}
7074

75+
if (is_rollup_honk_recursion_constraint) {
76+
// Key field is the whether the proof contains an aggregation object.
77+
builder.assert_equal(builder.add_variable(1), key_fields[offset++].witness_index);
78+
// We are making the assumption that the IPA claim is behind the inner public inputs and pairing point object
79+
for (size_t i = 0; i < bb::IPA_CLAIM_SIZE; i++) {
80+
builder.assert_equal(builder.add_variable(num_inner_public_inputs + i), key_fields[offset].witness_index);
81+
offset++;
82+
}
83+
}
84+
7185
for (size_t i = 0; i < Flavor::NUM_PRECOMPUTED_ENTITIES; ++i) {
7286
auto comm = curve::BN254::AffineElement::one() * fr::random_element();
7387
auto frs = field_conversion::convert_to_bn254_frs(comm);
@@ -96,6 +110,14 @@ void create_dummy_vkey_and_proof(Builder& builder,
96110
offset++;
97111
}
98112

113+
if (is_rollup_honk_recursion_constraint) {
114+
IPAClaimIndices ipa_claim; // WORKTODO: initialize this to something?
115+
for (auto idx : ipa_claim) {
116+
builder.assert_equal(idx, proof_fields[offset].witness_index);
117+
offset++;
118+
}
119+
}
120+
99121
// first 8 witness commitments
100122
for (size_t i = 0; i < Flavor::NUM_WITNESS_ENTITIES; i++) {
101123
auto comm = curve::BN254::AffineElement::one() * fr::random_element();
@@ -107,7 +129,8 @@ void create_dummy_vkey_and_proof(Builder& builder,
107129
offset += 4;
108130
}
109131

110-
// now the univariates, which can just be 0s (8*CONST_PROOF_SIZE_LOG_N Frs, where 8 is the maximum relation degree)
132+
// now the univariates, which can just be 0s (8*CONST_PROOF_SIZE_LOG_N Frs, where 8 is the maximum relation
133+
// degree)
111134
for (size_t i = 0; i < CONST_PROOF_SIZE_LOG_N * Flavor::BATCHED_RELATION_PARTIAL_LENGTH; i++) {
112135
builder.assert_equal(builder.add_variable(fr::random_element()), proof_fields[offset].witness_index);
113136
offset++;
@@ -162,17 +185,20 @@ void create_dummy_vkey_and_proof(Builder& builder,
162185
* We would either need a separate ACIR opcode where inner_proof_contains_pairing_point_accumulator = true,
163186
* or we need non-witness data to be provided as metadata in the ACIR opcode
164187
*/
165-
PairingPointAccumulatorIndices create_honk_recursion_constraints(
188+
189+
template <typename Flavor>
190+
HonkRecursionConstraintOutput create_honk_recursion_constraints(
166191
Builder& builder,
167192
const RecursionConstraint& input,
168193
PairingPointAccumulatorIndices input_aggregation_object_indices,
169194
bool has_valid_witness_assignments)
170195
{
171-
using Flavor = UltraRecursiveFlavor_<Builder>;
172196
using RecursiveVerificationKey = Flavor::VerificationKey;
173197
using RecursiveVerifier = bb::stdlib::recursion::honk::UltraRecursiveVerifier_<Flavor>;
174198

175-
ASSERT(input.proof_type == HONK);
199+
bool is_rollup_honk_recursion_constraint =
200+
(input.proof_type == ROLLUP_HONK || input.proof_type == ROLLUP_ROOT_HONK);
201+
ASSERT(input.proof_type == HONK || is_rollup_honk_recursion_constraint);
176202

177203
// Construct an in-circuit representation of the verification key.
178204
// For now, the v-key is a circuit constant and is fixed for the circuit.
@@ -200,21 +226,63 @@ PairingPointAccumulatorIndices create_honk_recursion_constraints(
200226
// In the constraint, the agg object public inputs are still contained in the proof. To get the 'raw' size of
201227
// the proof and public_inputs we subtract and add the corresponding amount from the respective sizes.
202228
size_t size_of_proof_with_no_pub_inputs = input.proof.size() - bb::PAIRING_POINT_ACCUMULATOR_SIZE;
229+
if (is_rollup_honk_recursion_constraint) {
230+
size_of_proof_with_no_pub_inputs -= bb::IPA_CLAIM_SIZE;
231+
}
203232
size_t total_num_public_inputs = input.public_inputs.size() + bb::PAIRING_POINT_ACCUMULATOR_SIZE;
204-
create_dummy_vkey_and_proof(
205-
builder, size_of_proof_with_no_pub_inputs, total_num_public_inputs, key_fields, proof_fields);
233+
if (is_rollup_honk_recursion_constraint) {
234+
total_num_public_inputs += bb::IPA_CLAIM_SIZE;
235+
}
236+
create_dummy_vkey_and_proof<typename Flavor::NativeFlavor>(builder,
237+
size_of_proof_with_no_pub_inputs,
238+
total_num_public_inputs,
239+
key_fields,
240+
proof_fields,
241+
is_rollup_honk_recursion_constraint);
206242
}
207243

208244
// Recursively verify the proof
209245
auto vkey = std::make_shared<RecursiveVerificationKey>(builder, key_fields);
210246
RecursiveVerifier verifier(&builder, vkey);
211247
aggregation_state_ct input_agg_obj = bb::stdlib::recursion::convert_witness_indices_to_agg_obj<Builder, bn254>(
212248
builder, input_aggregation_object_indices);
213-
UltraRecursiveVerifierOutput<Flavor> output = verifier.verify_proof(proof_fields, input_agg_obj);
249+
std::vector<field_ct> honk_proof = proof_fields;
250+
HonkRecursionConstraintOutput output;
251+
if (is_rollup_honk_recursion_constraint) {
252+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1168): Add formula to flavor
253+
const size_t HONK_PROOF_LENGTH = 473;
254+
// The extra calculation is for the IPA proof length.
255+
ASSERT(input.proof.size() == HONK_PROOF_LENGTH + 1 + 4 * (CONST_ECCVM_LOG_N) + 2 + 2);
256+
ASSERT(proof_fields.size() == HONK_PROOF_LENGTH + 65 + input.public_inputs.size());
257+
// split out the ipa proof
258+
const std::ptrdiff_t honk_proof_with_pub_inputs_length =
259+
static_cast<std::ptrdiff_t>(HONK_PROOF_LENGTH + input.public_inputs.size());
260+
output.ipa_proof =
261+
StdlibProof<Builder>(honk_proof.begin() + honk_proof_with_pub_inputs_length, honk_proof.end());
262+
honk_proof = StdlibProof<Builder>(honk_proof.begin(), honk_proof.end() + honk_proof_with_pub_inputs_length);
263+
}
264+
UltraRecursiveVerifierOutput<Flavor> verifier_output = verifier.verify_proof(honk_proof, input_agg_obj);
214265
// TODO(https://github.com/AztecProtocol/barretenberg/issues/996): investigate whether assert_equal on public inputs
215266
// is important, like what the plonk recursion constraint does.
216267

217-
return output.agg_obj.get_witness_indices();
268+
output.agg_obj_indices = verifier_output.agg_obj.get_witness_indices();
269+
if (is_rollup_honk_recursion_constraint) {
270+
ASSERT(HasIPAAccumulator<Flavor>);
271+
output.ipa_claim = verifier_output.ipa_opening_claim;
272+
}
273+
return output;
218274
}
219275

276+
template HonkRecursionConstraintOutput create_honk_recursion_constraints<UltraRecursiveFlavor_<Builder>>(
277+
Builder& builder,
278+
const RecursionConstraint& input,
279+
PairingPointAccumulatorIndices input_aggregation_object_indices,
280+
bool has_valid_witness_assignments);
281+
282+
template HonkRecursionConstraintOutput create_honk_recursion_constraints<UltraRollupRecursiveFlavor_<Builder>>(
283+
Builder& builder,
284+
const RecursionConstraint& input,
285+
PairingPointAccumulatorIndices input_aggregation_object_indices,
286+
bool has_valid_witness_assignments);
287+
220288
} // namespace acir_format
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
#pragma once
2+
#include "barretenberg/commitment_schemes/claim.hpp"
23
#include "barretenberg/dsl/acir_format/recursion_constraint.hpp"
4+
#include "barretenberg/honk/proof_system/types/proof.hpp"
35
#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp"
6+
#include "barretenberg/stdlib/primitives/curves/grumpkin.hpp"
47
#include <vector>
58

69
namespace acir_format {
710
using Builder = bb::UltraCircuitBuilder;
811

912
using namespace bb;
1013

11-
PairingPointAccumulatorIndices create_honk_recursion_constraints(
12-
Builder& builder,
13-
const RecursionConstraint& input,
14-
PairingPointAccumulatorIndices input_aggregation_object,
15-
bool has_valid_witness_assignments = false);
14+
struct HonkRecursionConstraintOutput {
15+
PairingPointAccumulatorIndices agg_obj_indices;
16+
OpeningClaim<stdlib::grumpkin<Builder>> ipa_claim;
17+
StdlibProof<Builder> ipa_proof;
18+
};
19+
20+
template <typename Flavor>
21+
HonkRecursionConstraintOutput create_honk_recursion_constraints(Builder& builder,
22+
const RecursionConstraint& input,
23+
PairingPointAccumulatorIndices input_aggregation_object,
24+
bool has_valid_witness_assignments = false);
1625

1726
} // namespace acir_format

0 commit comments

Comments
 (0)