Skip to content

Commit

Permalink
feat: modify HonkRecursionConstraint to handle IPA claims (#10469)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lucasxia01 authored Dec 9, 2024
1 parent 015ec0e commit a204d1b
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 73 deletions.
36 changes: 34 additions & 2 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "barretenberg/common/throw_or_abort.hpp"
#include "barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp"
#include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp"
#include "barretenberg/stdlib/primitives/curves/grumpkin.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 @@ -350,13 +351,44 @@ PairingPointAccumulatorIndices process_honk_recursion_constraints(
{
// Add recursion constraints
size_t idx = 0;
std::vector<OpeningClaim<stdlib::grumpkin<Builder>>> nested_ipa_claims;
std::vector<StdlibProof<Builder>> nested_ipa_proofs;
for (auto& constraint : constraint_system.honk_recursion_constraints) {
current_aggregation_object = create_honk_recursion_constraints(
builder, constraint, current_aggregation_object, has_valid_witness_assignments);
if (constraint.proof_type == HONK) {
auto [next_aggregation_object, _ipa_claim, _ipa_proof] =
create_honk_recursion_constraints<UltraRecursiveFlavor_<Builder>>(
builder, constraint, current_aggregation_object, has_valid_witness_assignments);
current_aggregation_object = next_aggregation_object;
} else if (constraint.proof_type == ROLLUP_HONK || constraint.proof_type == ROLLUP_ROOT_HONK) {
auto [next_aggregation_object, ipa_claim, ipa_proof] =
create_honk_recursion_constraints<UltraRollupRecursiveFlavor_<Builder>>(
builder, constraint, current_aggregation_object, has_valid_witness_assignments);
current_aggregation_object = next_aggregation_object;

nested_ipa_claims.push_back(ipa_claim);
nested_ipa_proofs.push_back(ipa_proof);
} else {
throw_or_abort("Invalid Honk proof type");
}

gate_counter.track_diff(constraint_system.gates_per_opcode,
constraint_system.original_opcode_indices.honk_recursion_constraints.at(idx++));
}
// Accumulate the claims
if (nested_ipa_claims.size() == 2) {
auto commitment_key = std::make_shared<CommitmentKey<curve::Grumpkin>>(1 << CONST_ECCVM_LOG_N);
using StdlibTranscript = bb::stdlib::recursion::honk::UltraStdlibTranscript;

auto ipa_transcript_1 = std::make_shared<StdlibTranscript>(nested_ipa_proofs[0]);
auto ipa_transcript_2 = std::make_shared<StdlibTranscript>(nested_ipa_proofs[1]);
IPA<stdlib::grumpkin<Builder>>::accumulate(
commitment_key, ipa_transcript_1, nested_ipa_claims[0], ipa_transcript_2, nested_ipa_claims[1]);
} else if (nested_ipa_claims.size() == 1) {
builder.add_ipa_claim(nested_ipa_claims[0].get_witness_indices());
// This conversion looks suspicious but there's no need to make this an output of the circuit since its a proof
// that will be checked anyway.
builder.ipa_proof = convert_stdlib_proof_to_native(nested_ipa_proofs[0]);
}
return current_aggregation_object;
}

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

Expand All @@ -653,6 +654,8 @@ void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg,
af.original_opcode_indices.recursion_constraints.push_back(opcode_index);
break;
case HONK:
case ROLLUP_HONK:
case ROLLUP_ROOT_HONK:
af.honk_recursion_constraints.push_back(c);
af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,18 @@ namespace {
* @param key_fields
* @param proof_fields
*/
template <typename Flavor>
void create_dummy_vkey_and_proof(Builder& builder,
size_t proof_size,
size_t public_inputs_size,
const std::vector<field_ct>& key_fields,
const std::vector<field_ct>& proof_fields)
const std::vector<field_ct>& proof_fields,
bool is_rollup_honk_recursion_constraint)
{
using Flavor = UltraFlavor;

// Set vkey->circuit_size correctly based on the proof size
size_t num_frs_comm = bb::field_conversion::calc_num_bn254_frs<Flavor::Commitment>();
size_t num_frs_fr = bb::field_conversion::calc_num_bn254_frs<Flavor::FF>();
size_t num_frs_comm = bb::field_conversion::calc_num_bn254_frs<typename Flavor::Commitment>();
size_t num_frs_fr = bb::field_conversion::calc_num_bn254_frs<typename Flavor::FF>();
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1168): Add formula to flavor
assert((proof_size - bb::HONK_PROOF_PUBLIC_INPUT_OFFSET - Flavor::NUM_WITNESS_ENTITIES * num_frs_comm -
Flavor::NUM_ALL_ENTITIES * num_frs_fr - num_frs_comm) %
(num_frs_comm + num_frs_fr * (Flavor::BATCHED_RELATION_PARTIAL_LENGTH + 1)) ==
Expand All @@ -61,13 +62,26 @@ void create_dummy_vkey_and_proof(Builder& builder,
builder.assert_equal(builder.add_variable(1), key_fields[3].witness_index);
uint32_t offset = 4;
size_t num_inner_public_inputs = public_inputs_size - bb::PAIRING_POINT_ACCUMULATOR_SIZE;
if (is_rollup_honk_recursion_constraint) {
num_inner_public_inputs -= bb::IPA_CLAIM_SIZE;
}

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

if (is_rollup_honk_recursion_constraint) {
// Key field is the whether the proof contains an aggregation object.
builder.assert_equal(builder.add_variable(1), key_fields[offset++].witness_index);
// We are making the assumption that the IPA claim is behind the inner public inputs and pairing point object
for (size_t i = 0; i < bb::IPA_CLAIM_SIZE; i++) {
builder.assert_equal(builder.add_variable(num_inner_public_inputs + i), key_fields[offset].witness_index);
offset++;
}
}

for (size_t i = 0; i < Flavor::NUM_PRECOMPUTED_ENTITIES; ++i) {
auto comm = curve::BN254::AffineElement::one() * fr::random_element();
auto frs = field_conversion::convert_to_bn254_frs(comm);
Expand Down Expand Up @@ -96,6 +110,14 @@ void create_dummy_vkey_and_proof(Builder& builder,
offset++;
}

if (is_rollup_honk_recursion_constraint) {
IPAClaimIndices ipa_claim; // WORKTODO: initialize this to something?
for (auto idx : ipa_claim) {
builder.assert_equal(idx, proof_fields[offset].witness_index);
offset++;
}
}

// first 8 witness commitments
for (size_t i = 0; i < Flavor::NUM_WITNESS_ENTITIES; i++) {
auto comm = curve::BN254::AffineElement::one() * fr::random_element();
Expand All @@ -107,7 +129,8 @@ void create_dummy_vkey_and_proof(Builder& builder,
offset += 4;
}

// now the univariates, which can just be 0s (8*CONST_PROOF_SIZE_LOG_N Frs, where 8 is the maximum relation degree)
// now the univariates, which can just be 0s (8*CONST_PROOF_SIZE_LOG_N Frs, where 8 is the maximum relation
// degree)
for (size_t i = 0; i < CONST_PROOF_SIZE_LOG_N * Flavor::BATCHED_RELATION_PARTIAL_LENGTH; i++) {
builder.assert_equal(builder.add_variable(fr::random_element()), proof_fields[offset].witness_index);
offset++;
Expand Down Expand Up @@ -162,17 +185,20 @@ void create_dummy_vkey_and_proof(Builder& builder,
* We would either need a separate ACIR opcode where inner_proof_contains_pairing_point_accumulator = true,
* or we need non-witness data to be provided as metadata in the ACIR opcode
*/
PairingPointAccumulatorIndices create_honk_recursion_constraints(

template <typename Flavor>
HonkRecursionConstraintOutput create_honk_recursion_constraints(
Builder& builder,
const RecursionConstraint& input,
PairingPointAccumulatorIndices input_aggregation_object_indices,
bool has_valid_witness_assignments)
{
using Flavor = UltraRecursiveFlavor_<Builder>;
using RecursiveVerificationKey = Flavor::VerificationKey;
using RecursiveVerifier = bb::stdlib::recursion::honk::UltraRecursiveVerifier_<Flavor>;

ASSERT(input.proof_type == HONK);
bool is_rollup_honk_recursion_constraint =
(input.proof_type == ROLLUP_HONK || input.proof_type == ROLLUP_ROOT_HONK);
ASSERT(input.proof_type == HONK || is_rollup_honk_recursion_constraint);

// Construct an in-circuit representation of the verification key.
// For now, the v-key is a circuit constant and is fixed for the circuit.
Expand Down Expand Up @@ -200,21 +226,63 @@ PairingPointAccumulatorIndices create_honk_recursion_constraints(
// In the constraint, the agg object public inputs are still contained in the proof. To get the 'raw' size of
// the proof and public_inputs we subtract and add the corresponding amount from the respective sizes.
size_t size_of_proof_with_no_pub_inputs = input.proof.size() - bb::PAIRING_POINT_ACCUMULATOR_SIZE;
if (is_rollup_honk_recursion_constraint) {
size_of_proof_with_no_pub_inputs -= bb::IPA_CLAIM_SIZE;
}
size_t total_num_public_inputs = input.public_inputs.size() + bb::PAIRING_POINT_ACCUMULATOR_SIZE;
create_dummy_vkey_and_proof(
builder, size_of_proof_with_no_pub_inputs, total_num_public_inputs, key_fields, proof_fields);
if (is_rollup_honk_recursion_constraint) {
total_num_public_inputs += bb::IPA_CLAIM_SIZE;
}
create_dummy_vkey_and_proof<typename Flavor::NativeFlavor>(builder,
size_of_proof_with_no_pub_inputs,
total_num_public_inputs,
key_fields,
proof_fields,
is_rollup_honk_recursion_constraint);
}

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

return output.agg_obj.get_witness_indices();
output.agg_obj_indices = verifier_output.agg_obj.get_witness_indices();
if (is_rollup_honk_recursion_constraint) {
ASSERT(HasIPAAccumulator<Flavor>);
output.ipa_claim = verifier_output.ipa_opening_claim;
}
return output;
}

template HonkRecursionConstraintOutput create_honk_recursion_constraints<UltraRecursiveFlavor_<Builder>>(
Builder& builder,
const RecursionConstraint& input,
PairingPointAccumulatorIndices input_aggregation_object_indices,
bool has_valid_witness_assignments);

template HonkRecursionConstraintOutput create_honk_recursion_constraints<UltraRollupRecursiveFlavor_<Builder>>(
Builder& builder,
const RecursionConstraint& input,
PairingPointAccumulatorIndices input_aggregation_object_indices,
bool has_valid_witness_assignments);

} // namespace acir_format
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
#pragma once
#include "barretenberg/commitment_schemes/claim.hpp"
#include "barretenberg/dsl/acir_format/recursion_constraint.hpp"
#include "barretenberg/honk/proof_system/types/proof.hpp"
#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp"
#include "barretenberg/stdlib/primitives/curves/grumpkin.hpp"
#include <vector>

namespace acir_format {
using Builder = bb::UltraCircuitBuilder;

using namespace bb;

PairingPointAccumulatorIndices create_honk_recursion_constraints(
Builder& builder,
const RecursionConstraint& input,
PairingPointAccumulatorIndices input_aggregation_object,
bool has_valid_witness_assignments = false);
struct HonkRecursionConstraintOutput {
PairingPointAccumulatorIndices agg_obj_indices;
OpeningClaim<stdlib::grumpkin<Builder>> ipa_claim;
StdlibProof<Builder> ipa_proof;
};

template <typename Flavor>
HonkRecursionConstraintOutput create_honk_recursion_constraints(Builder& builder,
const RecursionConstraint& input,
PairingPointAccumulatorIndices input_aggregation_object,
bool has_valid_witness_assignments = false);

} // namespace acir_format
Loading

0 comments on commit a204d1b

Please sign in to comment.