Skip to content

Commit 1ce3c43

Browse files
authored
feat: IPA claim as public component (#13429)
Use PublicInputComponent pattern to handle IPA claims carried on the public inputs. (Caveat is we still need to move from storing the public input indices to storing a PublicComponentKey. There is an existing issue for this that also applies to pairing inputs). Closes AztecProtocol/barretenberg#1284
1 parent c9e7654 commit 1ce3c43

File tree

14 files changed

+118
-84
lines changed

14 files changed

+118
-84
lines changed

barretenberg/cpp/src/barretenberg/api/prove_tube.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void prove_tube(const std::string& output_path, const std::string& vk_path)
5050
AggregationObject::add_default_pairing_points_to_public_inputs(*builder);
5151

5252
// The tube only calls an IPA recursive verifier once, so we can just add this IPA claim and proof
53-
builder->add_ipa_claim(client_ivc_rec_verifier_output.opening_claim.get_witness_indices());
53+
client_ivc_rec_verifier_output.opening_claim.set_public();
5454
builder->ipa_proof = convert_stdlib_proof_to_native(client_ivc_rec_verifier_output.ipa_transcript->proof_data);
5555
ASSERT(builder->ipa_proof.size() && "IPA proof should not be empty");
5656

barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_ultra_recursive_verifier.test.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ template <typename RecursiveFlavor> class BoomerangRecursiveVerifierTest : publi
8383
if constexpr (HasIPAAccumulator<RecursiveFlavor>) {
8484
auto [stdlib_opening_claim, ipa_proof] =
8585
IPA<grumpkin<InnerBuilder>>::create_fake_ipa_claim_and_proof(builder);
86-
builder.add_ipa_claim(stdlib_opening_claim.get_witness_indices());
86+
stdlib_opening_claim.set_public();
8787
builder.ipa_proof = ipa_proof;
8888
}
8989
return builder;
@@ -125,7 +125,7 @@ template <typename RecursiveFlavor> class BoomerangRecursiveVerifierTest : publi
125125
pairing_points.P1.x.fix_witness();
126126
pairing_points.P1.y.fix_witness();
127127
if constexpr (HasIPAAccumulator<OuterFlavor>) {
128-
outer_circuit.add_ipa_claim(output.ipa_opening_claim.get_witness_indices());
128+
output.ipa_opening_claim.set_public();
129129
outer_circuit.ipa_proof = convert_stdlib_proof_to_native(output.ipa_proof);
130130
}
131131
info("Recursive Verifier: num gates = ", outer_circuit.get_estimated_num_finalized_gates());
@@ -153,4 +153,4 @@ HEAVY_TYPED_TEST(BoomerangRecursiveVerifierTest, SingleRecursiveVerification)
153153
TestFixture::test_recursive_verification();
154154
};
155155

156-
} // namespace bb::stdlib::recursion::honk
156+
} // namespace bb::stdlib::recursion::honk

