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;
215 for (size_t idx = 0; idx < hn_recursion_constraints.size(); ++idx) {
216 program.constraints.original_opcode_indices.hn_recursion_constraints.push_back(static_cast<uint32_t>(idx));
217 }
218
219 return program;
220 }
221
223 {
224 // construct a mock kernel program (acir) from the ivc verification queue
225 const ProgramMetadata metadata{ ivc };
226 AcirProgram mock_kernel_program = construct_mock_kernel_program(ivc->verification_queue);
227 auto kernel = acir_format::create_circuit<Builder>(mock_kernel_program, metadata);
228 const bool is_hiding_kernel = ivc->num_circuits_accumulated == ivc->get_num_circuits() - 1;
229 auto kernel_vk = get_kernel_vk_from_circuit(kernel, is_hiding_kernel);
230 ivc->accumulate(kernel, kernel_vk);
231 }
232
234 {
235 // construct a mock kernel program (acir) from the ivc verification queue
236 auto app_circuit = construct_mock_app_circuit(ivc);
237 ivc->accumulate(app_circuit, get_verification_key(app_circuit));
238 }
239
247 AcirProgram& program, bool is_hiding_kernel = false)
248 {
249 // Create kernel circuit from the kernel program
250 auto kernel = acir_format::create_circuit<Builder>(program);
251 if (is_hiding_kernel) {
252 auto prover_instance = std::make_shared<Chonk::HidingKernelProverInstance>(kernel);
253 return std::make_shared<Chonk::MegaVerificationKey>(prover_instance->get_precomputed());
254 }
255 auto prover_instance = std::make_shared<Chonk::ProverInstance>(kernel);
256 return std::make_shared<Chonk::MegaVerificationKey>(prover_instance->get_precomputed());
257 }
258
260 bool is_hiding_kernel = false)
261 {
262 if (is_hiding_kernel) {
263 auto prover_instance = std::make_shared<Chonk::HidingKernelProverInstance>(kernel);
264 return std::make_shared<Chonk::MegaVerificationKey>(prover_instance->get_precomputed());
265 }
266 auto prover_instance = std::make_shared<Chonk::ProverInstance>(kernel);
267 return std::make_shared<Chonk::MegaVerificationKey>(prover_instance->get_precomputed());
268 }
269
270 protected:
272};
273
278{
280 EXPECT_EQ(merge_proof.size(), MERGE_PROOF_SIZE);
281}
282
288{
289 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5 /* app, kernel, reset, tail, hiding */);
290
291 // construct a mock app_circuit
292 construct_and_accumulate_mock_app(ivc);
293
294 // Construct kernel consisting only of the kernel completion logic
295 construct_and_accumulate_mock_kernel(ivc);
296
297 // add the trailing kernels
298 construct_and_accumulate_trailing_kernels(ivc);
299
300 auto proof = ivc->prove();
301 {
302 auto vk_and_hash = ivc->get_hiding_kernel_vk_and_hash();
303 ChonkNativeVerifier verifier(vk_and_hash);
304 EXPECT_TRUE(verifier.verify(proof));
305 }
306}
307
313{
314 // 4 ciruits and the tail kernel
315 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/7);
316
317 // construct a mock app_circuit
318 construct_and_accumulate_mock_app(ivc);
319
320 const ProgramMetadata metadata{ ivc };
321
322 // Construct kernel_0; consists of a single oink recursive verification for app (plus databus/merge logic)
323 construct_and_accumulate_mock_kernel(ivc);
324
325 // construct a mock app_circuit
326 construct_and_accumulate_mock_app(ivc);
327
328 // Construct and accumulate another Kernel circuit
329 construct_and_accumulate_mock_kernel(ivc);
330
331 // Accumulate the trailing kernels
332 construct_and_accumulate_trailing_kernels(ivc);
333
334 auto proof = ivc->prove();
335 {
336 ChonkNativeVerifier verifier(ivc->get_hiding_kernel_vk_and_hash());
337 EXPECT_TRUE(verifier.verify(proof));
338 }
339}
340
341// Test generation of "init" kernel VK via dummy IVC data
342TEST_F(HypernovaRecursionConstraintTest, GenerateInitKernelVKFromConstraints)
343{
345 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
347 {
348 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
349
350 // Construct and accumulate mock app_circuit
351 construct_and_accumulate_mock_app(ivc);
352
353 // Construct and accumulate kernel consisting only of the kernel completion logic
354 construct_and_accumulate_mock_kernel(ivc);
355 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
356 }
357
358 // Now, construct the kernel VK by mocking the post app accumulation state of the IVC
360 {
361 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
362
363 // Construct kernel consisting only of the kernel completion logic
364 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
365 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
366 program.witness = {}; // remove the witness to mimick VK construction context
367
368 kernel_vk = construct_kernel_vk_from_acir_program(program);
369 }
370
371 // Compare the VK constructed via running the IVc with the one constructed via mocking
372 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
373}
374
375// Test generation of "reset" kernel VK via dummy IVC data
376TEST_F(HypernovaRecursionConstraintTest, GenerateResetKernelVKFromConstraints)
377{
379 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
381 {
382 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
383
384 const ProgramMetadata metadata{ ivc };
385
386 // Construct and accumulate mock app_circuit
387 construct_and_accumulate_mock_app(ivc);
388
389 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
390 construct_and_accumulate_mock_kernel(ivc);
391 EXPECT_TRUE(ivc->verification_queue.size() == 1);
392 EXPECT_TRUE(ivc->verification_queue[0].type == bb::Chonk::QUEUE_TYPE::HN);
393
394 // Construct and accumulate a mock RESET kernel (HN recursion for kernel accumulation)
395 construct_and_accumulate_mock_kernel(ivc);
396 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
397 }
398
399 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
401 {
402 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
403
404 // Construct kernel consisting only of the kernel completion logic
405 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/true);
406 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
407 program.witness = {}; // remove the witness to mimick VK construction context
408 kernel_vk = construct_kernel_vk_from_acir_program(program);
409 }
410
411 // Compare the VK constructed via running the IVc with the one constructed via mocking
412 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
413}
414
415// Test generation of "tail" kernel VK via dummy IVC data
416TEST_F(HypernovaRecursionConstraintTest, GenerateTailKernelVKFromConstraints)
417{
419 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
421 {
422 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
423
424 const ProgramMetadata metadata{ ivc };
425
426 // Construct and accumulate mock app_circuit
427 construct_and_accumulate_mock_app(ivc);
428
429 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
430 construct_and_accumulate_mock_kernel(ivc);
431
432 // Construct and accumulate a mock RESET kernel (HN recursion for kernel accumulation)
433 construct_and_accumulate_mock_kernel(ivc);
434
435 // Construct and accumulate a mock TAIL kernel (HN recursion for kernel accumulation)
436 EXPECT_TRUE(ivc->verification_queue.size() == 1);
437 EXPECT_TRUE(ivc->verification_queue[0].type == bb::Chonk::QUEUE_TYPE::HN_TAIL);
438 construct_and_accumulate_mock_kernel(ivc);
439
440 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
441 }
442
443 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
445 {
446 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
447
448 // Construct kernel consisting only of the kernel completion logic
449 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN_TAIL, /*is_kernel=*/true);
450 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
451 program.witness = {}; // remove the witness to mimick VK construction context
452
453 kernel_vk = construct_kernel_vk_from_acir_program(program);
454 }
455
456 // Compare the VK constructed via running the IVc with the one constructed via mocking
457 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
458}
459
460// Test generation of "inner" kernel VK via dummy IVC data
461TEST_F(HypernovaRecursionConstraintTest, GenerateInnerKernelVKFromConstraints)
462{
464 // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel)
466 {
467 // we have to set the number of circuits one more than the number of circuits we're accumulating as otherwise
468 // the last circuit will be seen as a tail
469 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/6);
470
471 const ProgramMetadata metadata{ ivc };
472
473 { // Construct and accumulate mock app_circuit
474 construct_and_accumulate_mock_app(ivc);
475 }
476
477 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
478 construct_and_accumulate_mock_kernel(ivc);
479
480 { // Construct and accumulate a second mock app_circuit
481 construct_and_accumulate_mock_app(ivc);
482 }
483
484 { // Construct and accumulate a mock INNER kernel (HN recursion for kernel accumulation)
485 EXPECT_TRUE(ivc->verification_queue.size() == 2);
486 EXPECT_TRUE(ivc->verification_queue[1].type == bb::Chonk::QUEUE_TYPE::HN);
487 construct_and_accumulate_mock_kernel(ivc);
488 }
489
490 expected_kernel_vk = ivc->verification_queue.back().honk_vk;
491 }
492
493 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
495 {
496 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/4);
497
498 // Construct kernel consisting only of the kernel completion logic
499 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/true);
500 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/false);
501 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
502 program.witness = {}; // remove the witness to mimick VK construction context
503
504 kernel_vk = construct_kernel_vk_from_acir_program(program);
505 }
506
507 // Compare the VK constructed via running the IVc with the one constructed via mocking
508 EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get());
509}
510
511// Test generation of "hiding" kernel VK via dummy IVC data
512TEST_F(HypernovaRecursionConstraintTest, GenerateMegaVerificationKeyFromConstraints)
513{
515 // First, construct the kernel VK by running the full IVC
516 std::shared_ptr<MegaFlavor::VerificationKey> expected_hiding_kernel_vk;
517 {
518 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
519 const ProgramMetadata metadata{ ivc };
520
521 {
522 // Construct and accumulate mock app_circuit
523 construct_and_accumulate_mock_app(ivc);
524 }
525
526 {
527 // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation)
528 construct_and_accumulate_mock_kernel(ivc);
529 }
530
531 construct_and_accumulate_trailing_kernels(ivc);
532
533 // The single entry in the verification queue corresponds to the hiding kernel
534 expected_hiding_kernel_vk = ivc->verification_queue[0].honk_vk;
535 }
536
537 // Now, construct the kernel VK by mocking the IVC state prior to kernel construction
539 {
540 // mock IVC accumulation increases the num_circuits_accumualted, hence we need to assume the tail kernel has
541 // been accumulated
542 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
543 // construct a mock tail kernel
545 Chonk::QUEUE_TYPE::HN_FINAL,
546 /*is_kernel=*/true);
547 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
548 program.witness = {}; // remove the witness to mimick VK construction context
549 kernel_vk = construct_kernel_vk_from_acir_program(program, /*is_hiding_kernel=*/true);
550 }
551
552 // Compare the VK constructed via running the IVc with the one constructed via mocking
553 EXPECT_EQ(*kernel_vk.get(), *expected_hiding_kernel_vk.get());
554}
555
559TEST_F(HypernovaRecursionConstraintTest, RecursiveVerifierAppCircuit)
560{
561 auto ivc = std::make_shared<Chonk>(/*num_circuits*/ 5);
562
563 // construct a mock app_circuit with an UH recursion call
564 Builder app_circuit = construct_mock_UH_recursion_app_circuit(ivc, /*tamper_vk=*/false);
565
566 // Complete instance and generate an oink proof
567 ivc->accumulate(app_circuit, get_verification_key(app_circuit));
568
569 // Construct kernel consisting only of the kernel completion logic
570 construct_and_accumulate_mock_kernel(ivc);
571
572 construct_and_accumulate_trailing_kernels(ivc);
573
574 auto proof = ivc->prove();
575 {
576 ChonkNativeVerifier verifier(ivc->get_hiding_kernel_vk_and_hash());
577 EXPECT_TRUE(verifier.verify(proof));
578 }
579}
580
585TEST_F(HypernovaRecursionConstraintTest, RecursiveVerifierAppCircuitFailure)
586{
587 BB_DISABLE_ASSERTS(); // Disable assert in HN prover
588
589 auto ivc = std::make_shared<Chonk>(/*num_circuits*/ 5);
590
591 // construct and accumulate mock app_circuit that has bad pairing point object
592 Builder app_circuit = construct_mock_UH_recursion_app_circuit(ivc, /*tamper_vk=*/true);
593 ivc->accumulate(app_circuit, get_verification_key(app_circuit));
594
595 // Construct kernel consisting only of the kernel completion logic
596 construct_and_accumulate_mock_kernel(ivc);
597
598 // add the trailing kernels
599 construct_and_accumulate_trailing_kernels(ivc);
600
601 // We expect the Chonk proof to fail due to the app with a failed UH recursive verification
602 auto proof = ivc->prove();
603 {
604 ChonkNativeVerifier verifier(ivc->get_hiding_kernel_vk_and_hash());
605 EXPECT_FALSE(verifier.verify(proof));
606 }
607}
608
613{
615 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
616
617 // Mock the post-app accumulation state (OINK proof ready to be verified)
618 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
619
620 // Construct kernel program with gate counting enabled
621 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
622 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
623
624 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
625
626 // Verify the gate count was recorded
627 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
628
629 // Assert gate count
631
632 // Assert ECC row count
633 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
634 EXPECT_EQ(actual_ecc_rows, INIT_KERNEL_ECC_ROWS);
635
636 // Assert ultra ops count
637 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
638 EXPECT_EQ(actual_ultra_ops, INIT_KERNEL_ULTRA_OPS);
639}
640
645{
647 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/4);
648
649 // Mock the state where we need to verify a previous kernel (HN) and a new app (HN)
650 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/true);
651 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN, /*is_kernel=*/false);
652
653 // Construct kernel program with gate counting enabled
654 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
655 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
656
657 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
658
659 // Verify the gate count was recorded
660 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 2);
661
662 // Assert gate counts (HN verification + OINK verification)
664
665 // Assert ECC row count
666 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
667 EXPECT_EQ(actual_ecc_rows, INNER_KERNEL_ECC_ROWS);
668
669 // Assert ultra ops count
670 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
671 EXPECT_EQ(actual_ultra_ops, INNER_KERNEL_ULTRA_OPS);
672}
673
678{
680 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
681
682 // Mock the state where we need to verify a tail kernel proof
683 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN_TAIL, /*is_kernel=*/true);
684
685 // Construct kernel program with gate counting enabled
686 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
687 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
688
689 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
690
691 // Verify the gate count was recorded
692 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
693
694 // Assert gate count
696
697 // Assert ECC row count
698 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
699 EXPECT_EQ(actual_ecc_rows, TAIL_KERNEL_ECC_ROWS);
700
701 // Assert ultra ops count
702 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
703 EXPECT_EQ(actual_ultra_ops, TAIL_KERNEL_ULTRA_OPS);
704}
705
710{
712 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
713
714 // Mock the state where we need to verify a hiding kernel proof
715 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::HN_FINAL, /*is_kernel=*/true);
716
717 // Construct kernel program with gate counting enabled
718 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
719 ProgramMetadata metadata{ .ivc = ivc, .collect_gates_per_opcode = true };
720
721 auto kernel = acir_format::create_circuit<Builder>(program, metadata);
722
723 // Verify the gate count was recorded
724 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
725
726 // Assert gate count
728
729 // Assert ECC row count
730 size_t actual_ecc_rows = kernel.op_queue->get_num_rows();
731 EXPECT_EQ(actual_ecc_rows, HIDING_KERNEL_ECC_ROWS);
732
733 // Assert ultra ops count
734 size_t actual_ultra_ops = kernel.op_queue->get_current_subtable_size();
735 EXPECT_EQ(actual_ultra_ops, HIDING_KERNEL_ULTRA_OPS);
736}
737
738// =====================================================================================
739// Boundary check failure tests - verify that invalid inputs are rejected
740// =====================================================================================
741
745TEST_F(HypernovaRecursionConstraintTest, FailsOnConstraintIndicesSizeMismatch)
746{
747 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
748 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
749
750 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
751
752 // Corrupt the opcode indices to have wrong size
754
755 ProgramMetadata metadata{ .ivc = ivc };
756
757 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
758 "hn_recursion_data constraints/indices size mismatch");
759}
760
764TEST_F(HypernovaRecursionConstraintTest, FailsOnAcirQueueSizeMismatch)
765{
766 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
767 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
768
769 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
770
771 // Add an extra constraint that doesn't exist in the IVC queue
774
775 ProgramMetadata metadata{ .ivc = ivc };
776
777 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
778 "mismatch in number of recursive verifications");
779}
780
784TEST_F(HypernovaRecursionConstraintTest, FailsOnNonEmptyPublicInputs)
785{
786 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
787 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
788
789 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
790
791 // Add public inputs to the constraint (which should be empty for HN)
792 program.constraints.hn_recursion_constraints[0].public_inputs = { 0, 1, 2 };
793
794 ProgramMetadata metadata{ .ivc = ivc };
795
796 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
797 "unexpected non-empty public_inputs in HN constraint");
798}
799
803TEST_F(HypernovaRecursionConstraintTest, FailsOnProofTypeMismatch)
804{
805 auto ivc = std::make_shared<Chonk>(/*num_circuits=*/5);
806 acir_format::mock_chonk_accumulation(ivc, Chonk::QUEUE_TYPE::OINK, /*is_kernel=*/false);
807
808 AcirProgram program = construct_mock_kernel_program(ivc->verification_queue);
809
810 // Change the proof type to something that doesn't match the queue entry
811 // OINK queue entry expects PROOF_TYPE::OINK, change to HN
812 program.constraints.hn_recursion_constraints[0].proof_type = PROOF_TYPE::HN;
813
814 ProgramMetadata metadata{ .ivc = ivc };
815
816 EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit<Builder>(program, metadata),
817 "ACIR constraint proof_type does not match IVC queue type");
818}
#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
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.