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"
10#include "proof_surgeon.hpp"
11
12#include <gtest/gtest.h>
13#include <vector>
14
15using namespace acir_format;
16using namespace bb;
17
18template <typename RecursiveFlavor> class AcirHonkRecursionConstraint : public ::testing::Test {
19
20 public:
21 using InnerFlavor = typename RecursiveFlavor::NativeFlavor;
22 using InnerBuilder = typename InnerFlavor::CircuitBuilder;
25 using InnerVerificationKey = typename InnerFlavor::VerificationKey;
27 using OuterBuilder = typename RecursiveFlavor::CircuitBuilder;
34 using OuterVerificationKey = typename OuterFlavor::VerificationKey;
36
38 {
47 RangeConstraint range_a{
48 .witness = 0,
49 .num_bits = 32,
50 };
51 RangeConstraint range_b{
52 .witness = 1,
53 .num_bits = 32,
54 };
55
56 LogicConstraint logic_constraint{
59 .result = 2,
60 .num_bits = 32,
61 .is_xor_gate = 1,
62 };
63 poly_triple expr_a{
64 .a = 2,
65 .b = 3,
66 .c = 0,
67 .q_m = 0,
68 .q_l = 1,
69 .q_r = -1,
70 .q_o = 0,
71 .q_c = -10,
72 };
73 poly_triple expr_b{
74 .a = 3,
75 .b = 4,
76 .c = 5,
77 .q_m = 1,
78 .q_l = 0,
79 .q_r = 0,
80 .q_o = -1,
81 .q_c = 0,
82 };
83 poly_triple expr_c{
84 .a = 3,
85 .b = 5,
86 .c = 3,
87 .q_m = 1,
88 .q_l = 0,
89 .q_r = 0,
90 .q_o = -1,
91 .q_c = 0,
92
93 };
94 poly_triple expr_d{
95 .a = 5,
96 .b = 0,
97 .c = 0,
98 .q_m = 0,
99 .q_l = -1,
100 .q_r = 0,
101 .q_o = 0,
102 .q_c = 1,
103 };
104
105 AcirFormat constraint_system{
106 .varnum = 6,
107 .num_acir_opcodes = 7,
108 .public_inputs = { 1, 2 },
109 .logic_constraints = { logic_constraint },
110 .range_constraints = { range_a, range_b },
111 .poly_triple_constraints = { expr_a, expr_b, expr_c, expr_d },
112 .original_opcode_indices = create_empty_original_opcode_indices(),
113 };
114 mock_opcode_indices(constraint_system);
115
116 uint256_t inverse_of_five = fr(5).invert();
117 WitnessVector witness{
118 5, 10, 15, 5, inverse_of_five, 1,
119 };
120 uint32_t honk_recursion = 0;
121 if constexpr (IsAnyOf<InnerFlavor, UltraFlavor, UltraZKFlavor>) {
122 honk_recursion = 1;
123 } else if constexpr (IsAnyOf<InnerFlavor, UltraRollupFlavor>) {
124 honk_recursion = 2;
125 }
126 ProgramMetadata metadata{ .recursive = true, .honk_recursion = honk_recursion };
127 AcirProgram program{ constraint_system, witness };
128 auto builder = create_circuit(program, metadata);
129 return builder;
130 }
131
140 template <typename BuilderType>
142 bool dummy_witnesses,
143 bool predicate_val)
144 {
145 std::vector<RecursionConstraint> honk_recursion_constraints;
146
147 SlabVector<fr> witness;
148
149 for (auto& inner_circuit : inner_circuits) {
150
151 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
152 auto verification_key = std::make_shared<InnerVerificationKey>(prover_instance->get_precomputed());
153 InnerProver prover(prover_instance, verification_key);
154 InnerVerifier verifier(verification_key);
155 auto inner_proof = prover.construct_proof();
156
157 std::vector<bb::fr> key_witnesses = verification_key->to_field_elements();
158 fr key_hash_witness = verification_key->hash();
159 std::vector<fr> proof_witnesses = inner_proof;
160
161 // Compute the number of public inputs to extract (the ones from the circuit) and the proof type based on
162 // the Flavor
163 auto [num_public_inputs_to_extract, proof_type] = [&]() -> std::pair<size_t, acir_format::PROOF_TYPE> {
164 size_t num_public_inputs_to_extract = inner_circuit.num_public_inputs();
165 if constexpr (HasIPAAccumulator<InnerFlavor>) {
166 return { num_public_inputs_to_extract - RollupIO::PUBLIC_INPUTS_SIZE, ROLLUP_HONK };
167 } else if constexpr (InnerFlavor::HasZK) {
168 return { num_public_inputs_to_extract - DefaultIO::PUBLIC_INPUTS_SIZE, HONK_ZK };
169 } else {
170 return { num_public_inputs_to_extract - DefaultIO::PUBLIC_INPUTS_SIZE, HONK };
171 }
172 }();
173
174 auto [key_indices, key_hash_index, proof_indices, inner_public_inputs] =
176 witness, proof_witnesses, key_witnesses, key_hash_witness, num_public_inputs_to_extract);
177
178 auto predicate = WitnessOrConstant<fr>::from_index(static_cast<uint32_t>(witness.size()));
179 if (predicate_val) {
180 witness.push_back(fr(1));
181 } else {
182 witness.push_back(fr(0));
183 }
184
185 RecursionConstraint honk_recursion_constraint{
186 .key = key_indices,
187 .proof = proof_indices,
188 .public_inputs = inner_public_inputs,
189 .key_hash = key_hash_index,
190 .proof_type = proof_type,
191 .predicate = predicate,
192 };
193 honk_recursion_constraints.push_back(honk_recursion_constraint);
194 }
195
196 AcirFormat constraint_system{};
197 constraint_system.varnum = static_cast<uint32_t>(witness.size());
198 constraint_system.num_acir_opcodes = static_cast<uint32_t>(honk_recursion_constraints.size());
199 constraint_system.honk_recursion_constraints = honk_recursion_constraints;
200 constraint_system.original_opcode_indices = create_empty_original_opcode_indices();
201
202 mock_opcode_indices(constraint_system);
203 uint32_t honk_recursion = 0;
204 if constexpr (IsAnyOf<InnerFlavor, UltraFlavor, UltraZKFlavor>) {
205 honk_recursion = 1;
206 } else if constexpr (IsAnyOf<InnerFlavor, UltraRollupFlavor>) {
207 honk_recursion = 2;
208 }
209 ProgramMetadata metadata{ .honk_recursion = honk_recursion };
210 if (dummy_witnesses) {
211 witness = {}; // set it all to 0
212 }
213 AcirProgram program{ constraint_system, witness };
214 BuilderType outer_circuit = create_circuit<BuilderType>(program, metadata);
215
216 return outer_circuit;
217 }
218
220 const std::shared_ptr<OuterVerificationKey>& verification_key,
221 const HonkProof& proof)
222 {
224
225 bool result = false;
226
227 if constexpr (HasIPAAccumulator<RecursiveFlavor>) {
228 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key(1 << CONST_ECCVM_LOG_N);
229 OuterVerifier verifier(verification_key, ipa_verification_key);
230 result = verifier.template verify_proof<IO>(proof, prover_instance->ipa_proof).result;
231 } else {
232 OuterVerifier verifier(verification_key);
233 result = verifier.template verify_proof<IO>(proof).result;
234 }
235
236 return result;
237 }
238
239 protected:
241};
242
243using Flavors = testing::Types<UltraRecursiveFlavor_<UltraCircuitBuilder>,
248
250
251TYPED_TEST(AcirHonkRecursionConstraint, TestHonkRecursionConstraintVKGeneration)
252{
254 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
255
256 auto layer_2_circuit = TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(
257 layer_1_circuits, /*dummy_witnesses=*/false, /*predicate_val=*/true);
258
259 auto layer_2_circuit_with_dummy_witnesses =
260 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_1_circuits,
261 /*dummy_witnesses=*/true,
262 /*predicate_val=*/true);
263
264 auto prover_instance = std::make_shared<typename TestFixture::OuterProverInstance>(layer_2_circuit);
265 auto verification_key =
266 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance->get_precomputed());
267
268 auto prover_instance_dummy =
269 std::make_shared<typename TestFixture::OuterProverInstance>(layer_2_circuit_with_dummy_witnesses);
270 auto verification_key_dummy =
271 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance_dummy->get_precomputed());
272
273 // Compare the two vks
274 EXPECT_EQ(*verification_key_dummy, *verification_key);
275}
276
277TYPED_TEST(AcirHonkRecursionConstraint, TestBasicSingleHonkRecursionConstraint)
278{
280 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
281
282 auto layer_2_circuit =
283 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_1_circuits,
284 /*dummy_witnesses=*/false,
285 /*predicate_val=*/true);
286
287 info("estimate finalized circuit gates = ", layer_2_circuit.get_estimated_num_finalized_gates());
288
289 auto prover_instance = std::make_shared<typename TestFixture::OuterProverInstance>(layer_2_circuit);
290 auto verification_key =
291 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance->get_precomputed());
292 typename TestFixture::OuterProver prover(prover_instance, verification_key);
293 info("prover gates = ", prover_instance->dyadic_size());
294 auto proof = prover.construct_proof();
295
296 EXPECT_EQ(TestFixture::verify_proof(prover_instance, verification_key, proof), true);
297}
298
299TYPED_TEST(AcirHonkRecursionConstraint, TestBasicDoubleHonkRecursionConstraints)
300{
302 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
303
304 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
305
306 auto layer_2_circuit =
307 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_1_circuits, false, false);
308
309 info("circuit gates = ", layer_2_circuit.get_estimated_num_finalized_gates());
310
311 auto prover_instance = std::make_shared<typename TestFixture::OuterProverInstance>(layer_2_circuit);
312 auto verification_key =
313 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance->get_precomputed());
314 typename TestFixture::OuterProver prover(prover_instance, verification_key);
315 info("prover gates = ", prover_instance->dyadic_size());
316 auto proof = prover.construct_proof();
317
318 EXPECT_EQ(TestFixture::verify_proof(prover_instance, verification_key, proof), true);
319}
320
321TYPED_TEST(AcirHonkRecursionConstraint, TestOneOuterRecursiveCircuit)
322{
357 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
358 info("created first inner circuit");
359
361 layer_2_circuits.push_back(TestFixture::create_inner_circuit());
362 info("created second inner circuit");
363
364 layer_2_circuits.push_back(
365 TestFixture::template create_outer_circuit<typename TestFixture::InnerBuilder>(layer_1_circuits,
366 /*dummy_witnesses=*/false,
367 /*predicate_val=*/true));
368 info("created first outer circuit");
369
370 auto layer_3_circuit =
371 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_2_circuits,
372 /*dummy_witnesses=*/false,
373 /*predicate_val=*/true);
374 info("created second outer circuit");
375 info("number of gates in layer 3 = ", layer_3_circuit.get_estimated_num_finalized_gates());
376
377 auto prover_instance = std::make_shared<typename TestFixture::OuterProverInstance>(layer_3_circuit);
378 auto verification_key =
379 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance->get_precomputed());
380 typename TestFixture::OuterProver prover(prover_instance, verification_key);
381 info("prover gates = ", prover_instance->dyadic_size());
382 auto proof = prover.construct_proof();
383
384 EXPECT_EQ(TestFixture::verify_proof(prover_instance, verification_key, proof), true);
385}
386
404TYPED_TEST(AcirHonkRecursionConstraint, TestFullRecursiveComposition)
405{
407 layer_b_1_circuits.push_back(TestFixture::create_inner_circuit());
408 info("created first inner circuit");
409
411 layer_b_2_circuits.push_back(TestFixture::create_inner_circuit());
412 info("created second inner circuit");
413
414 std::vector<Builder> layer_2_circuits;
415 layer_2_circuits.push_back(
416 TestFixture::template create_outer_circuit<typename TestFixture::InnerBuilder>(layer_b_1_circuits,
417 /*dummy_witnesses=*/false,
418 /*predicate_val=*/true));
419 info("created first outer circuit");
420
421 layer_2_circuits.push_back(
422 TestFixture::template create_outer_circuit<typename TestFixture::InnerBuilder>(layer_b_2_circuits,
423 /*dummy_witnesses=*/false,
424 /*predicate_val=*/true));
425 info("created second outer circuit");
426
427 auto layer_3_circuit =
428 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_2_circuits,
429 /*dummy_witnesses=*/false,
430 /*predicate_val=*/true);
431 info("created third outer circuit");
432 info("number of gates in layer 3 circuit = ", layer_3_circuit.get_estimated_num_finalized_gates());
433
434 auto prover_instance = std::make_shared<typename TestFixture::OuterProverInstance>(layer_3_circuit);
435 auto verification_key =
436 std::make_shared<typename TestFixture::OuterVerificationKey>(prover_instance->get_precomputed());
437 typename TestFixture::OuterProver prover(prover_instance, verification_key);
438 info("prover gates = ", prover_instance->dyadic_size());
439 auto proof = prover.construct_proof();
440
441 EXPECT_EQ(TestFixture::verify_proof(prover_instance, verification_key, proof), true);
442}
acir_format::AcirFormatOriginalOpcodeIndices create_empty_original_opcode_indices()
void mock_opcode_indices(acir_format::AcirFormat &constraint_system)
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(bb::SlabVector< 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
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.
void info(Args... args)
Definition log.hpp:74
AluTraceBuilder builder
Definition alu.test.cpp:123
UltraCircuitBuilder create_circuit(AcirProgram &program, const ProgramMetadata &metadata)
Specialization for creating an Ultra circuit from an acir program.
bb::SlabVector< bb::fr > WitnessVector
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.
std::vector< fr > HonkProof
Definition proof.hpp:15
field< Bn254FrParams > fr
Definition fr.hpp:174
std::vector< T, bb::ContainerSlabAllocator< T > > SlabVector
A vector that uses the slab allocator.
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
WitnessOrConstant< bb::fr > a
RecursionConstraint struct contains information required to recursively verify a proof!
static WitnessOrConstant from_index(uint32_t index)
constexpr field invert() const noexcept