Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
hypernova_recursion_constraint.test.cpp
Go to the documentation of this file.
2#include "acir_format.hpp"
14
15#include <gtest/gtest.h>
16#include <vector>
17
18using namespace acir_format;
19using namespace bb;
20
21class HypernovaRecursionConstraintTest : public ::testing::Test {
22
23 public:
27 using FF = Flavor::FF;
32
33 static constexpr size_t NUM_TRAILING_KERNELS = 3; // reset, tail, hiding
34
47
48 static std::shared_ptr<VerificationKey> get_verification_key(Builder& builder_in)
49 {
50 // This is a workaround to ensure that the circuit is finalized before we create the verification key
51 // In practice, this should not be needed as the circuit will be finalized when it is accumulated into the IVC
52 // but this is a workaround for the test setup.
53 // Create a copy of the input circuit
55
56 // Deepcopy the opqueue to avoid modifying the original one
57 builder.op_queue = std::make_shared<ECCOpQueue>(*builder.op_queue);
59 std::shared_ptr<VerificationKey> vk = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
60 return vk;
61 }
62
64 {
65
66 // Reset kernel
67 EXPECT_EQ(ivc->verification_queue.size(), 1);
68 EXPECT_EQ(ivc->verification_queue[0].type, QUEUE_TYPE::HN);
70
71 // Tail kernel
72 EXPECT_EQ(ivc->verification_queue.size(), 1);
73 EXPECT_EQ(ivc->verification_queue[0].type, QUEUE_TYPE::HN_TAIL);
75
76 // Hiding kernel
77 EXPECT_EQ(ivc->verification_queue.size(), 1);
78 EXPECT_EQ(ivc->verification_queue[0].type, QUEUE_TYPE::HN_FINAL);
80 }
81
82 static UltraCircuitBuilder create_inner_circuit(size_t log_num_gates = 10)
83 {
85
86 // Create 2^log_n many add gates based on input log num gates
87 const size_t num_gates = (1 << log_num_gates);
88 for (size_t i = 0; i < num_gates; ++i) {
90 uint32_t a_idx = builder.add_variable(a);
91
94 fr d = a + b + c;
95 uint32_t b_idx = builder.add_variable(b);
96 uint32_t c_idx = builder.add_variable(c);
97 uint32_t d_idx = builder.add_variable(d);
98
99 builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
100 }
101
103 return builder;
104 }
105
111 {
112 AcirProgram program;
113 std::vector<RecursionConstraint> recursion_constraints;
114
115 Builder circuit{ ivc->goblin.op_queue };
118
119 {
120 using RecursiveFlavor = UltraRecursiveFlavor_<Builder>;
122 using StdlibProof = bb::stdlib::Proof<Builder>;
124
125 // Create an arbitrary inner circuit
126 auto inner_circuit = create_inner_circuit();
127
128 // Compute native verification key
129 auto prover_instance = std::make_shared<ProverInstance_<UltraFlavor>>(inner_circuit);
130 auto honk_vk = std::make_shared<UltraFlavor::VerificationKey>(prover_instance->get_precomputed());
131 UltraProver prover(prover_instance, honk_vk); // A prerequisite for computing VK
132 auto inner_proof = prover.construct_proof();
133
134 if (tamper_vk) {
135 honk_vk->q_l = g1::one;
136 auto honk_vk_and_hash = std::make_shared<UltraFlavor::VKAndHash>(honk_vk);
137 UltraVerifier_<UltraFlavor, DefaultIO> verifier(honk_vk_and_hash);
138 EXPECT_FALSE(verifier.verify_proof(inner_proof).result);
139 }
140 // Instantiate the recursive verifier using the native verification key
141 auto stdlib_vk_and_hash = std::make_shared<RecursiveFlavor::VKAndHash>(circuit, honk_vk);
142 bb::UltraVerifier_<RecursiveFlavor, StdlibIO> verifier(stdlib_vk_and_hash);
143
144 StdlibProof stdlib_inner_proof(circuit, inner_proof);
145 VerifierOutput output = verifier.verify_proof(stdlib_inner_proof);
146
147 // IO
148 StdlibIO inputs;
149 inputs.pairing_inputs = output.points_accumulator;
150 inputs.set_public(); // propagate resulting pairing points on the public inputs
151 }
152
153 return circuit;
154 }
155
164 static RecursionConstraint create_recursion_constraint(const VerifierInputs& input, std::vector<FF>& witness)
165 {
166 // Use centralized conversion from QUEUE_TYPE to PROOF_TYPE
167 PROOF_TYPE proof_type = queue_type_to_proof_type(input.type);
168
169 RecursionConstraint constraint =
171 input.proof, // proof contains the public inputs at this stage
172 input.honk_vk->to_field_elements(),
173 input.honk_vk->hash(),
174 bb::fr::zero(),
175 /*num_public_inputs_to_extract=*/0,
176 proof_type);
177
178 constraint.proof = {}; // the proof witness indices are not needed in an ivc recursion constraint
179
180 return constraint;
181 }
182
197 {
198 AcirProgram program;
199
200 // Construct recursion constraints based on the ivc verification queue; populate the witness along the way
201 std::vector<RecursionConstraint> hn_recursion_constraints;
202 hn_recursion_constraints.reserve(verification_queue.size());
203 for (const auto& queue_entry : verification_queue) {
204 hn_recursion_constraints.push_back(create_recursion_constraint(queue_entry, program.witness));
205 }
206
207 // Construct a constraint system containing the business logic and ivc recursion constraints
208 program.constraints.max_witness_index = static_cast<uint32_t>(program.witness.size() - 1);
209 program.constraints.num_acir_opcodes = static_cast<uint32_t>(hn_recursion_constraints.size());
210 program.constraints.hn_recursion_constraints = hn_recursion_constraints;
212 hn_recursion_constraints.size() == 1
215
216 return program;
217 }
218
220 {
221 // construct a mock kernel program (acir) from the ivc verification queue
222 const ProgramMetadata metadata{ ivc };
223 AcirProgram mock_kernel_program = construct_mock_kernel_program(ivc->verification_queue);
224 auto kernel = acir_format::create_circuit<Builder>(mock_kernel_program, metadata);
225 auto kernel_vk = get_kernel_vk_from_circuit(kernel);
226 ivc->accumulate(kernel, kernel_vk);
227 }
228
230 {
231 // construct a mock kernel program (acir) from the ivc verification queue
232 auto app_circuit = construct_mock_app_circuit(ivc);
233 ivc->accumulate(app_circuit, get_verification_key(app_circuit));
234 }
235
243 {
244 // Create kernel circuit from the kernel program
245 auto kernel = acir_format::create_circuit<Builder>(program);
246
247 // Manually construct the VK for the kernel circuit
248 auto prover_instance = std::make_shared<Chonk::ProverInstance>(kernel);
249 auto verification_key = std::make_shared<Chonk::MegaVerificationKey>(prover_instance->get_precomputed());
250 return verification_key;
251 }
252
254 {
255 auto prover_instance = std::make_shared<Chonk::ProverInstance>(kernel);
256 auto verification_key = std::make_shared<Chonk::MegaVerificationKey>(prover_instance->get_precomputed());
257 return verification_key;
258 }
259
260 protected:
262};
263
268{
270 EXPECT_EQ(merge_proof.size(), MERGE_PROOF_SIZE);
271}
272
278{
279 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5 /* app, kernel, reset, tail, hiding */);
280
281 // construct a mock app_circuit
282 construct_and_accumulate_mock_app(ivc);
283
284 // Construct kernel consisting only of the kernel completion logic
285 construct_and_accumulate_mock_kernel(ivc);
286
287 // add the trailing kernels
288 construct_and_accumulate_trailing_kernels(ivc);
289
290 auto proof = ivc->prove();
291 {
292 auto vk_and_hash = ivc->get_hiding_kernel_vk_and_hash();
293 ChonkNativeVerifier verifier(vk_and_hash);
294 EXPECT_TRUE(verifier.verify(proof));
295 }
296}
297
303{
304 // 4 ciruits and the tail kernel
305 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/7);
306
307 // construct a mock app_circuit
308 construct_and_accumulate_mock_app(ivc);
309
310 const ProgramMetadata metadata{ ivc };
311
312 // Construct kernel_0; consists of a single oink recursive verification for app (plus databus/merge logic)
313 construct_and_accumulate_mock_kernel(ivc);
314
315 // construct a mock app_circuit
316 construct_and_accumulate_mock_app(ivc);
317
318 // Construct and accumulate another Kernel circuit
319 construct_and_accumulate_mock_kernel(ivc);
320
321 // Accumulate the trailing kernels
322 construct_and_accumulate_trailing_kernels(ivc);
323
324 auto proof = ivc->prove();
325 {
326 ChonkNativeVerifier verifier(ivc->get_hiding_kernel_vk_and_hash());
327 EXPECT_TRUE(verifier.verify(proof));
328 }
329}
330
331// Test generation of "init" kernel VK via dummy IVC data
332TEST_F(HypernovaRecursionConstraintTest, GenerateInitKernelVKFromConstraints)
333{
335 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
337 {
338 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
339
340 // Construct and accumulate mock app_circuit
341 construct_and_accumulate_mock_app(ivc);
342
343 // Construct and accumulate kernel consisting only of the kernel completion logic
344 construct_and_accumulate_mock_kernel(ivc);
345 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
346 }
347
348 // Now, construct the kernel VK by mocking the post app accumulation state of the IVC
350 {
351 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
352
353 // Construct kernel consisting only of the kernel completion logic
354 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
355 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
356 program.witness = {}; // remove the witness to mimick VK construction context
357
358 kernel_vk = construct_kernel_vk_from_acir_program(program);
359 }
360
361 // Compare the VK constructed via running the IVc with the one constructed via mocking
362 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
363}
364
365// Test generation of "reset" kernel VK via dummy IVC data
366TEST_F(HypernovaRecursionConstraintTest, GenerateResetKernelVKFromConstraints)
367{
369 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
371 {
372 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
373
374 const ProgramMetadata metadata{ ivc };
375
376 // Construct and accumulate mock app_circuit
377 construct_and_accumulate_mock_app(ivc);
378
379 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
380 construct_and_accumulate_mock_kernel(ivc);
381 EXPECT_TRUE(ivc->verification_queue.size() == 1);
382 EXPECT_TRUE(ivc->verification_queue[0].type == bb::Chonk::QUEUE_TYPE::HN);
383
384 // Construct and accumulate a mock RESET kernel (HN recursion for kernel accumulation)
385 construct_and_accumulate_mock_kernel(ivc);
386 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
387 }
388
389 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
391 {
392 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
393
394 // Construct kernel consisting only of the kernel completion logic
395 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/true);
396 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
397 program.witness = {}; // remove the witness to mimick VK construction context
398 kernel_vk = construct_kernel_vk_from_acir_program(program);
399 }
400
401 // Compare the VK constructed via running the IVc with the one constructed via mocking
402 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
403}
404
405// Test generation of "tail" kernel VK via dummy IVC data
406TEST_F(HypernovaRecursionConstraintTest, GenerateTailKernelVKFromConstraints)
407{
409 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
411 {
412 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
413
414 const ProgramMetadata metadata{ ivc };
415
416 // Construct and accumulate mock app_circuit
417 construct_and_accumulate_mock_app(ivc);
418
419 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
420 construct_and_accumulate_mock_kernel(ivc);
421
422 // Construct and accumulate a mock RESET kernel (HN recursion for kernel accumulation)
423 construct_and_accumulate_mock_kernel(ivc);
424
425 // Construct and accumulate a mock TAIL kernel (HN recursion for kernel accumulation)
426 EXPECT_TRUE(ivc->verification_queue.size() == 1);
427 EXPECT_TRUE(ivc->verification_queue[0].type == bb::Chonk::QUEUE_TYPE::HN_TAIL);
428 construct_and_accumulate_mock_kernel(ivc);
429
430 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
431 }
432
433 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
435 {
436 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
437
438 // Construct kernel consisting only of the kernel completion logic
439 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN_TAIL, /*is_kernel=*/true);
440 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
441 program.witness = {}; // remove the witness to mimick VK construction context
442
443 kernel_vk = construct_kernel_vk_from_acir_program(program);
444 }
445
446 // Compare the VK constructed via running the IVc with the one constructed via mocking
447 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
448}
449
450// Test generation of "inner" kernel VK via dummy IVC data
451TEST_F(HypernovaRecursionConstraintTest, GenerateInnerKernelVKFromConstraints)
452{
454 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
456 {
457 // we have to set the number of circuits one more than the number of circuits we're accumulating as otherwise
458 // the last circuit will be seen as a tail
459 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/6);
460
461 const ProgramMetadata metadata{ ivc };
462
463 { // Construct and accumulate mock app_circuit
464 construct_and_accumulate_mock_app(ivc);
465 }
466
467 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
468 construct_and_accumulate_mock_kernel(ivc);
469
470 { // Construct and accumulate a second mock app_circuit
471 construct_and_accumulate_mock_app(ivc);
472 }
473
474 { // Construct and accumulate a mock INNER kernel (HN recursion for kernel accumulation)
475 EXPECT_TRUE(ivc->verification_queue.size() == 2);
476 EXPECT_TRUE(ivc->verification_queue[1].type == bb::Chonk::QUEUE_TYPE::HN);
477 construct_and_accumulate_mock_kernel(ivc);
478 }
479
480 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
481 }
482
483 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
485 {
486 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/4);
487
488 // Construct kernel consisting only of the kernel completion logic
489 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/true);
490 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/false);
491 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
492 program.witness = {}; // remove the witness to mimick VK construction context
493
494 kernel_vk = construct_kernel_vk_from_acir_program(program);
495 }
496
497 // Compare the VK constructed via running the IVc with the one constructed via mocking
498 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
499}
500
501// Test generation of "hiding" kernel VK via dummy IVC data
502TEST_F(HypernovaRecursionConstraintTest, GenerateMegaVerificationKeyFromConstraints)
503{
505 // First, construct the kernel VK by running the full IVC
506 std::shared_ptr<MegaFlavor::VerificationKey> expected_hiding_kernel_vk;
507 {
508 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
509 const ProgramMetadata metadata{ ivc };
510
511 {
512 // Construct and accumulate mock app_circuit
513 construct_and_accumulate_mock_app(ivc);
514 }
515
516 {
517 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
518 construct_and_accumulate_mock_kernel(ivc);
519 }
520
521 construct_and_accumulate_trailing_kernels(ivc);
522
523 // The single entry in the verification queue corresponds to the hiding kernel
524 expected_hiding_kernel_vk = ivc->verification_queue[0].honk_vk;
525 }
526
527 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
529 {
530 // mock IVC accumulation increases the num_circuits_accumualted, hence we need to assume the tail kernel has
531 // been accumulated
532 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
533 // construct a mock tail kernel
535 Chonk::QUEUE_TYPE::HN_FINAL,
536 /*is_kernel=*/true);
537 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
538 program.witness = {}; // remove the witness to mimick VK construction context
539 kernel_vk = construct_kernel_vk_from_acir_program(program);
540 }
541
542 // Compare the VK constructed via running the IVc with the one constructed via mocking
543 EXPECT_EQ(*kernel_vk.get(), *expected_hiding_kernel_vk.get());
544}
545
549TEST_F(HypernovaRecursionConstraintTest, RecursiveVerifierAppCircuit)
550{
551 auto ivc = std::make_shared<Chonk>(/*num_circuits*/ 5);
552
553 // construct a mock app_circuit with an UH recursion call
554 Builder app_circuit = construct_mock_UH_recursion_app_circuit(ivc, /*tamper_vk=*/false);
555
556 // Complete instance and generate an oink proof
557 ivc->accumulate(app_circuit, get_verification_key(app_circuit));
558
559 // Construct kernel consisting only of the kernel completion logic
560 construct_and_accumulate_mock_kernel(ivc);
561
562 construct_and_accumulate_trailing_kernels(ivc);
563
564 auto proof = ivc->prove();
565 {
566 ChonkNativeVerifier verifier(ivc->get_hiding_kernel_vk_and_hash());
567 EXPECT_TRUE(verifier.verify(proof));
568 }
569}
570
575TEST_F(HypernovaRecursionConstraintTest, RecursiveVerifierAppCircuitFailure)
576{
577 BB_DISABLE_ASSERTS(); // Disable assert in HN prover
578
579 auto ivc = std::make_shared<Chonk>(/*num_circuits*/ 5);
580
581 // construct and accumulate mock app_circuit that has bad pairing point object
582 Builder app_circuit = construct_mock_UH_recursion_app_circuit(ivc, /*tamper_vk=*/true);
583 ivc->accumulate(app_circuit, get_verification_key(app_circuit));
584
585 // Construct kernel consisting only of the kernel completion logic
586 construct_and_accumulate_mock_kernel(ivc);
587
588 // add the trailing kernels
589 construct_and_accumulate_trailing_kernels(ivc);
590
591 // We expect the Chonk proof to fail due to the app with a failed UH recursive verification
592 auto proof = ivc->prove();
593 {
594 ChonkNativeVerifier verifier(ivc->get_hiding_kernel_vk_and_hash());
595 EXPECT_FALSE(verifier.verify(proof));
596 }
597}
598
603{
605 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
606
607 // Mock the post-app accumulation state (OINK proof ready to be verified)
608 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
609
610 // Construct kernel program with gate counting enabled
611 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
612 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
613
614 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
615
616 // Verify the gate count was recorded
617 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
618
619 // Assert gate count
621
622 // Assert ECC row count
623 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
624 EXPECT_EQ(actual_ecc_rows, INIT_KERNEL_ECC_ROWS);
625
626 // Assert ultra ops count
627 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
628 EXPECT_EQ(actual_ultra_ops, INIT_KERNEL_ULTRA_OPS);
629}
630
635{
637 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/4);
638
639 // Mock the state where we need to verify a previous kernel (HN) and a new app (HN)
640 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/true);
641 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/false);
642
643 // Construct kernel program with gate counting enabled
644 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
645 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
646
647 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
648
649 // Verify the gate count was recorded
650 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 2);
651
652 // Assert gate counts (HN verification + OINK verification)
654
655 // Assert ECC row count
656 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
657 EXPECT_EQ(actual_ecc_rows, INNER_KERNEL_ECC_ROWS);
658
659 // Assert ultra ops count
660 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
661 EXPECT_EQ(actual_ultra_ops, INNER_KERNEL_ULTRA_OPS);
662}
663
668{
670 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
671
672 // Mock the state where we need to verify a tail kernel proof
673 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN_TAIL, /*is_kernel=*/true);
674
675 // Construct kernel program with gate counting enabled
676 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
677 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
678
679 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
680
681 // Verify the gate count was recorded
682 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
683
684 // Assert gate count
686
687 // Assert ECC row count
688 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
689 EXPECT_EQ(actual_ecc_rows, TAIL_KERNEL_ECC_ROWS);
690
691 // Assert ultra ops count
692 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
693 EXPECT_EQ(actual_ultra_ops, TAIL_KERNEL_ULTRA_OPS);
694}
695
700{
702 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
703
704 // Mock the state where we need to verify a hiding kernel proof
705 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN_FINAL, /*is_kernel=*/true);
706
707 // Construct kernel program with gate counting enabled
708 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
709 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
710
711 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
712
713 // Verify the gate count was recorded
714 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
715
716 // Assert gate count
718
719 // Assert ECC row count
720 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
721 EXPECT_EQ(actual_ecc_rows, HIDING_KERNEL_ECC_ROWS);
722
723 // Assert ultra ops count
724 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
725 EXPECT_EQ(actual_ultra_ops, HIDING_KERNEL_ULTRA_OPS);
726}
727
728// =====================================================================================
729// Boundary check failure tests - verify that invalid inputs are rejected
730// =====================================================================================
731
735TEST_F(HypernovaRecursionConstraintTest, FailsOnConstraintIndicesSizeMismatch)
736{
737 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
738 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
739
740 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
741
742 // Corrupt the opcode indices to have wrong size
744
745 ProgramMetadata metadata{ .ivc = ivc };
746
747 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
748 "hn_recursion_data constraints/indices size mismatch");
749}
750
754TEST_F(HypernovaRecursionConstraintTest, FailsOnAcirQueueSizeMismatch)
755{
756 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
757 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
758
759 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
760
761 // Add an extra constraint that doesn't exist in the IVC queue
764
765 ProgramMetadata metadata{ .ivc = ivc };
766
767 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
768 "mismatch in number of recursive verifications");
769}
770
774TEST_F(HypernovaRecursionConstraintTest, FailsOnNonEmptyPublicInputs)
775{
776 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
777 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
778
779 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
780
781 // Add public inputs to the constraint (which should be empty for HN)
782 program.constraints.hn_recursion_constraints[0].public_inputs = { 0, 1, 2 };
783
784 ProgramMetadata metadata{ .ivc = ivc };
785
786 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
787 "unexpected non-empty public_inputs in HN constraint");
788}
789
793TEST_F(HypernovaRecursionConstraintTest, FailsOnProofTypeMismatch)
794{
795 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
796 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
797
798 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
799
800 // Change the proof type to something that doesn't match the queue entry
801 // OINK queue entry expects PROOF_TYPE::OINK, change to HN
802 program.constraints.hn_recursion_constraints[0].proof_type = PROOF_TYPE::HN;
803
804 ProgramMetadata metadata{ .ivc = ivc };
805
806 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
807 "ACIR constraint proof_type does not match IVC queue type");
808}
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
Shared type definitions for the Barretenberg RPC API.
static void construct_and_accumulate_mock_app(std::shared_ptr< Chonk > ivc)
static void construct_and_accumulate_mock_kernel(std::shared_ptr< Chonk > ivc)
static UltraCircuitBuilder create_inner_circuit(size_t log_num_gates=10)
static AcirProgram construct_mock_kernel_program(const VerificationQueue &verification_queue)
Generate an acir program {constraints, witness} for a mock kernel.
static void construct_and_accumulate_trailing_kernels(const std::shared_ptr< Chonk > &ivc)
static std::shared_ptr< Chonk::MegaVerificationKey > construct_kernel_vk_from_acir_program(AcirProgram &program)
Construct a kernel circuit VK from an acir program with IVC recursion constraints.
static std::shared_ptr< Chonk::MegaVerificationKey > get_kernel_vk_from_circuit(Builder &kernel)
static Builder construct_mock_app_circuit(const std::shared_ptr< Chonk > &ivc)
Constuct a simple arbitrary circuit to represent a mock app circuit.
static std::shared_ptr< VerificationKey > get_verification_key(Builder &builder_in)
static Builder construct_mock_UH_recursion_app_circuit(const std::shared_ptr< Chonk > &ivc, const bool tamper_vk)
Constuct a mock app circuit with a UH recursive verifier.
static RecursionConstraint create_recursion_constraint(const VerifierInputs &input, std::vector< FF > &witness)
Create an ACIR RecursionConstraint given the corresponding verifier inputs.
QUEUE_TYPE
Proof type determining recursive verification logic in kernel circuits.
Definition chonk.hpp:105
std::deque< VerifierInputs > VerificationQueue
Definition chonk.hpp:114
stdlib::recursion::PairingPoints< stdlib::bn254< ClientCircuit > > PairingPoints
Definition chonk.hpp:65
Verifier for Chonk IVC proofs (both native and recursive).
Output verify(const Proof &proof)
Verify a Chonk proof.
MergeProver::MergeProof MergeProof
Definition goblin.hpp:40
static void add_some_ecc_op_gates(MegaBuilder &builder)
Generate a simple test circuit with some ECC op gates and conventional arithmetic gates.
std::shared_ptr< ECCOpQueue > op_queue
Curve::ScalarField FF
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
The verification key is responsible for storing the commitments to the precomputed (non-witness) poly...
static void add_arithmetic_gates(Builder &builder, const size_t num_gates=4)
Add a specified number of arithmetic gates to the provided circuit.
Base Native verification key class.
Definition flavor.hpp:172
The recursive counterpart to the "native" Ultra flavor.
Output verify_proof(const Proof &proof)
Perform ultra verification.
static constexpr element one
Definition group.hpp:46
A simple wrapper around a vector of stdlib field elements representing a proof.
Definition proof.hpp:19
Manages the data that is propagated on the public inputs of an application/function circuit.
static void add_default(Builder &builder)
Add default public inputs when they are not present.
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
AvmProvingInputs inputs
constexpr size_t INIT_KERNEL_ECC_ROWS
constexpr size_t HIDING_KERNEL_ULTRA_OPS
Goblin::MergeProof create_mock_merge_proof()
Create a mock merge proof which has the correct structure but is not necessarily valid.
RecursionConstraint recursion_data_to_recursion_constraint(std::vector< bb::fr > &witness, const std::vector< bb::fr > &proof, const std::vector< bb::fr > &key, const bb::fr &key_hash, const bb::fr &predicate, const size_t num_public_inputs_to_extract, const uint32_t proof_type)
========== TESTING UTILITIES ========== ///
Definition utils.cpp:53
constexpr size_t HIDING_KERNEL_ECC_ROWS
constexpr size_t TAIL_KERNEL_GATE_COUNT
PROOF_TYPE queue_type_to_proof_type(Chonk::QUEUE_TYPE queue_type)
constexpr size_t TAIL_KERNEL_ULTRA_OPS
constexpr size_t INIT_KERNEL_GATE_COUNT
constexpr size_t INIT_KERNEL_ULTRA_OPS
constexpr size_t INNER_KERNEL_GATE_COUNT_HN
constexpr size_t TAIL_KERNEL_ECC_ROWS
constexpr size_t INNER_KERNEL_ULTRA_OPS
constexpr size_t HIDING_KERNEL_GATE_COUNT
constexpr size_t INNER_KERNEL_ECC_ROWS
void mock_chonk_accumulation(const std::shared_ptr< Chonk > &ivc, Chonk::QUEUE_TYPE type, const bool is_kernel)
Add mock accumulation state to a Chonk instance for a single circuit.
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
TEST_F(BoomerangGoblinRecursiveVerifierTests, graph_description_basic)
Construct and check a goblin recursive verification circuit.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:174
MegaCircuitBuilder_< field< Bn254FrParams > > MegaCircuitBuilder
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
AcirFormatOriginalOpcodeIndices original_opcode_indices
std::vector< RecursionConstraint > hn_recursion_constraints
std::vector< size_t > gates_per_opcode
Indices of the original opcode that originated each constraint in AcirFormat.
Struct containing both the constraints to be added to the circuit and the witness vector.
Metadata required to create a circuit.
std::shared_ptr< bb::IVCBase > ivc
RecursionConstraint struct contains information required to recursively verify a proof.
std::shared_ptr< MegaVerificationKey > honk_vk
Definition chonk.hpp:110
std::vector< FF > proof
Definition chonk.hpp:109
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr field zero()
An object storing two EC points that represent the inputs to a pairing check.
Output type for recursive ultra verification.