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, bool is_hiding_kernel = false)
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);
58 if (is_hiding_kernel) {
60 return std::make_shared<VerificationKey>(prover_instance->get_precomputed());
61 }
63 std::shared_ptr<VerificationKey> vk = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
64 return vk;
65 }
66
68 {
69
70 // Reset kernel
71 EXPECT_EQ(ivc->verification_queue.size(), 1);
72 EXPECT_EQ(ivc->verification_queue[0].type, QUEUE_TYPE::HN);
74
75 // Tail kernel
76 EXPECT_EQ(ivc->verification_queue.size(), 1);
77 EXPECT_EQ(ivc->verification_queue[0].type, QUEUE_TYPE::HN_TAIL);
79
80 // Hiding kernel
81 EXPECT_EQ(ivc->verification_queue.size(), 1);
82 EXPECT_EQ(ivc->verification_queue[0].type, QUEUE_TYPE::HN_FINAL);
84 }
85
86 static UltraCircuitBuilder create_inner_circuit(size_t log_num_gates = 10)
87 {
89
90 // Create 2^log_n many add gates based on input log num gates
91 const size_t num_gates = (1 << log_num_gates);
92 for (size_t i = 0; i < num_gates; ++i) {
94 uint32_t a_idx = builder.add_variable(a);
95
98 fr d = a + b + c;
99 uint32_t b_idx = builder.add_variable(b);
100 uint32_t c_idx = builder.add_variable(c);
101 uint32_t d_idx = builder.add_variable(d);
102
103 builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
104 }
105
107 return builder;
108 }
109
115 {
116 AcirProgram program;
117 std::vector<RecursionConstraint> recursion_constraints;
118
119 Builder circuit{ ivc->goblin.op_queue };
122
123 {
124 using RecursiveFlavor = UltraRecursiveFlavor_<Builder>;
126 using StdlibProof = bb::stdlib::Proof<Builder>;
128
129 // Create an arbitrary inner circuit
130 auto inner_circuit = create_inner_circuit();
131
132 // Compute native verification key
133 auto prover_instance = std::make_shared<ProverInstance_<UltraFlavor>>(inner_circuit);
134 auto honk_vk = std::make_shared<UltraFlavor::VerificationKey>(prover_instance->get_precomputed());
135 UltraProver prover(prover_instance, honk_vk); // A prerequisite for computing VK
136 auto inner_proof = prover.construct_proof();
137
138 if (tamper_vk) {
139 honk_vk->q_l = g1::one;
140 auto honk_vk_and_hash = std::make_shared<UltraFlavor::VKAndHash>(honk_vk);
141 UltraVerifier_<UltraFlavor, DefaultIO> verifier(honk_vk_and_hash);
142 EXPECT_FALSE(verifier.verify_proof(inner_proof).result);
143 }
144 // Instantiate the recursive verifier using the native verification key
145 auto stdlib_vk_and_hash = std::make_shared<RecursiveFlavor::VKAndHash>(circuit, honk_vk);
146 bb::UltraVerifier_<RecursiveFlavor, StdlibIO> verifier(stdlib_vk_and_hash);
147
148 StdlibProof stdlib_inner_proof(circuit, inner_proof);
149 VerifierOutput output = verifier.verify_proof(stdlib_inner_proof);
150
151 // IO
152 StdlibIO inputs;
153 inputs.pairing_inputs = output.points_accumulator;
154 inputs.set_public(); // propagate resulting pairing points on the public inputs
155 }
156
157 return circuit;
158 }
159
168 static RecursionConstraint create_recursion_constraint(const VerifierInputs& input, std::vector<FF>& witness)
169 {
170 // Use centralized conversion from QUEUE_TYPE to PROOF_TYPE
171 PROOF_TYPE proof_type = queue_type_to_proof_type(input.type);
172
173 RecursionConstraint constraint =
175 input.proof, // proof contains the public inputs at this stage
176 input.honk_vk->to_field_elements(),
177 input.honk_vk->hash(),
178 bb::fr::zero(),
179 /*num_public_inputs_to_extract=*/0,
180 proof_type);
181
182 constraint.proof = {}; // the proof witness indices are not needed in an ivc recursion constraint
183
184 return constraint;
185 }
186
201 {
202 AcirProgram program;
203
204 // Construct recursion constraints based on the ivc verification queue; populate the witness along the way
205 std::vector<RecursionConstraint> hn_recursion_constraints;
206 hn_recursion_constraints.reserve(verification_queue.size());
207 for (const auto& queue_entry : verification_queue) {
208 hn_recursion_constraints.push_back(create_recursion_constraint(queue_entry, program.witness));
209 }
210
211 // Construct a constraint system containing the business logic and ivc recursion constraints
212 program.constraints.max_witness_index = static_cast<uint32_t>(program.witness.size() - 1);
213 program.constraints.num_acir_opcodes = static_cast<uint32_t>(hn_recursion_constraints.size());
214 program.constraints.hn_recursion_constraints = hn_recursion_constraints;
216 hn_recursion_constraints.size() == 1
219
220 return program;
221 }
222
224 {
225 // construct a mock kernel program (acir) from the ivc verification queue
226 const ProgramMetadata metadata{ ivc };
227 AcirProgram mock_kernel_program = construct_mock_kernel_program(ivc->verification_queue);
228 auto kernel = acir_format::create_circuit<Builder>(mock_kernel_program, metadata);
229 const bool is_hiding_kernel = ivc->num_circuits_accumulated == ivc->get_num_circuits() - 1;
230 auto kernel_vk = get_kernel_vk_from_circuit(kernel, is_hiding_kernel);
231 ivc->accumulate(kernel, kernel_vk);
232 }
233
235 {
236 // construct a mock kernel program (acir) from the ivc verification queue
237 auto app_circuit = construct_mock_app_circuit(ivc);
238 ivc->accumulate(app_circuit, get_verification_key(app_circuit));
239 }
240
248 AcirProgram& program, bool is_hiding_kernel = false)
249 {
250 // Create kernel circuit from the kernel program
251 auto kernel = acir_format::create_circuit<Builder>(program);
252 if (is_hiding_kernel) {
253 auto prover_instance = std::make_shared<Chonk::HidingKernelProverInstance>(kernel);
254 return std::make_shared<Chonk::MegaVerificationKey>(prover_instance->get_precomputed());
255 }
256 auto prover_instance = std::make_shared<Chonk::ProverInstance>(kernel);
257 return std::make_shared<Chonk::MegaVerificationKey>(prover_instance->get_precomputed());
258 }
259
261 bool is_hiding_kernel = false)
262 {
263 if (is_hiding_kernel) {
264 auto prover_instance = std::make_shared<Chonk::HidingKernelProverInstance>(kernel);
265 return std::make_shared<Chonk::MegaVerificationKey>(prover_instance->get_precomputed());
266 }
267 auto prover_instance = std::make_shared<Chonk::ProverInstance>(kernel);
268 return std::make_shared<Chonk::MegaVerificationKey>(prover_instance->get_precomputed());
269 }
270
271 protected:
273};
274
279{
281 EXPECT_EQ(merge_proof.size(), MERGE_PROOF_SIZE);
282}
283
289{
290 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5 /* app, kernel, reset, tail, hiding */);
291
292 // construct a mock app_circuit
293 construct_and_accumulate_mock_app(ivc);
294
295 // Construct kernel consisting only of the kernel completion logic
296 construct_and_accumulate_mock_kernel(ivc);
297
298 // add the trailing kernels
299 construct_and_accumulate_trailing_kernels(ivc);
300
301 auto proof = ivc->prove();
302 {
303 auto vk_and_hash = ivc->get_hiding_kernel_vk_and_hash();
304 ChonkNativeVerifier verifier(vk_and_hash);
305 EXPECT_TRUE(verifier.verify(proof));
306 }
307}
308
314{
315 // 4 ciruits and the tail kernel
316 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/7);
317
318 // construct a mock app_circuit
319 construct_and_accumulate_mock_app(ivc);
320
321 const ProgramMetadata metadata{ ivc };
322
323 // Construct kernel_0; consists of a single oink recursive verification for app (plus databus/merge logic)
324 construct_and_accumulate_mock_kernel(ivc);
325
326 // construct a mock app_circuit
327 construct_and_accumulate_mock_app(ivc);
328
329 // Construct and accumulate another Kernel circuit
330 construct_and_accumulate_mock_kernel(ivc);
331
332 // Accumulate the trailing kernels
333 construct_and_accumulate_trailing_kernels(ivc);
334
335 auto proof = ivc->prove();
336 {
337 ChonkNativeVerifier verifier(ivc->get_hiding_kernel_vk_and_hash());
338 EXPECT_TRUE(verifier.verify(proof));
339 }
340}
341
342// Test generation of "init" kernel VK via dummy IVC data
343TEST_F(HypernovaRecursionConstraintTest, GenerateInitKernelVKFromConstraints)
344{
346 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
348 {
349 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
350
351 // Construct and accumulate mock app_circuit
352 construct_and_accumulate_mock_app(ivc);
353
354 // Construct and accumulate kernel consisting only of the kernel completion logic
355 construct_and_accumulate_mock_kernel(ivc);
356 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
357 }
358
359 // Now, construct the kernel VK by mocking the post app accumulation state of the IVC
361 {
362 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
363
364 // Construct kernel consisting only of the kernel completion logic
365 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
366 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
367 program.witness = {}; // remove the witness to mimick VK construction context
368
369 kernel_vk = construct_kernel_vk_from_acir_program(program);
370 }
371
372 // Compare the VK constructed via running the IVc with the one constructed via mocking
373 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
374}
375
376// Test generation of "reset" kernel VK via dummy IVC data
377TEST_F(HypernovaRecursionConstraintTest, GenerateResetKernelVKFromConstraints)
378{
380 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
382 {
383 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
384
385 const ProgramMetadata metadata{ ivc };
386
387 // Construct and accumulate mock app_circuit
388 construct_and_accumulate_mock_app(ivc);
389
390 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
391 construct_and_accumulate_mock_kernel(ivc);
392 EXPECT_TRUE(ivc->verification_queue.size() == 1);
393 EXPECT_TRUE(ivc->verification_queue[0].type == bb::Chonk::QUEUE_TYPE::HN);
394
395 // Construct and accumulate a mock RESET kernel (HN recursion for kernel accumulation)
396 construct_and_accumulate_mock_kernel(ivc);
397 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
398 }
399
400 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
402 {
403 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
404
405 // Construct kernel consisting only of the kernel completion logic
406 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/true);
407 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
408 program.witness = {}; // remove the witness to mimick VK construction context
409 kernel_vk = construct_kernel_vk_from_acir_program(program);
410 }
411
412 // Compare the VK constructed via running the IVc with the one constructed via mocking
413 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
414}
415
416// Test generation of "tail" kernel VK via dummy IVC data
417TEST_F(HypernovaRecursionConstraintTest, GenerateTailKernelVKFromConstraints)
418{
420 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
422 {
423 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
424
425 const ProgramMetadata metadata{ ivc };
426
427 // Construct and accumulate mock app_circuit
428 construct_and_accumulate_mock_app(ivc);
429
430 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
431 construct_and_accumulate_mock_kernel(ivc);
432
433 // Construct and accumulate a mock RESET kernel (HN recursion for kernel accumulation)
434 construct_and_accumulate_mock_kernel(ivc);
435
436 // Construct and accumulate a mock TAIL kernel (HN recursion for kernel accumulation)
437 EXPECT_TRUE(ivc->verification_queue.size() == 1);
438 EXPECT_TRUE(ivc->verification_queue[0].type == bb::Chonk::QUEUE_TYPE::HN_TAIL);
439 construct_and_accumulate_mock_kernel(ivc);
440
441 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
442 }
443
444 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
446 {
447 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
448
449 // Construct kernel consisting only of the kernel completion logic
450 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN_TAIL, /*is_kernel=*/true);
451 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
452 program.witness = {}; // remove the witness to mimick VK construction context
453
454 kernel_vk = construct_kernel_vk_from_acir_program(program);
455 }
456
457 // Compare the VK constructed via running the IVc with the one constructed via mocking
458 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
459}
460
461// Test generation of "inner" kernel VK via dummy IVC data
462TEST_F(HypernovaRecursionConstraintTest, GenerateInnerKernelVKFromConstraints)
463{
465 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
467 {
468 // we have to set the number of circuits one more than the number of circuits we're accumulating as otherwise
469 // the last circuit will be seen as a tail
470 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/6);
471
472 const ProgramMetadata metadata{ ivc };
473
474 { // Construct and accumulate mock app_circuit
475 construct_and_accumulate_mock_app(ivc);
476 }
477
478 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
479 construct_and_accumulate_mock_kernel(ivc);
480
481 { // Construct and accumulate a second mock app_circuit
482 construct_and_accumulate_mock_app(ivc);
483 }
484
485 { // Construct and accumulate a mock INNER kernel (HN recursion for kernel accumulation)
486 EXPECT_TRUE(ivc->verification_queue.size() == 2);
487 EXPECT_TRUE(ivc->verification_queue[1].type == bb::Chonk::QUEUE_TYPE::HN);
488 construct_and_accumulate_mock_kernel(ivc);
489 }
490
491 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
492 }
493
494 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
496 {
497 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/4);
498
499 // Construct kernel consisting only of the kernel completion logic
500 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/true);
501 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/false);
502 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
503 program.witness = {}; // remove the witness to mimick VK construction context
504
505 kernel_vk = construct_kernel_vk_from_acir_program(program);
506 }
507
508 // Compare the VK constructed via running the IVc with the one constructed via mocking
509 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
510}
511
512// Test generation of "hiding" kernel VK via dummy IVC data
513TEST_F(HypernovaRecursionConstraintTest, GenerateMegaVerificationKeyFromConstraints)
514{
516 // First, construct the kernel VK by running the full IVC
517 std::shared_ptr<MegaFlavor::VerificationKey> expected_hiding_kernel_vk;
518 {
519 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
520 const ProgramMetadata metadata{ ivc };
521
522 {
523 // Construct and accumulate mock app_circuit
524 construct_and_accumulate_mock_app(ivc);
525 }
526
527 {
528 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
529 construct_and_accumulate_mock_kernel(ivc);
530 }
531
532 construct_and_accumulate_trailing_kernels(ivc);
533
534 // The single entry in the verification queue corresponds to the hiding kernel
535 expected_hiding_kernel_vk = ivc->verification_queue[0].honk_vk;
536 }
537
538 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
540 {
541 // mock IVC accumulation increases the num_circuits_accumualted, hence we need to assume the tail kernel has
542 // been accumulated
543 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
544 // construct a mock tail kernel
546 Chonk::QUEUE_TYPE::HN_FINAL,
547 /*is_kernel=*/true);
548 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
549 program.witness = {}; // remove the witness to mimick VK construction context
550 kernel_vk = construct_kernel_vk_from_acir_program(program, /*is_hiding_kernel=*/true);
551 }
552
553 // Compare the VK constructed via running the IVc with the one constructed via mocking
554 EXPECT_EQ(*kernel_vk.get(), *expected_hiding_kernel_vk.get());
555}
556
560TEST_F(HypernovaRecursionConstraintTest, RecursiveVerifierAppCircuit)
561{
562 auto ivc = std::make_shared<Chonk>(/*num_circuits*/ 5);
563
564 // construct a mock app_circuit with an UH recursion call
565 Builder app_circuit = construct_mock_UH_recursion_app_circuit(ivc, /*tamper_vk=*/false);
566
567 // Complete instance and generate an oink proof
568 ivc->accumulate(app_circuit, get_verification_key(app_circuit));
569
570 // Construct kernel consisting only of the kernel completion logic
571 construct_and_accumulate_mock_kernel(ivc);
572
573 construct_and_accumulate_trailing_kernels(ivc);
574
575 auto proof = ivc->prove();
576 {
577 ChonkNativeVerifier verifier(ivc->get_hiding_kernel_vk_and_hash());
578 EXPECT_TRUE(verifier.verify(proof));
579 }
580}
581
586TEST_F(HypernovaRecursionConstraintTest, RecursiveVerifierAppCircuitFailure)
587{
588 BB_DISABLE_ASSERTS(); // Disable assert in HN prover
589
590 auto ivc = std::make_shared<Chonk>(/*num_circuits*/ 5);
591
592 // construct and accumulate mock app_circuit that has bad pairing point object
593 Builder app_circuit = construct_mock_UH_recursion_app_circuit(ivc, /*tamper_vk=*/true);
594 ivc->accumulate(app_circuit, get_verification_key(app_circuit));
595
596 // Construct kernel consisting only of the kernel completion logic
597 construct_and_accumulate_mock_kernel(ivc);
598
599 // add the trailing kernels
600 construct_and_accumulate_trailing_kernels(ivc);
601
602 // We expect the Chonk proof to fail due to the app with a failed UH recursive verification
603 auto proof = ivc->prove();
604 {
605 ChonkNativeVerifier verifier(ivc->get_hiding_kernel_vk_and_hash());
606 EXPECT_FALSE(verifier.verify(proof));
607 }
608}
609
614{
616 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
617
618 // Mock the post-app accumulation state (OINK proof ready to be verified)
619 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
620
621 // Construct kernel program with gate counting enabled
622 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
623 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
624
625 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
626
627 // Verify the gate count was recorded
628 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
629
630 // Assert gate count
632
633 // Assert ECC row count
634 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
635 EXPECT_EQ(actual_ecc_rows, INIT_KERNEL_ECC_ROWS);
636
637 // Assert ultra ops count
638 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
639 EXPECT_EQ(actual_ultra_ops, INIT_KERNEL_ULTRA_OPS);
640}
641
646{
648 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/4);
649
650 // Mock the state where we need to verify a previous kernel (HN) and a new app (HN)
651 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/true);
652 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/false);
653
654 // Construct kernel program with gate counting enabled
655 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
656 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
657
658 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
659
660 // Verify the gate count was recorded
661 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 2);
662
663 // Assert gate counts (HN verification + OINK verification)
665
666 // Assert ECC row count
667 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
668 EXPECT_EQ(actual_ecc_rows, INNER_KERNEL_ECC_ROWS);
669
670 // Assert ultra ops count
671 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
672 EXPECT_EQ(actual_ultra_ops, INNER_KERNEL_ULTRA_OPS);
673}
674
679{
681 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
682
683 // Mock the state where we need to verify a tail kernel proof
684 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN_TAIL, /*is_kernel=*/true);
685
686 // Construct kernel program with gate counting enabled
687 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
688 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
689
690 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
691
692 // Verify the gate count was recorded
693 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
694
695 // Assert gate count
697
698 // Assert ECC row count
699 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
700 EXPECT_EQ(actual_ecc_rows, TAIL_KERNEL_ECC_ROWS);
701
702 // Assert ultra ops count
703 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
704 EXPECT_EQ(actual_ultra_ops, TAIL_KERNEL_ULTRA_OPS);
705}
706
711{
713 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
714
715 // Mock the state where we need to verify a hiding kernel proof
716 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN_FINAL, /*is_kernel=*/true);
717
718 // Construct kernel program with gate counting enabled
719 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
720 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
721
722 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
723
724 // Verify the gate count was recorded
725 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
726
727 // Assert gate count
729
730 // Assert ECC row count
731 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
732 EXPECT_EQ(actual_ecc_rows, HIDING_KERNEL_ECC_ROWS);
733
734 // Assert ultra ops count
735 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
736 EXPECT_EQ(actual_ultra_ops, HIDING_KERNEL_ULTRA_OPS);
737}
738
739// =====================================================================================
740// Boundary check failure tests - verify that invalid inputs are rejected
741// =====================================================================================
742
746TEST_F(HypernovaRecursionConstraintTest, FailsOnConstraintIndicesSizeMismatch)
747{
748 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
749 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
750
751 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
752
753 // Corrupt the opcode indices to have wrong size
755
756 ProgramMetadata metadata{ .ivc = ivc };
757
758 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
759 "hn_recursion_data constraints/indices size mismatch");
760}
761
765TEST_F(HypernovaRecursionConstraintTest, FailsOnAcirQueueSizeMismatch)
766{
767 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
768 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
769
770 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
771
772 // Add an extra constraint that doesn't exist in the IVC queue
775
776 ProgramMetadata metadata{ .ivc = ivc };
777
778 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
779 "mismatch in number of recursive verifications");
780}
781
785TEST_F(HypernovaRecursionConstraintTest, FailsOnNonEmptyPublicInputs)
786{
787 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
788 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
789
790 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
791
792 // Add public inputs to the constraint (which should be empty for HN)
793 program.constraints.hn_recursion_constraints[0].public_inputs = { 0, 1, 2 };
794
795 ProgramMetadata metadata{ .ivc = ivc };
796
797 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
798 "unexpected non-empty public_inputs in HN constraint");
799}
800
804TEST_F(HypernovaRecursionConstraintTest, FailsOnProofTypeMismatch)
805{
806 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
807 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
808
809 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
810
811 // Change the proof type to something that doesn't match the queue entry
812 // OINK queue entry expects PROOF_TYPE::OINK, change to HN
813 program.constraints.hn_recursion_constraints[0].proof_type = PROOF_TYPE::HN;
814
815 ProgramMetadata metadata{ .ivc = ivc };
816
817 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
818 "ACIR constraint proof_type does not match IVC queue type");
819}
#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 std::shared_ptr< Chonk::MegaVerificationKey > get_kernel_vk_from_circuit(Builder &kernel, bool is_hiding_kernel=false)
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, bool is_hiding_kernel=false)
Construct a kernel circuit VK from an acir program with IVC recursion constraints.
static Builder construct_mock_app_circuit(const std::shared_ptr< Chonk > &ivc)
Constuct a simple arbitrary circuit to represent a mock app circuit.
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 std::shared_ptr< VerificationKey > get_verification_key(Builder &builder_in, bool is_hiding_kernel=false)
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:117
std::deque< VerifierInputs > VerificationQueue
Definition chonk.hpp:126
stdlib::recursion::PairingPoints< stdlib::bn254< ClientCircuit > > PairingPoints
Definition chonk.hpp:66
Verifier for Chonk IVC proofs (both native and recursive).
Output verify(const Proof &proof)
Verify a Chonk proof.
MergeProver::MergeProof MergeProof
Definition goblin.hpp:42
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 stores commitments to the precomputed (non-witness) polynomials used by the veri...
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:135
The recursive counterpart to the "native" Ultra flavor.
Output verify_proof(const Proof &proof)
Perform ultra verification.
static constexpr element one
Definition group.hpp:48
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:155
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:122
std::vector< FF > proof
Definition chonk.hpp:121
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr field zero()
Output type for recursive ultra verification.