barretenberg/cpp/src/barretenberg/commitment_schemes/claim.hpp

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,24 +51,62 @@ template <typename Curve> class OpeningClaim {
5151
using Fr = typename Curve::ScalarField;
5252

5353
public:
54+
using Builder =
55+
std::conditional_t<std::is_same_v<Curve, stdlib::grumpkin<UltraCircuitBuilder>>, UltraCircuitBuilder, void>;
5456
// (challenge r, evaluation v = p(r))
5557
OpeningPair<Curve> opening_pair;
5658
// commitment to univariate polynomial p(X)
5759
Commitment commitment;
5860

59-
IPAClaimIndices get_witness_indices() const
61+
// Size of public inputs representation of an opening claim over Grumpkin
62+
static constexpr size_t PUBLIC_INPUTS_SIZE = IPA_CLAIM_SIZE;
63+
64+
/**
65+
* @brief Set the witness indices for the opening claim to public
66+
* @note Implemented only for an opening claim over Grumpkin for use with IPA.
67+
*
68+
*/
69+
uint32_t set_public()
70+
requires(std::is_same_v<Curve, stdlib::grumpkin<UltraCircuitBuilder>>)
71+
{
72+
uint32_t start_idx = opening_pair.challenge.set_public();
73+
opening_pair.evaluation.set_public();
74+
commitment.set_public();
75+
76+
Builder* ctx = commitment.get_context();
77+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1325): Eventually the builder/PK/VK will store
78+
// public input key which should be set here and ipa_claim_public_input_indices should go away.
79+
uint32_t pub_idx = start_idx;
80+
for (uint32_t& idx : ctx->ipa_claim_public_input_indices) {
81+
idx = pub_idx++;
82+
}
83+
ctx->contains_ipa_claim = true;
84+
85+
return start_idx;
86+
}
87+
88+
/**
89+
* @brief Reconstruct an opening claim from limbs stored on the public inputs.
90+
* @note Implemented only for an opening claim over Grumpkin for use with IPA.
91+
*
92+
*/
93+
static OpeningClaim<Curve> reconstruct_from_public(
94+
const std::span<const stdlib::field_t<Builder>, PUBLIC_INPUTS_SIZE>& limbs)
6095
requires(std::is_same_v<Curve, stdlib::grumpkin<UltraCircuitBuilder>>)
6196
{
62-
return { opening_pair.challenge.binary_basis_limbs[0].element.normalize().witness_index,
63-
opening_pair.challenge.binary_basis_limbs[1].element.normalize().witness_index,
64-
opening_pair.challenge.binary_basis_limbs[2].element.normalize().witness_index,
65-
opening_pair.challenge.binary_basis_limbs[3].element.normalize().witness_index,
66-
opening_pair.evaluation.binary_basis_limbs[0].element.normalize().witness_index,
67-
opening_pair.evaluation.binary_basis_limbs[1].element.normalize().witness_index,
68-
opening_pair.evaluation.binary_basis_limbs[2].element.normalize().witness_index,
69-
opening_pair.evaluation.binary_basis_limbs[3].element.normalize().witness_index,
70-
commitment.x.normalize().witness_index, // no idea if we need these normalize() calls...
71-
commitment.y.normalize().witness_index };
97+
ASSERT(2 * Fr::PUBLIC_INPUTS_SIZE + Commitment::PUBLIC_INPUTS_SIZE == PUBLIC_INPUTS_SIZE);
98+
99+
const size_t FIELD_SIZE = Fr::PUBLIC_INPUTS_SIZE;
100+
const size_t COMMITMENT_SIZE = Commitment::PUBLIC_INPUTS_SIZE;
101+
std::span<const stdlib::field_t<Builder>, FIELD_SIZE> challenge_limbs{ limbs.data(), FIELD_SIZE };
102+
std::span<const stdlib::field_t<Builder>, FIELD_SIZE> evaluation_limbs{ limbs.data() + FIELD_SIZE, FIELD_SIZE };
103+
std::span<const stdlib::field_t<Builder>, COMMITMENT_SIZE> commitment_limbs{ limbs.data() + 2 * FIELD_SIZE,
104+
COMMITMENT_SIZE };
105+
auto challenge = Fr::reconstruct_from_public(challenge_limbs);
106+
auto evaluation = Fr::reconstruct_from_public(evaluation_limbs);
107+
auto commitment = Commitment::reconstruct_from_public(commitment_limbs);
108+
109+
return OpeningClaim<Curve>{ { challenge, evaluation }, commitment };
72110
}
73111

74112
auto get_native_opening_claim() const

