Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
chonk.test.cpp
Go to the documentation of this file.
1#include <functional>
2#include <ranges>
3#include <sys/resource.h>
4
22#include "gtest/gtest.h"
23
24using namespace bb;
25
26namespace {
27
28constexpr size_t SMALL_LOG_2_NUM_GATES = 5;
29
33enum class KernelIOField : uint8_t {
34 PAIRING_INPUTS,
35 ACCUMULATOR_HASH,
36 KERNEL_RETURN_DATA,
37 APP_RETURN_DATA,
38 ECC_OP_HASH
39};
40
41} // namespace
42
43class ChonkTests : public ::testing::Test {
44 protected:
46
48 using FF = typename Flavor::FF;
55 using CircuitProducer = PrivateFunctionExecutionMockCircuitProducer;
57
58 public:
62 using AccumulateHook = std::function<void(Chonk&, size_t)>;
63
70 static void tamper_with_proof(HonkProof& proof, size_t public_inputs_offset)
71 {
72 // Tamper with the commitment in the proof
73 Commitment commitment = FrCodec::deserialize_from_fields<Commitment>(
74 std::span{ proof }.subspan(public_inputs_offset, FrCodec::template calc_num_fields<Commitment>()));
75 commitment = commitment + Commitment::one();
76 auto commitment_frs = FrCodec::serialize_to_fields<Commitment>(commitment);
77 for (size_t idx = 0; idx < 4; ++idx) {
78 proof[public_inputs_offset + idx] = commitment_frs[idx];
79 }
80 }
81
83 size_t num_app_circuits,
84 TestSettings settings = {},
85 const AccumulateHook& post_hook = nullptr,
86 bool check_circuit_sizes = false)
87 {
88 CircuitProducer circuit_producer(num_app_circuits);
89 return run_ivc_impl(circuit_producer, settings, post_hook, check_circuit_sizes);
90 };
91
93 std::vector<bool> leading_is_kernel_flags,
94 TestSettings settings = {},
95 const AccumulateHook& post_hook = nullptr,
96 bool check_circuit_sizes = false)
97 {
98 CircuitProducer circuit_producer(std::move(leading_is_kernel_flags), /*large_first_app=*/false);
99 return run_ivc_impl(circuit_producer, settings, post_hook, check_circuit_sizes);
100 };
101
103 size_t num_app_circuits, TestSettings settings = {}, bool check_circuit_sizes = false)
104 {
105 return run_ivc(num_app_circuits, settings, /*post_hook=*/nullptr, check_circuit_sizes);
106 };
107
108 static bool verify_chonk(const ChonkProof& proof, const std::shared_ptr<MegaZKFlavor::VKAndHash>& vk_and_hash)
109 {
110 ChonkVerifier verifier(vk_and_hash);
111 return verifier.verify(proof);
112 }
113
120 {
122
123 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
124 auto [proof, vk] = run_ivc(/*num_app_circuits=*/2, settings, [](Chonk& ivc, size_t idx) {
125 if (idx == 1) {
126 auto& app_entry = ivc.verification_queue[1];
127 ASSERT_FALSE(app_entry.is_kernel) << "Expected second queue entry to be an app";
128
130 size_t num_public_inputs = app_entry.honk_vk->num_public_inputs;
131 AppIOSerde app_io = AppIOSerde::from_proof(app_entry.proof, num_public_inputs);
132
133 // Set P0 to [x]₁ (the first SRS point after [1]) and P1 to [1]₁
134 app_io.pairing_inputs.P0() = srs::get_crs_factory<curve::BN254>()->get_crs(2)->get_monomial_points()[1];
135 app_io.pairing_inputs.P1() = -Commitment::one();
136
137 EXPECT_TRUE(app_io.pairing_inputs.check());
138
139 app_io.to_proof(app_entry.proof, num_public_inputs);
140 }
141 });
142 EXPECT_FALSE(verify_chonk(proof, vk));
143 }
144
150 static void test_kernel_io_tampering(KernelIOField field_to_tamper)
151 {
153
154 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
155 auto [proof, vk] = run_ivc(/*num_app_circuits=*/4, settings, [field_to_tamper](Chonk& ivc, size_t idx) {
156 if (idx == 3) {
157 auto& kernel_entry = ivc.verification_queue[0];
158 ASSERT_TRUE(kernel_entry.is_kernel) << "Expected first queue entry to be a kernel";
159
160 using KernelIOSerde = bb::stdlib::recursion::honk::KernelIOSerde;
161 size_t num_public_inputs = kernel_entry.honk_vk->num_public_inputs;
162 KernelIOSerde kernel_io = KernelIOSerde::from_proof(kernel_entry.proof, num_public_inputs);
163
164 // Tamper with the specified field
165 switch (field_to_tamper) {
166 case KernelIOField::PAIRING_INPUTS: {
167 // Set P0 to [x]₁ (the first SRS point after [1]) and P1 to [1]₁
168 kernel_io.pairing_inputs.P0() =
169 srs::get_crs_factory<curve::BN254>()->get_crs(2)->get_monomial_points()[1];
170 kernel_io.pairing_inputs.P1() = -Commitment::one();
171
172 EXPECT_TRUE(kernel_io.pairing_inputs.check());
173 break;
174 }
175 case KernelIOField::ACCUMULATOR_HASH:
176 kernel_io.output_hn_accum_hash += FF(1);
177 break;
178 case KernelIOField::KERNEL_RETURN_DATA:
179 kernel_io.kernel_return_data = kernel_io.kernel_return_data + Commitment::one();
180 break;
181 case KernelIOField::APP_RETURN_DATA:
182 kernel_io.app_return_data[0] = kernel_io.app_return_data[0] + Commitment::one();
183 break;
184 case KernelIOField::ECC_OP_HASH:
185 kernel_io.ecc_op_hash += FF(1);
186 break;
187 }
188
189 kernel_io.to_proof(kernel_entry.proof, num_public_inputs);
190 }
191 });
192 EXPECT_FALSE(verify_chonk(proof, vk));
193 }
194
206 {
207 using HidingKernelIOSerde = bb::stdlib::recursion::honk::HidingKernelIOSerde;
208 using KernelIOSerde = bb::stdlib::recursion::honk::KernelIOSerde;
209
210 const size_t NUM_APP_CIRCUITS = 2;
211 const size_t NUM_TOTAL_CIRCUITS =
212 NUM_APP_CIRCUITS + static_cast<size_t>(ceil(static_cast<double>(NUM_APP_CIRCUITS) / MAX_APPS_PER_KERNEL)) +
213 /*num_trailing_kernels*/ 3;
214 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
215
216 // Extract tail kernel IO before the hiding kernel consumes the verification queue.
217 KernelIOSerde tail_io;
218 auto [proof, vk_and_hash] = run_ivc(
219 /*num_app_circuits=*/NUM_APP_CIRCUITS, settings, [&tail_io, &NUM_TOTAL_CIRCUITS](Chonk& ivc, size_t idx) {
220 // With 2 apps the layout is [app, kernel, app, kernel, reset, tail, hiding].
221 if (idx == NUM_TOTAL_CIRCUITS - 2) {
222 for (auto& it : std::ranges::reverse_view(ivc.verification_queue)) {
223 if (it.is_kernel) {
224 size_t num_public_inputs = it.honk_vk->num_public_inputs;
225 ASSERT_EQ(num_public_inputs, KernelIOSerde::PUBLIC_INPUTS_SIZE)
226 << "Tail kernel should use KernelIO format";
227 ASSERT_GT(it.proof.size(), num_public_inputs) << "Tail kernel proof too small";
228 tail_io = KernelIOSerde::from_proof(it.proof, num_public_inputs);
229 break;
230 }
231 }
232 }
233 });
234
235 size_t hiding_kernel_pub_inputs = vk_and_hash->vk->num_public_inputs;
236 ASSERT_EQ(hiding_kernel_pub_inputs, HidingKernelIOSerde::PUBLIC_INPUTS_SIZE)
237 << "HidingKernel should use HidingKernelIO format";
238 HidingKernelIOSerde hiding_io =
239 HidingKernelIOSerde::from_proof(proof.hiding_oink_proof, hiding_kernel_pub_inputs);
240
241 EXPECT_EQ(tail_io.kernel_return_data, hiding_io.kernel_return_data)
242 << "kernel_return_data mismatch: Tail has " << tail_io.kernel_return_data << " but HidingKernel has "
243 << hiding_io.kernel_return_data;
244 }
245
246 private:
248 CircuitProducer& circuit_producer,
249 TestSettings settings,
250 const AccumulateHook& post_hook,
251 bool check_circuit_sizes)
252 {
253 const size_t num_circuits = circuit_producer.total_num_circuits;
254 Chonk ivc{ num_circuits };
255
256 for (size_t idx = 0; idx < num_circuits; ++idx) {
257 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings, check_circuit_sizes);
258 if (post_hook) {
259 post_hook(ivc, idx);
260 }
261 }
262 return { ivc.prove(), ivc.get_hiding_kernel_vk_and_hash() };
263 }
264};
265
273TEST_F(ChonkTests, TestCircuitSizes)
274{
275 const size_t NUM_APP_CIRCUITS = 2;
276
277 // Check circuit sizes when no settings are passed
278 {
279 auto [proof, vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, {}, true);
280 EXPECT_TRUE(verify_chonk(proof, vk));
281 }
282
283 // Check circuit sizes when no settings are passed
284 {
285 auto [proof, vk] =
286 accumulate_and_prove_ivc(NUM_APP_CIRCUITS, { .log2_num_gates = SMALL_LOG_2_NUM_GATES }, true);
287 EXPECT_TRUE(verify_chonk(proof, vk));
288 }
289};
290
298{
299 const size_t NUM_APP_CIRCUITS = 2;
300 auto [proof, vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS);
301
302 EXPECT_TRUE(verify_chonk(proof, vk));
303};
304
312TEST_F(ChonkTests, BadProofFailure)
313{
314 BB_DISABLE_ASSERTS(); // Disable assert in HN prover
315
316 const size_t NUM_APP_CIRCUITS = 2;
317 // Confirm that the IVC verifies if nothing is tampered with
318 {
319
320 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
321 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
322 Chonk ivc{ NUM_CIRCUITS };
323 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
324
325 // Construct and accumulate a set of mocked private function execution circuits
326 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
327 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
328 }
329 auto proof = ivc.prove();
330 EXPECT_TRUE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
331 }
332
333 // The IVC throws an exception if the FIRST fold proof is tampered with
334 {
335 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
336 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
337 Chonk ivc{ NUM_CIRCUITS };
338
339 size_t num_public_inputs = 0;
340
341 // Construct and accumulate a set of mocked private function execution circuits
342 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
343 auto [circuit, vk] =
344 circuit_producer.create_next_circuit_and_vk(ivc, { .log2_num_gates = SMALL_LOG_2_NUM_GATES });
345 ivc.accumulate(circuit, vk);
346
347 if (idx == 1) {
348 num_public_inputs = circuit.num_public_inputs();
349 }
350
351 if (idx == 2) {
352 tamper_with_proof(ivc.verification_queue[0].proof,
353 num_public_inputs); // tamper with first proof
354 }
355 }
356 auto proof = ivc.prove();
357 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
358 }
359
360 // The IVC fails if the SECOND fold proof is tampered with
361 {
362 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
363 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
364 Chonk ivc{ NUM_CIRCUITS };
365
366 // Construct and accumulate a set of mocked private function execution circuits
367 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
368 auto [circuit, vk] =
369 circuit_producer.create_next_circuit_and_vk(ivc, { .log2_num_gates = SMALL_LOG_2_NUM_GATES });
370 ivc.accumulate(circuit, vk);
371
372 if (idx == 1) {
373 tamper_with_proof(ivc.verification_queue[1].proof,
374 circuit.num_public_inputs()); // tamper with second proof
375 }
376 }
377 auto proof = ivc.prove();
378 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
379 }
380};
381
386TEST_F(ChonkTests, VKIndependenceFromNumberOfCircuits)
387{
388 const TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
389
390 auto [unused_1, vk_and_hash_1] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
391 auto [unused_2, vk_and_hash_2] = accumulate_and_prove_ivc(/*num_app_circuits=*/3, settings);
392
393 // Check the equality of the hiding kernel VKeys
394 EXPECT_EQ(*vk_and_hash_1->vk.get(), *vk_and_hash_2->vk.get());
395};
396
401TEST_F(ChonkTests, VKIndependenceFromCircuitSize)
402{
403 // Run IVC for two sets of circuits
404 const size_t NUM_APP_CIRCUITS = 1;
405 const size_t log2_num_gates_small = 5;
406 const size_t log2_num_gates_big = 18;
407
408 const TestSettings settings_1{ .log2_num_gates = log2_num_gates_small };
409 const TestSettings settings_2{ .log2_num_gates = log2_num_gates_big };
410
411 auto [unused_1, vk_and_hash_1] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, settings_1);
412 auto [unused_2, vk_and_hash_2] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, settings_2);
413
414 // Check the equality of the hiding kernel VKeys
415 EXPECT_EQ(*vk_and_hash_1->vk.get(), *vk_and_hash_2->vk.get());
416};
417
422#ifdef NDEBUG
423HEAVY_TEST(ChonkKernelCapacity, MaxCapacityPassing)
424{
426
427 auto [proof, vk] = ChonkTests::accumulate_and_prove_ivc(CHONK_MAX_NUM_APPS);
428
429 bool verified = ChonkTests::verify_chonk(proof, vk);
430 EXPECT_TRUE(verified);
431};
432#endif
433
438TEST_F(ChonkTests, MsgpackProofFromFileOrBuffer)
439{
440 // Generate an arbitrary valid CICV proof
441 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
442 auto [proof, vk] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
443
444 { // Serialize/deserialize the proof to/from a file, check that it verifies
445 const std::string filename = "proof.msgpack";
446 proof.to_file_msgpack(filename);
447 auto proof_deserialized = ChonkProof::from_file_msgpack(filename);
448
449 EXPECT_TRUE(verify_chonk(proof_deserialized, vk));
450 }
451
452 { // Serialize/deserialize proof to/from a heap buffer, check that it verifies
453 uint8_t* buffer = proof.to_msgpack_heap_buffer();
454 auto uint8_buffer = from_buffer<std::vector<uint8_t>>(buffer);
455 uint8_t const* uint8_ptr = uint8_buffer.data();
456 auto proof_deserialized = ChonkProof::from_msgpack_buffer(uint8_ptr);
457
458 EXPECT_TRUE(verify_chonk(proof_deserialized, vk));
459 }
460
461 { // Check that attempting to deserialize a proof from a buffer with random bytes fails gracefully
462 msgpack::sbuffer buffer = proof.to_msgpack_buffer();
463 auto proof_deserialized = ChonkProof::from_msgpack_buffer(buffer);
464 EXPECT_TRUE(verify_chonk(proof_deserialized, vk));
465
466 std::vector<uint8_t> random_bytes(buffer.size());
467 std::generate(random_bytes.begin(), random_bytes.end(), []() { return static_cast<uint8_t>(rand() % 256); });
468 std::copy(random_bytes.begin(), random_bytes.end(), buffer.data());
469
470 // Expect deserialization to fail (either msgpack parse error, type mismatch, trailing data,
471 // or non-canonical field encoding)
472 EXPECT_ANY_THROW(ChonkProof::from_msgpack_buffer(buffer));
473 }
474};
475
476class KernelIOTamperingTests : public ChonkTests, public testing::WithParamInterface<KernelIOField> {};
477
483TEST_F(ChonkTests, AppPairingInputsTamperingFailure)
484{
486}
487
488TEST_P(KernelIOTamperingTests, CausesVerificationFailure)
489{
490 test_kernel_io_tampering(GetParam());
491}
492
495 testing::Values(KernelIOField::PAIRING_INPUTS,
496 KernelIOField::ACCUMULATOR_HASH,
497 KernelIOField::KERNEL_RETURN_DATA,
498 KernelIOField::APP_RETURN_DATA,
499 KernelIOField::ECC_OP_HASH),
500 [](const testing::TestParamInfo<KernelIOField>& info) {
501 switch (info.param) {
502 case KernelIOField::PAIRING_INPUTS:
503 return "PairingInputs";
504 case KernelIOField::ACCUMULATOR_HASH:
505 return "AccumulatorHash";
506 case KernelIOField::KERNEL_RETURN_DATA:
507 return "KernelReturnData";
508 case KernelIOField::APP_RETURN_DATA:
509 return "AppReturnData";
510 case KernelIOField::ECC_OP_HASH:
511 return "EccOpHash";
512 }
513 return "Unknown";
514 });
515
521TEST_F(ChonkTests, KernelReturnDataPropagationConsistency)
522{
524}
525
531TEST_F(ChonkTests, SmallAppProvingMemory)
532{
533 auto get_peak_rss_mib = []() -> size_t {
534 struct rusage usage{};
535 getrusage(RUSAGE_SELF, &usage);
536 return static_cast<size_t>(usage.ru_maxrss) / 1024; // Linux: ru_maxrss is in KB
537 };
538
539 constexpr size_t LOG2_NUM_GATES = 10;
540 const size_t NUM_APP_CIRCUITS = 1;
541
542 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
543 const size_t num_circuits = circuit_producer.total_num_circuits;
544 Chonk ivc{ num_circuits };
545 TestSettings settings{ .log2_num_gates = LOG2_NUM_GATES };
546
547 for (size_t j = 0; j < num_circuits; ++j) {
548 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
549 }
550
551 info("Peak RSS before prove: ", get_peak_rss_mib(), " MiB");
552
553 ChonkProof proof = ivc.prove();
554
555 info("Peak RSS after prove: ", get_peak_rss_mib(), " MiB");
556
557 // Verify the proof is valid
558 EXPECT_TRUE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
559}
560
561TEST_F(ChonkTests, ProofCompressionRoundtrip)
562{
563 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
564 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
565
566 auto original_flat = proof.to_field_elements();
567 info("Original proof size: ", original_flat.size(), " Fr elements (", original_flat.size() * 32, " bytes)");
568
569 auto compressed = ProofCompressor::compress_chonk_proof(proof);
570 double ratio = static_cast<double>(original_flat.size() * 32) / static_cast<double>(compressed.size());
571 info("Compressed proof size: ", compressed.size(), " bytes");
572 info("Compression ratio: ", ratio, "x");
573
574 // Compression should achieve at least 1.5x (commitments 4 Fr → 32 bytes, scalars 1:1)
575 EXPECT_GE(ratio, 1.5) << "Compression ratio " << ratio << "x is below the expected minimum of 1.5x";
576
577 size_t mega_num_pub_inputs =
578 proof.hiding_oink_proof.size() - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
579 ChonkProof decompressed = ProofCompressor::decompress_chonk_proof(compressed, mega_num_pub_inputs);
580
581 // Verify element-by-element roundtrip
582 auto decompressed_flat = decompressed.to_field_elements();
583 ASSERT_EQ(decompressed_flat.size(), original_flat.size());
584 for (size_t i = 0; i < original_flat.size(); i++) {
585 ASSERT_EQ(decompressed_flat[i], original_flat[i]) << "Mismatch at element " << i;
586 }
587
588 // Verify the decompressed proof
589 EXPECT_TRUE(verify_chonk(decompressed, vk_and_hash));
590}
591
595static std::vector<uint8_t> compress_and_corrupt(const ChonkProof& proof, size_t element_idx, const uint256_t& value)
596{
597 auto compressed = ProofCompressor::compress_chonk_proof(proof);
598 size_t byte_offset = element_idx * 32;
599 EXPECT_LT(byte_offset + 32, compressed.size());
600 // Overwrite the element with the given value (big-endian)
601 std::vector<uint8_t> buf;
602 write(buf, value);
603 std::copy(buf.begin(), buf.end(), compressed.begin() + static_cast<ptrdiff_t>(byte_offset));
604 return compressed;
605}
606
607// Rejects a BN scalar value >= Fr::modulus
608TEST_F(ChonkTests, DecompressionRejectsNonCanonicalBN254Scalar)
609{
610 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
611 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
612 size_t mega_num_pub_inputs =
613 proof.hiding_oink_proof.size() - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
614 ASSERT_GT(mega_num_pub_inputs, 0); // Need at least one public input (BN254 scalar)
615
616 // Element 0 is the first public input (a BN254 scalar). Set it to Fr::modulus (non-canonical).
618
619 auto corrupted = compress_and_corrupt(proof, 0, Fr::modulus);
620 EXPECT_THROW_OR_ABORT(ProofCompressor::decompress_chonk_proof(corrupted, mega_num_pub_inputs), "");
621}
622
623// Rejects a BN commitment x-coordinate >= Fq::modulus
624TEST_F(ChonkTests, DecompressionRejectsNonCanonicalBN254CommitmentX)
625{
627
628 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
629 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
630 size_t mega_num_pub_inputs =
631 proof.hiding_oink_proof.size() - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
632
633 // First BN254 commitment is at element index mega_num_pub_inputs.
634 // Set x-coordinate to Fq::modulus + 1 (non-canonical, with no sign bit).
635 auto corrupted = compress_and_corrupt(proof, mega_num_pub_inputs, Fq::modulus + 1);
636 EXPECT_THROW_OR_ABORT(ProofCompressor::decompress_chonk_proof(corrupted, mega_num_pub_inputs), "");
637}
638
639// Rejects a canonical x-coordinate that is not on the BN254 curve
640TEST_F(ChonkTests, DecompressionRejectsInvalidBN254CurvePoint)
641{
643
644 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
645 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
646 size_t mega_num_pub_inputs =
647 proof.hiding_oink_proof.size() - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
648
649 // x=4 is not on BN254
650 Fq x_not_on_curve(4);
651 Fq rhs = x_not_on_curve * x_not_on_curve * x_not_on_curve + Fq(3);
652 auto [is_sq, _] = rhs.sqrt();
653 ASSERT_FALSE(is_sq);
654
655 auto corrupted = compress_and_corrupt(proof, mega_num_pub_inputs, uint256_t(x_not_on_curve));
656 EXPECT_THROW_OR_ABORT(ProofCompressor::decompress_chonk_proof(corrupted, mega_num_pub_inputs), "");
657}
658
659// Rejects a compressed proof that is too short (read_u256 bounds check)
660TEST_F(ChonkTests, DecompressionRejectsTruncatedProof)
661{
662 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
663 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
664 size_t mega_num_pub_inputs =
665 proof.hiding_oink_proof.size() - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
666
667 auto compressed = ProofCompressor::compress_chonk_proof(proof);
668 // Truncate by removing the last 32-byte element
669 compressed.resize(compressed.size() - 32);
670 EXPECT_THROW_OR_ABORT(ProofCompressor::decompress_chonk_proof(compressed, mega_num_pub_inputs), "");
671}
#define EXPECT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:192
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
TEST_F(ChonkTests, TestCircuitSizes)
Test sizes of the circuits generated by MockCircuitProducer.
INSTANTIATE_TEST_SUITE_P(All, KernelIOTamperingTests, testing::Values(KernelIOField::PAIRING_INPUTS, KernelIOField::ACCUMULATOR_HASH, KernelIOField::KERNEL_RETURN_DATA, KernelIOField::APP_RETURN_DATA, KernelIOField::ECC_OP_HASH), [](const testing::TestParamInfo< KernelIOField > &info) { switch(info.param) { case KernelIOField::PAIRING_INPUTS:return "PairingInputs";case KernelIOField::ACCUMULATOR_HASH:return "AccumulatorHash";case KernelIOField::KERNEL_RETURN_DATA:return "KernelReturnData";case KernelIOField::APP_RETURN_DATA:return "AppReturnData";case KernelIOField::ECC_OP_HASH:return "EccOpHash";} return "Unknown";})
TEST_P(KernelIOTamperingTests, CausesVerificationFailure)
PrivateFunctionExecutionMockCircuitProducer CircuitProducer
static std::pair< ChonkProof, std::shared_ptr< MegaZKFlavor::VKAndHash > > run_ivc(std::vector< bool > leading_is_kernel_flags, TestSettings settings={}, const AccumulateHook &post_hook=nullptr, bool check_circuit_sizes=false)
static bool verify_chonk(const ChonkProof &proof, const std::shared_ptr< MegaZKFlavor::VKAndHash > &vk_and_hash)
static void tamper_with_proof(HonkProof &proof, size_t public_inputs_offset)
Tamper with a proof.
Flavor::Commitment Commitment
static void test_kernel_io_tampering(KernelIOField field_to_tamper)
Helper function to test tampering with KernelIO fields.
static void test_kernel_return_data_propagation()
Helper function to test HidingKernelIO field propagation consistency.
static void SetUpTestSuite()
typename Flavor::FF FF
std::function< void(Chonk &, size_t)> AccumulateHook
Hook fired after each accumulate() inside run_ivc.
static std::pair< ChonkProof, std::shared_ptr< MegaZKFlavor::VKAndHash > > run_ivc_impl(CircuitProducer &circuit_producer, TestSettings settings, const AccumulateHook &post_hook, bool check_circuit_sizes)
static std::pair< ChonkProof, std::shared_ptr< MegaZKFlavor::VKAndHash > > accumulate_and_prove_ivc(size_t num_app_circuits, TestSettings settings={}, bool check_circuit_sizes=false)
static void test_app_io_tampering()
Helper function to test tampering with AppIO pairing inputs.
static std::pair< ChonkProof, std::shared_ptr< MegaZKFlavor::VKAndHash > > run_ivc(size_t num_app_circuits, TestSettings settings={}, const AccumulateHook &post_hook=nullptr, bool check_circuit_sizes=false)
The IVC scheme used by the aztec client for private function execution.
Definition chonk.hpp:39
HypernovaDeciderProver DeciderProver
Definition chonk.hpp:78
ChonkProof prove()
Construct Chonk proof using the batched MegaZK + Translator protocol.
Definition chonk.cpp:626
ProverInstance_< Flavor > ProverInstance
Definition chonk.hpp:50
MegaFlavor Flavor
Definition chonk.hpp:43
VerifierInstance_< Flavor > VerifierInstance
Definition chonk.hpp:52
void accumulate(ClientCircuit &circuit, const std::shared_ptr< MegaVerificationKey > &precomputed_vk) override
Perform prover work for accumulation (e.g. HN folding, merge proving)
Definition chonk.cpp:573
MegaCircuitBuilder ClientCircuit
Definition chonk.hpp:53
VerificationQueue verification_queue
Definition chonk.hpp:175
Verifier for Chonk IVC proofs (both native and recursive).
Output verify(const Proof &proof)
Verify a Chonk proof.
HyperNova decider prover. Produces final opening proof for the accumulated claim.
Curve::ScalarField FF
Curve::AffineElement Commitment
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
The verification key stores commitments to the precomputed (non-witness) polynomials used by the veri...
Base Native verification key class.
Definition flavor.hpp:135
static std::vector< uint8_t > compress_chonk_proof(const ChonkProof &proof)
static ChonkProof decompress_chonk_proof(const std::vector< uint8_t > &compressed, size_t mega_num_public_inputs)
Contains all the information required by a Honk prover to create a proof, constructed from a finalize...
The VerifierInstance encapsulates all the necessary information for a Honk Verifier to verify a proof...
bb::fq BaseField
Definition bn254.hpp:19
bb::fr ScalarField
Definition bn254.hpp:18
Native representation and serde for AppIO public inputs.
Native representation and serde for HidingKernelIO public inputs.
#define info(...)
Definition log.hpp:93
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:60
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
KernelIOSerde_< MAX_APPS_PER_KERNEL > KernelIOSerde
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::vector< fr > HonkProof
Definition proof.hpp:15
::testing::Types< BN254Settings, GrumpkinSettings > TestSettings
void write(B &buf, field2< base_field, Params > const &value)
ChonkVerifier< false > ChonkNativeVerifier
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static ChonkProof_ from_msgpack_buffer(uint8_t const *&buffer)
std::vector< FF > to_field_elements() const
Serialize proof to field elements (native mode)
static ChonkProof_ from_file_msgpack(const std::string &filename)
Computes Oink proof length from flavor traits.
static constexpr uint256_t modulus
#define HEAVY_TEST(x, y)
Definition test.hpp:9