Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
honk_recursion_constraint.test.cpp
Go to the documentation of this file.
2#include "acir_format.hpp"
13#include "proof_surgeon.hpp"
14
15#include <gtest/gtest.h>
16#include <vector>
17
18using namespace acir_format;
19using namespace bb;
20
21template <typename RecursiveFlavor> class AcirHonkRecursionConstraint : public ::testing::Test {
22
23 public:
24 using InnerFlavor = typename RecursiveFlavor::NativeFlavor;
25 using InnerBuilder = typename InnerFlavor::CircuitBuilder;
28 using InnerVerificationKey = typename InnerFlavor::VerificationKey;
30 using OuterBuilder = typename RecursiveFlavor::CircuitBuilder;
37 using OuterVerificationKey = typename OuterFlavor::VerificationKey;
39
41 {
43
46
47 builder.add_public_variable(fr::one());
48 builder.add_public_variable(fr::one());
49
50 if constexpr (HasIPAAccumulator<InnerFlavor>) {
52 } else {
54 }
55
56 return builder;
57 }
58
67 template <typename BuilderType>
69 bool dummy_witnesses,
70 bool predicate_val)
71 {
72 std::vector<RecursionConstraint> honk_recursion_constraints;
73
74 std::vector<fr> witness;
75
76 for (auto& inner_circuit : inner_circuits) {
77
78 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
79 auto verification_key = std::make_shared<InnerVerificationKey>(prover_instance->get_precomputed());
80 InnerProver prover(prover_instance, verification_key);
81 InnerVerifier verifier(verification_key);
82 auto inner_proof = prover.construct_proof();
83
84 std::vector<bb::fr> key_witnesses = verification_key->to_field_elements();
85 fr key_hash_witness = verification_key->hash();
86 std::vector<fr> proof_witnesses = inner_proof;
87
88 // Compute the number of public inputs to extract (the ones from the circuit) and the proof type based on
89 // the Flavor
90 auto [num_public_inputs_to_extract, proof_type] = [&]() -> std::pair<size_t, acir_format::PROOF_TYPE> {
91 size_t num_public_inputs_to_extract = inner_circuit.num_public_inputs();
92 if constexpr (HasIPAAccumulator<InnerFlavor>) {
93 return { num_public_inputs_to_extract - RollupIO::PUBLIC_INPUTS_SIZE, ROLLUP_HONK };
94 } else if constexpr (InnerFlavor::HasZK) {
95 return { num_public_inputs_to_extract - DefaultIO::PUBLIC_INPUTS_SIZE, HONK_ZK };
96 } else {
97 return { num_public_inputs_to_extract - DefaultIO::PUBLIC_INPUTS_SIZE, HONK };
98 }
99 }();
100
101 auto [key_indices, key_hash_index, proof_indices, inner_public_inputs] =
103 witness, proof_witnesses, key_witnesses, key_hash_witness, num_public_inputs_to_extract);
104
105 uint32_t predicate_index = add_to_witness_and_track_indices(witness, predicate_val ? fr(1) : fr(0));
106 auto predicate = WitnessOrConstant<fr>::from_index(predicate_index);
107
108 RecursionConstraint honk_recursion_constraint{
109 .key = key_indices,
110 .proof = proof_indices,
111 .public_inputs = inner_public_inputs,
112 .key_hash = key_hash_index,
113 .proof_type = proof_type,
114 .predicate = predicate,
115 };
116 honk_recursion_constraints.push_back(honk_recursion_constraint);
117 }
118
119 AcirFormat constraint_system{};
120 constraint_system.max_witness_index = static_cast<uint32_t>(witness.size() - 1);
121 constraint_system.num_acir_opcodes = static_cast<uint32_t>(honk_recursion_constraints.size());
122 constraint_system.honk_recursion_constraints = honk_recursion_constraints;
123 constraint_system.original_opcode_indices = create_empty_original_opcode_indices();
124
125 mock_opcode_indices(constraint_system);
126 bool constexpr has_ipa_claim = IsAnyOf<InnerFlavor, UltraRollupFlavor>;
127
128 ProgramMetadata metadata{ .has_ipa_claim = has_ipa_claim };
129 if (dummy_witnesses) {
130 witness = {}; // set it all to 0
131 }
132 AcirProgram program{ constraint_system, witness };
133 auto outer_circuit = create_circuit<BuilderType>(program, metadata);
134
135 return outer_circuit;
136 }
137
139 const std::shared_ptr<OuterVerificationKey>& verification_key,
140 const HonkProof& proof)
141 {
143
144 bool result = false;
145
146 if constexpr (HasIPAAccumulator<RecursiveFlavor>) {
147 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key(1 << CONST_ECCVM_LOG_N);
148 OuterVerifier verifier(verification_key, ipa_verification_key);
149 result = verifier.template verify_proof<IO>(proof, prover_instance->ipa_proof).result;
150 } else {
151 OuterVerifier verifier(verification_key);
152 result = verifier.template verify_proof<IO>(proof).result;
153 }
154
155 return result;
156 }
157
158 protected:
160};
161
162using Flavors = testing::Types<UltraRecursiveFlavor_<UltraCircuitBuilder>,
167
169
170TYPED_TEST(AcirHonkRecursionConstraint, TestHonkRecursionConstraintVKGeneration)
171{
172#ifndef NDEBUG
174#endif
176 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
177
178 auto layer_2_circuit = TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(
179 layer_1_circuits, /*dummy_witnesses=*/false, /*predicate_val=*/true);
180
181 auto layer_2_circuit_with_dummy_witnesses =
182 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_1_circuits,
183 /*dummy_witnesses=*/true,
184 /*predicate_val=*/true);
185
186 auto prover_instance = std::make_shared<typename TestFixture::OuterProverInstance>(layer_2_circuit);
187 auto verification_key =
188 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance->get_precomputed());
189
190 auto prover_instance_dummy =
191 std::make_shared<typename TestFixture::OuterProverInstance>(layer_2_circuit_with_dummy_witnesses);
192 auto verification_key_dummy =
193 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance_dummy->get_precomputed());
194
195 // Compare the two vks
196 EXPECT_EQ(*verification_key_dummy, *verification_key);
197}
198
199TYPED_TEST(AcirHonkRecursionConstraint, TestBasicSingleHonkRecursionConstraint)
200{
202 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
203
204 auto layer_2_circuit =
205 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_1_circuits,
206 /*dummy_witnesses=*/false,
207 /*predicate_val=*/true);
208
209 auto prover_instance = std::make_shared<typename TestFixture::OuterProverInstance>(layer_2_circuit);
210 auto verification_key =
211 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance->get_precomputed());
212 typename TestFixture::OuterProver prover(prover_instance, verification_key);
213 auto proof = prover.construct_proof();
214
215 EXPECT_EQ(TestFixture::verify_proof(prover_instance, verification_key, proof), true);
216}
217
218TYPED_TEST(AcirHonkRecursionConstraint, TestBasicDoubleHonkRecursionConstraints)
219{
221 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
222
223 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
224
225 auto layer_2_circuit =
226 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_1_circuits, false, false);
227
228 auto prover_instance = std::make_shared<typename TestFixture::OuterProverInstance>(layer_2_circuit);
229 auto verification_key =
230 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance->get_precomputed());
231 typename TestFixture::OuterProver prover(prover_instance, verification_key);
232 auto proof = prover.construct_proof();
233
234 EXPECT_EQ(TestFixture::verify_proof(prover_instance, verification_key, proof), true);
235}
236
237TYPED_TEST(AcirHonkRecursionConstraint, TestOneOuterRecursiveCircuit)
238{
273 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
274 info("created first inner circuit");
275
277 layer_2_circuits.push_back(TestFixture::create_inner_circuit());
278 info("created second inner circuit");
279
280 layer_2_circuits.push_back(
281 TestFixture::template create_outer_circuit<typename TestFixture::InnerBuilder>(layer_1_circuits,
282 /*dummy_witnesses=*/false,
283 /*predicate_val=*/true));
284 info("created first outer circuit");
285
286 auto layer_3_circuit =
287 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_2_circuits,
288 /*dummy_witnesses=*/false,
289 /*predicate_val=*/true);
290 info("created second outer circuit");
291
292 auto prover_instance = std::make_shared<typename TestFixture::OuterProverInstance>(layer_3_circuit);
293 auto verification_key =
294 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance->get_precomputed());
295 typename TestFixture::OuterProver prover(prover_instance, verification_key);
296 auto proof = prover.construct_proof();
297
298 EXPECT_EQ(TestFixture::verify_proof(prover_instance, verification_key, proof), true);
299}
300
318TYPED_TEST(AcirHonkRecursionConstraint, TestFullRecursiveComposition)
319{
321 layer_b_1_circuits.push_back(TestFixture::create_inner_circuit());
322 info("created first inner circuit");
323
325 layer_b_2_circuits.push_back(TestFixture::create_inner_circuit());
326 info("created second inner circuit");
327
329 layer_2_circuits.push_back(
330 TestFixture::template create_outer_circuit<typename TestFixture::InnerBuilder>(layer_b_1_circuits,
331 /*dummy_witnesses=*/false,
332 /*predicate_val=*/true));
333 info("created first outer circuit");
334
335 layer_2_circuits.push_back(
336 TestFixture::template create_outer_circuit<typename TestFixture::InnerBuilder>(layer_b_2_circuits,
337 /*dummy_witnesses=*/false,
338 /*predicate_val=*/true));
339 info("created second outer circuit");
340
341 auto layer_3_circuit =
342 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_2_circuits,
343 /*dummy_witnesses=*/false,
344 /*predicate_val=*/true);
345 info("created third outer circuit");
346
347 auto prover_instance = std::make_shared<typename TestFixture::OuterProverInstance>(layer_3_circuit);
348 auto verification_key =
349 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance->get_precomputed());
350 typename TestFixture::OuterProver prover(prover_instance, verification_key);
351 auto proof = prover.construct_proof();
352
353 EXPECT_EQ(TestFixture::verify_proof(prover_instance, verification_key, proof), true);
354}
355
356TYPED_TEST(AcirHonkRecursionConstraint, GateCountSingleHonkRecursion)
357{
358 using InnerFlavor = TestFixture::InnerFlavor;
359 using RecursiveFlavor = TypeParam;
360 using InnerVerificationKey = TestFixture::InnerVerificationKey;
361 using InnerProverInstance = TestFixture::InnerProverInstance;
362 using OuterBuilder = TestFixture::OuterBuilder;
363
365 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
366
367 // Create outer circuit with gate counting enabled
368 std::vector<RecursionConstraint> honk_recursion_constraints;
369 std::vector<fr> witness;
370
371 auto& inner_circuit = layer_1_circuits[0];
372 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
373 auto verification_key = std::make_shared<InnerVerificationKey>(prover_instance->get_precomputed());
374 typename TestFixture::InnerProver prover(prover_instance, verification_key);
375 auto inner_proof = prover.construct_proof();
376
377 std::vector<bb::fr> key_witnesses = verification_key->to_field_elements();
378 fr key_hash_witness = verification_key->hash();
379
380 auto [num_public_inputs_to_extract, proof_type] = [&]() -> std::pair<size_t, acir_format::PROOF_TYPE> {
381 size_t num_public_inputs_to_extract = inner_circuit.num_public_inputs();
382 if constexpr (HasIPAAccumulator<InnerFlavor>) {
383 return { num_public_inputs_to_extract - RollupIO::PUBLIC_INPUTS_SIZE, ROLLUP_HONK };
384 } else if constexpr (InnerFlavor::HasZK) {
385 return { num_public_inputs_to_extract - DefaultIO::PUBLIC_INPUTS_SIZE, HONK_ZK };
386 } else {
387 return { num_public_inputs_to_extract - DefaultIO::PUBLIC_INPUTS_SIZE, HONK };
388 }
389 }();
390
391 auto [key_indices, key_hash_index, proof_indices, inner_public_inputs] =
393 witness, inner_proof, key_witnesses, key_hash_witness, num_public_inputs_to_extract);
394
395 // We pin the number of gates with predicate set to witness true, so this is an upper bound for when the constraint
396 // is added with a constant predicate
397 uint32_t predicate_index = add_to_witness_and_track_indices(witness, fr(1));
398 auto predicate = WitnessOrConstant<fr>::from_index(predicate_index);
399
400 RecursionConstraint honk_recursion_constraint{
401 .key = key_indices,
402 .proof = proof_indices,
403 .public_inputs = inner_public_inputs,
404 .key_hash = key_hash_index,
405 .proof_type = proof_type,
406 .predicate = predicate,
407 };
408 honk_recursion_constraints.push_back(honk_recursion_constraint);
409
410 AcirFormat constraint_system{};
411 constraint_system.max_witness_index = static_cast<uint32_t>(witness.size() - 1);
412 constraint_system.num_acir_opcodes = 1;
413 constraint_system.honk_recursion_constraints = honk_recursion_constraints;
414 constraint_system.original_opcode_indices = create_empty_original_opcode_indices();
415 mock_opcode_indices(constraint_system);
416
417 bool constexpr has_ipa_claim = IsAnyOf<InnerFlavor, UltraRollupFlavor>;
418 ProgramMetadata metadata{ .has_ipa_claim = has_ipa_claim, .collect_gates_per_opcode = true };
419
420 AcirProgram program{ constraint_system, witness };
421 auto outer_circuit = create_circuit<OuterBuilder>(program, metadata);
422
423 // Verify the gate count was recorded
424 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
425
426 // Get expected values from shared constants
427 static auto [EXPECTED_GATE_COUNT, EXPECTED_ECC_ROWS, EXPECTED_ULTRA_OPS] =
428 HONK_RECURSION_CONSTANTS<RecursiveFlavor>;
429
430 // Assert gate count
431 EXPECT_EQ(program.constraints.gates_per_opcode[0], EXPECTED_GATE_COUNT);
432
433 // For MegaBuilder, also assert ECC row count and ultra ops count
434 if constexpr (IsMegaBuilder<OuterBuilder>) {
435 size_t actual_ecc_rows = outer_circuit.op_queue->get_num_rows();
436 EXPECT_EQ(actual_ecc_rows, EXPECTED_ECC_ROWS);
437 size_t actual_ultra_ops = outer_circuit.op_queue->get_current_subtable_size();
438 EXPECT_EQ(actual_ultra_ops, EXPECTED_ULTRA_OPS);
439 }
440}
acir_format::AcirFormatOriginalOpcodeIndices create_empty_original_opcode_indices()
void mock_opcode_indices(acir_format::AcirFormat &constraint_system)
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:32
typename InnerFlavor::VerificationKey InnerVerificationKey
std::conditional_t< IsMegaBuilder< OuterBuilder >, MegaFlavor, std::conditional_t< HasIPAAccumulator< InnerFlavor >, UltraRollupFlavor, UltraFlavor > > OuterFlavor
typename RecursiveFlavor::CircuitBuilder OuterBuilder
BuilderType create_outer_circuit(std::vector< InnerBuilder > &inner_circuits, bool dummy_witnesses, bool predicate_val)
Create a circuit that recursively verifies one or more circuits.
typename InnerFlavor::CircuitBuilder InnerBuilder
typename OuterFlavor::VerificationKey OuterVerificationKey
typename RecursiveFlavor::NativeFlavor InnerFlavor
bool verify_proof(const std::shared_ptr< OuterProverInstance > &prover_instance, const std::shared_ptr< OuterVerificationKey > &verification_key, const HonkProof &proof)
static RecursionWitnessData populate_recursion_witness_data(std::vector< FF > &witness, std::vector< FF > &proof_witnesses, const std::vector< FF > &key_witnesses, const FF &key_hash_witness, const size_t num_public_inputs_to_extract)
Populate a witness vector with key, proof, and public inputs; track witness indices for each componen...
Manages the data that is propagated on the public inputs of an application/function circuit.
static constexpr size_t PUBLIC_INPUTS_SIZE
static void add_lookup_gates(Builder &builder, size_t num_iterations=1)
Add lookup gates using the uint32 XOR lookup table (table size 4096)
static void add_arithmetic_gates(Builder &builder, const size_t num_gates=4)
Add a specified number of arithmetic gates to the provided circuit.
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
The data that is propagated on the public inputs of a rollup circuit.
static constexpr size_t PUBLIC_INPUTS_SIZE
The recursive counterpart to the "native" Ultra flavor.
The recursive counterpart to the "native" UltraRollupFlavor.
The recursive counterpart to the Ultra flavor with ZK.
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
static void add_default(Builder &builder)
Add default public inputs when they are not present.
static void add_default(Builder &builder)
Add default public inputs when they are not present.
void info(Args... args)
Definition log.hpp:75
AluTraceBuilder builder
Definition alu.test.cpp:124
std::vector< uint32_t > add_to_witness_and_track_indices(WitnessVector &witness, const T &input)
Append values to a witness vector and track their indices.
Definition utils.hpp:70
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
testing::Types< UltraRecursiveFlavor_< UltraCircuitBuilder > > Flavors
TYPED_TEST_SUITE(BoomerangRecursiveVerifierTest, Flavors)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::vector< fr > HonkProof
Definition proof.hpp:15
field< Bn254FrParams > fr
Definition fr.hpp:174
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
RecursionConstraint struct contains information required to recursively verify a proof!
static WitnessOrConstant from_index(uint32_t index)
static constexpr field one()