barretenberg/cpp/src/barretenberg/commitment_schemes_recursion/ipa_recursive.test.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class IPARecursiveTests : public CommitmentTest<NativeCurve> {
160160
// polynomial.
161161
auto [output_claim, ipa_proof] =
162162
RecursiveIPA::accumulate(this->ck(), transcript_1, claim_1, transcript_2, claim_2);
163-
builder.add_ipa_claim(output_claim.get_witness_indices());
163+
output_claim.set_public();
164164
builder.ipa_proof = ipa_proof;
165165
builder.finalize_circuit(/*ensure_nonzero=*/false);
166166
info("Circuit with 2 IPA Recursive Verifiers and IPA Accumulation num finalized gates = ",
@@ -266,7 +266,7 @@ TEST_F(IPARecursiveTests, AccumulationAndFullRecursiveVerifier)
266266
// Creates two IPA accumulators and accumulators from the two claims. Also constructs the accumulated h
267267
// polynomial.
268268
auto [output_claim, ipa_proof] = RecursiveIPA::accumulate(this->ck(), transcript_1, claim_1, transcript_2, claim_2);
269-
builder.add_ipa_claim(output_claim.get_witness_indices());
269+
output_claim.set_public();
270270
builder.ipa_proof = ipa_proof;
271271
builder.finalize_circuit(/*ensure_nonzero=*/false);
272272
info("Circuit with 2 IPA Recursive Verifiers and IPA Accumulation num finalized gates = ",
@@ -314,7 +314,7 @@ TEST_F(IPARecursiveTests, AccumulationWithDifferentSizes)
314314
// Creates two IPA accumulators and accumulators from the two claims. Also constructs the accumulated h
315315
// polynomial.
316316
auto [output_claim, ipa_proof] = RecursiveIPA::accumulate(this->ck(), transcript_1, claim_1, transcript_2, claim_2);
317-
builder.add_ipa_claim(output_claim.get_witness_indices());
317+
output_claim.set_public();
318318
builder.ipa_proof = ipa_proof;
319319
builder.finalize_circuit(/*ensure_nonzero=*/false);
320320
info("Circuit with 2 IPA Recursive Verifiers and IPA Accumulation num finalized gates = ",
@@ -332,4 +332,4 @@ TEST_F(IPARecursiveTests, AccumulationWithDifferentSizes)
332332

333333
auto result = NativeIPA::reduce_verify(this->vk(), opening_claim, verifier_transcript);
334334
EXPECT_TRUE(result);
335-
}
335+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ void build_constraints(Builder& builder, AcirProgram& program, const ProgramMeta
295295
// If we are proving with UltraRollupFlavor, the IPA proof should have nonzero size.
296296
ASSERT((metadata.honk_recursion == 2) == (output.ipa_proof.size() > 0));
297297
if (metadata.honk_recursion == 2) {
298-
builder.add_ipa_claim(output.ipa_claim.get_witness_indices());
298+
output.ipa_claim.set_public();
299299
builder.ipa_proof = output.ipa_proof;
300300
}
301301
}

barretenberg/cpp/src/barretenberg/stdlib/client_ivc_verifier/client_ivc_recursive_verifier.test.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ TEST_F(ClientIVCRecursionTests, ClientTubeBase)
109109
// just using default.
110110
AggregationObject::add_default_pairing_points_to_public_inputs(*tube_builder);
111111
// The tube only calls an IPA recursive verifier once, so we can just add this IPA claim and proof
112-
tube_builder->add_ipa_claim(client_ivc_rec_verifier_output.opening_claim.get_witness_indices());
112+
client_ivc_rec_verifier_output.opening_claim.set_public();
113113
tube_builder->ipa_proof = convert_stdlib_proof_to_native(client_ivc_rec_verifier_output.ipa_transcript->proof_data);
114114

115115
info("ClientIVC Recursive Verifier: num prefinalized gates = ", tube_builder->num_gates);
@@ -140,7 +140,7 @@ TEST_F(ClientIVCRecursionTests, ClientTubeBase)
140140
base_verifier.verify_proof(base_tube_proof, AggregationObject::construct_default(base_builder));
141141
info("Tube UH Recursive Verifier: num prefinalized gates = ", base_builder.num_gates);
142142
output.agg_obj.set_public();
143-
base_builder.add_ipa_claim(output.ipa_opening_claim.get_witness_indices());
143+
output.ipa_opening_claim.set_public();
144144
base_builder.ipa_proof = tube_prover.proving_key->proving_key.ipa_proof;
145145
EXPECT_EQ(base_builder.failed(), false) << base_builder.err();
146146
EXPECT_TRUE(CircuitChecker::check(base_builder));
@@ -172,7 +172,7 @@ TEST_F(ClientIVCRecursionTests, TubeVKIndependentOfInputCircuits)
172172
// instead of just using default.
173173
AggregationObject::add_default_pairing_points_to_public_inputs(*tube_builder);
174174
// The tube only calls an IPA recursive verifier once, so we can just add this IPA claim and proof
175-
tube_builder->add_ipa_claim(client_ivc_rec_verifier_output.opening_claim.get_witness_indices());
175+
client_ivc_rec_verifier_output.opening_claim.set_public();
176176
tube_builder->ipa_proof =
177177
convert_stdlib_proof_to_native(client_ivc_rec_verifier_output.ipa_transcript->proof_data);
178178

barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -137,36 +137,12 @@ UltraRecursiveVerifier_<Flavor>::Output UltraRecursiveVerifier_<Flavor>::verify_
137137
// Extract the IPA claim from the public inputs
138138
// Parse out the nested IPA claim using key->ipa_claim_public_input_indices and run the native IPA verifier.
139139
if constexpr (HasIPAAccumulator<Flavor>) {
140-
const auto recover_fq_from_public_inputs = [](std::array<FF, Curve::BaseField::NUM_LIMBS>& limbs) {
141-
for (size_t k = 0; k < Curve::BaseField::NUM_LIMBS; k++) {
142-
limbs[k].create_range_constraint(Curve::BaseField::NUM_LIMB_BITS, "limb_" + std::to_string(k));
143-
}
144-
return Curve::BaseField::unsafe_construct_from_limbs(limbs[0], limbs[1], limbs[2], limbs[3], false);
145-
};
140+
using PublicIpaClaim = PublicInputComponent<OpeningClaim<grumpkin<Builder>>>;
146141

147142
if (verification_key->verification_key->contains_ipa_claim) {
148-
OpeningClaim<grumpkin<Builder>> ipa_claim;
149-
std::array<FF, Curve::BaseField::NUM_LIMBS> challenge_bigfield_limbs;
150-
for (size_t k = 0; k < Curve::BaseField::NUM_LIMBS; k++) {
151-
challenge_bigfield_limbs[k] =
152-
verification_key
153-
->public_inputs[verification_key->verification_key->ipa_claim_public_input_indices[k]];
154-
}
155-
std::array<FF, Curve::BaseField::NUM_LIMBS> evaluation_bigfield_limbs;
156-
for (size_t k = 0; k < Curve::BaseField::NUM_LIMBS; k++) {
157-
evaluation_bigfield_limbs[k] =
158-
verification_key
159-
->public_inputs[verification_key->verification_key
160-
->ipa_claim_public_input_indices[Curve::BaseField::NUM_LIMBS + k]];
161-
}
162-
ipa_claim.opening_pair.challenge = recover_fq_from_public_inputs(challenge_bigfield_limbs);
163-
ipa_claim.opening_pair.evaluation = recover_fq_from_public_inputs(evaluation_bigfield_limbs);
164-
ipa_claim.commitment = {
165-
verification_key->public_inputs[verification_key->verification_key->ipa_claim_public_input_indices[8]],
166-
verification_key->public_inputs[verification_key->verification_key->ipa_claim_public_input_indices[9]],
167-
false
168-
};
169-
output.ipa_opening_claim = std::move(ipa_claim);
143+
PublicComponentKey ipa_claim_key{ verification_key->verification_key->ipa_claim_public_input_indices[0],
144+
true };
145+
output.ipa_opening_claim = PublicIpaClaim::reconstruct(verification_key->public_inputs, ipa_claim_key);
170146
}
171147
}
172148

barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.test.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ template <typename RecursiveFlavor> class RecursiveVerifierTest : public testing
7979
if constexpr (HasIPAAccumulator<RecursiveFlavor>) {
8080
auto [stdlib_opening_claim, ipa_proof] =
8181
IPA<grumpkin<InnerBuilder>>::create_fake_ipa_claim_and_proof(builder);
82-
builder.add_ipa_claim(stdlib_opening_claim.get_witness_indices());
82+
stdlib_opening_claim.set_public();
8383
builder.ipa_proof = ipa_proof;
8484
}
8585
return builder;
@@ -163,7 +163,7 @@ template <typename RecursiveFlavor> class RecursiveVerifierTest : public testing
163163
typename RecursiveVerifier::Output verifier_output =
164164
verifier.verify_proof(inner_proof, AggState::construct_default(outer_circuit));
165165
if constexpr (HasIPAAccumulator<OuterFlavor>) {
166-
outer_circuit.add_ipa_claim(verifier_output.ipa_opening_claim.get_witness_indices());
166+
verifier_output.ipa_opening_claim.set_public();
167167
outer_circuit.ipa_proof = convert_stdlib_proof_to_native(verifier_output.ipa_proof);
168168
}
169169

@@ -204,7 +204,7 @@ template <typename RecursiveFlavor> class RecursiveVerifierTest : public testing
204204
VerifierOutput output = verifier.verify_proof(inner_proof, agg_obj);
205205
AggState pairing_points = output.agg_obj;
206206
if constexpr (HasIPAAccumulator<OuterFlavor>) {
207-
outer_circuit.add_ipa_claim(output.ipa_opening_claim.get_witness_indices());
207+
output.ipa_opening_claim.set_public();
208208
outer_circuit.ipa_proof = convert_stdlib_proof_to_native(output.ipa_proof);
209209
}
210210
info("Recursive Verifier: num gates = ", outer_circuit.get_estimated_num_finalized_gates());

barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,16 @@ template <typename Builder> class field_t {
281281
void assert_is_not_zero(std::string const& msg = "field_t::assert_is_not_zero") const;
282282
void assert_is_zero(std::string const& msg = "field_t::assert_is_zero") const;
283283
bool is_constant() const { return witness_index == IS_CONSTANT; }
284-
void set_public() const
284+
uint32_t set_public() const
285285
{
286+
uint32_t public_input_index = 0;
286287
if constexpr (IsSimulator<Builder>) {
287288
auto value = normalize().get_value();
288-
context->set_public_input(value);
289+
public_input_index = context->set_public_input(value);
289290
} else {
290-
context->set_public_input(normalize().witness_index);
291+
public_input_index = context->set_public_input(normalize().witness_index);
291292
}
293+
return public_input_index;
292294
}
293295

294296
/**

barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ template <typename Builder> class cycle_group {
4848
static constexpr size_t NUM_ROUNDS = (NUM_BITS + TABLE_BITS - 1) / TABLE_BITS;
4949
inline static constexpr std::string_view OFFSET_GENERATOR_DOMAIN_SEPARATOR = "cycle_group_offset_generator";
5050

51+
// Since the cycle_group base field is the circuit's native field, it can be stored using two public inputs.
52+
static constexpr size_t PUBLIC_INPUTS_SIZE = 2;
53+
5154
private:
5255
public:
5356
/**
@@ -276,6 +279,31 @@ template <typename Builder> class cycle_group {
276279
return OriginTag(x.get_origin_tag(), y.get_origin_tag(), _is_infinity.get_origin_tag());
277280
}
278281

282+
/**
283+
* @brief Set the witness indices representing the cycle_group to public
284+
*
285+
* @return uint32_t Index into the public inputs array at which the representation is stored
286+
*/
287+
uint32_t set_public()
288+
{
289+
uint32_t start_idx = x.set_public();
290+
y.set_public();
291+
return start_idx;
292+
}
293+
294+
/**
295+
* @brief Reconstruct a cycle_group from limbs (generally stored in the public inputs)
296+
* @details The base field of the cycle_group curve is the same as the circuit's native field so each coordinate is
297+
* represented by a single "limb".
298+
*
299+
* @param limbs The coordinates of the cycle_group element
300+
* @return cycle_group
301+
*/
302+
static cycle_group reconstruct_from_public(const std::span<const field_t, 2>& limbs)
303+
{
304+
return cycle_group(limbs[0], limbs[1], false);
305+
}
306+
279307
field_t x;
280308
field_t y;
281309

barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,12 @@ template <typename FF_> class CircuitBuilderBase {
183183
virtual uint32_t add_public_variable(const FF& in);
184184

185185
/**
186-
* Make a witness variable public.
186+
* @brief Make a witness variable public.
187187
*
188188
* @param witness_index The index of the witness.
189-
* */
190-
virtual void set_public_input(uint32_t witness_index);
189+
* @return uint32_t The index of the witness in the public inputs vector.
190+
*/
191+
virtual uint32_t set_public_input(uint32_t witness_index);
191192
virtual void assert_equal(uint32_t a_idx, uint32_t b_idx, std::string const& msg = "assert_equal");
192193

193194
// TODO(#216)(Adrian): This method should belong in the ComposerHelper, where the number of reserved gates can be
@@ -216,8 +217,6 @@ template <typename FF_> class CircuitBuilderBase {
216217
void add_pairing_point_accumulator_for_plonk(
217218
const PairingPointAccumulatorIndices& pairing_point_accum_witness_indices);
218219

219-
void add_ipa_claim(const IPAClaimIndices& ipa_claim_witness_indices);
220-
221220
bool failed() const;
222221
const std::string& err() const;
223222

barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base_impl.hpp

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -176,17 +176,20 @@ template <typename FF_> uint32_t CircuitBuilderBase<FF_>::add_public_variable(co
176176
return index;
177177
}
178178

179-
template <typename FF_> void CircuitBuilderBase<FF_>::set_public_input(const uint32_t witness_index)
179+
template <typename FF_> uint32_t CircuitBuilderBase<FF_>::set_public_input(const uint32_t witness_index)
180180
{
181181
for (const uint32_t public_input : public_inputs) {
182182
if (public_input == witness_index) {
183183
if (!failed()) {
184184
failure("Attempted to set a public input that is already public!");
185185
}
186-
return;
186+
return 0;
187187
}
188188
}
189+
uint32_t public_input_index = static_cast<uint32_t>(public_inputs.size());
189190
public_inputs.emplace_back(witness_index);
191+
192+
return public_input_index;
190193
}
191194

192195
/**
@@ -255,22 +258,6 @@ void CircuitBuilderBase<FF_>::add_pairing_point_accumulator_for_plonk(
255258
}
256259
}
257260

258-
template <typename FF_> void CircuitBuilderBase<FF_>::add_ipa_claim(const IPAClaimIndices& ipa_claim_witness_indices)
259-
{
260-
if (contains_ipa_claim) {
261-
failure("added IPA claim when one already exists");
262-
ASSERT(0);
263-
}
264-
contains_ipa_claim = true;
265-
266-
size_t i = 0;
267-
for (const auto& idx : ipa_claim_witness_indices) {
268-
set_public_input(idx);
269-
ipa_claim_public_input_indices[i] = static_cast<uint32_t>(public_inputs.size() - 1);
270-
++i;
271-
}
272-
}
273-
274261
template <typename FF_> bool CircuitBuilderBase<FF_>::failed() const
275262
{
276263
return _failed;

0 commit comments

Comments
 (0)