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
47 using FF = Chonk::FF;
51 using CircuitProducer = PrivateFunctionExecutionMockCircuitProducer;
53
54 public:
58 using AccumulateHook = std::function<void(Chonk&, size_t)>;
59
66 static void tamper_with_proof(HonkProof& proof, size_t public_inputs_offset)
67 {
68 // Tamper with the commitment in the proof
69 Commitment commitment = FrCodec::deserialize_from_fields<Commitment>(
70 std::span{ proof }.subspan(public_inputs_offset, FrCodec::template calc_num_fields<Commitment>()));
71 commitment = commitment + Commitment::one();
72 auto commitment_frs = FrCodec::serialize_to_fields<Commitment>(commitment);
73 for (size_t idx = 0; idx < 4; ++idx) {
74 proof[public_inputs_offset + idx] = commitment_frs[idx];
75 }
76 }
77
79 size_t num_app_circuits,
80 TestSettings settings = {},
81 const AccumulateHook& post_hook = nullptr,
82 bool check_circuit_sizes = false)
83 {
84 CircuitProducer circuit_producer(num_app_circuits);
85 return run_ivc_impl(circuit_producer, settings, post_hook, check_circuit_sizes);
86 };
87
89 std::vector<bool> leading_is_kernel_flags,
90 TestSettings settings = {},
91 const AccumulateHook& post_hook = nullptr,
92 bool check_circuit_sizes = false)
93 {
94 CircuitProducer circuit_producer(std::move(leading_is_kernel_flags), /*large_first_app=*/false);
95 return run_ivc_impl(circuit_producer, settings, post_hook, check_circuit_sizes);
96 };
97
99 size_t num_app_circuits, TestSettings settings = {}, bool check_circuit_sizes = false)
100 {
101 return run_ivc(num_app_circuits, settings, /*post_hook=*/nullptr, check_circuit_sizes);
102 };
103
104 static bool verify_chonk(const ChonkProof& proof, const std::shared_ptr<MegaZKFlavor::VKAndHash>& vk_and_hash)
105 {
106 ChonkVerifier verifier(vk_and_hash);
107 return verifier.verify(proof);
108 }
109
116 {
118
119 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
120 auto [proof, vk] = run_ivc(/*num_app_circuits=*/2, settings, [](Chonk& ivc, size_t idx) {
121 if (idx == 1) {
122 auto& app_entry = ivc.verification_queue[1];
123 ASSERT_FALSE(app_entry.is_kernel()) << "Expected second queue entry to be an app";
124
126 size_t num_public_inputs = app_entry.num_public_inputs();
127 AppIOSerde app_io = AppIOSerde::from_proof(app_entry.proof, num_public_inputs);
128
129 // Set P0 to [x]₁ (the first SRS point after [1]) and P1 to [1]₁
130 app_io.pairing_inputs.P0() = srs::get_crs_factory<curve::BN254>()->get_crs(2)->get_monomial_points()[1];
131 app_io.pairing_inputs.P1() = -Commitment::one();
132
133 EXPECT_TRUE(app_io.pairing_inputs.check());
134
135 app_io.to_proof(app_entry.proof, num_public_inputs);
136 }
137 });
138 EXPECT_FALSE(verify_chonk(proof, vk));
139 }
140
146 static void test_kernel_io_tampering(KernelIOField field_to_tamper)
147 {
149
150 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
151 auto [proof, vk] =
152 run_ivc(/*num_app_circuits=*/MAX_APPS_PER_KERNEL + 1, settings, [field_to_tamper](Chonk& ivc, size_t idx) {
153 if (idx == MAX_APPS_PER_KERNEL) {
154 auto& kernel_entry = ivc.verification_queue[0];
155 ASSERT_TRUE(kernel_entry.is_kernel()) << "Expected first queue entry to be a kernel";
156
157 using KernelIOSerde = bb::stdlib::recursion::honk::KernelIOSerde;
158 size_t num_public_inputs = kernel_entry.num_public_inputs();
159 KernelIOSerde kernel_io = KernelIOSerde::from_proof(kernel_entry.proof, num_public_inputs);
160
161 // Tamper with the specified field
162 switch (field_to_tamper) {
163 case KernelIOField::PAIRING_INPUTS: {
164 // Set P0 to [x]₁ (the first SRS point after [1]) and P1 to [1]₁
165 kernel_io.pairing_inputs.P0() =
166 srs::get_crs_factory<curve::BN254>()->get_crs(2)->get_monomial_points()[1];
167 kernel_io.pairing_inputs.P1() = -Commitment::one();
168
169 EXPECT_TRUE(kernel_io.pairing_inputs.check());
170 break;
171 }
172 case KernelIOField::ACCUMULATOR_HASH:
173 kernel_io.output_hn_accum_hash += FF(1);
174 break;
175 case KernelIOField::KERNEL_RETURN_DATA:
176 kernel_io.kernel_return_data = kernel_io.kernel_return_data + Commitment::one();
177 break;
178 case KernelIOField::APP_RETURN_DATA:
179 kernel_io.app_return_data[0] = kernel_io.app_return_data[0] + Commitment::one();
180 break;
181 case KernelIOField::ECC_OP_HASH:
182 kernel_io.ecc_op_hash += FF(1);
183 break;
184 }
185
186 kernel_io.to_proof(kernel_entry.proof, num_public_inputs);
187 }
188 });
189 EXPECT_FALSE(verify_chonk(proof, vk));
190 }
191
192 private:
194 CircuitProducer& circuit_producer,
195 TestSettings settings,
196 const AccumulateHook& post_hook,
197 bool check_circuit_sizes)
198 {
199 const size_t num_circuits = circuit_producer.total_num_circuits;
200 Chonk ivc{ circuit_producer.circuit_kinds() };
201
202 for (size_t idx = 0; idx < num_circuits; ++idx) {
203 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings, check_circuit_sizes);
204 if (post_hook) {
205 post_hook(ivc, idx);
206 }
207 }
208 return { ivc.prove(), ivc.get_hiding_kernel_vk_and_hash() };
209 }
210};
211
219TEST_F(ChonkTests, TestCircuitSizes)
220{
221 const size_t NUM_APP_CIRCUITS = 2;
222
223 // Check circuit sizes when no settings are passed
224 {
225 auto [proof, vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, {}, true);
226 EXPECT_TRUE(verify_chonk(proof, vk));
227 }
228
229 // Check circuit sizes when no settings are passed
230 {
231 auto [proof, vk] =
232 accumulate_and_prove_ivc(NUM_APP_CIRCUITS, { .log2_num_gates = SMALL_LOG_2_NUM_GATES }, true);
233 EXPECT_TRUE(verify_chonk(proof, vk));
234 }
235};
236
244{
245 const size_t NUM_APP_CIRCUITS = 2;
246 auto [proof, vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS);
247
248 EXPECT_TRUE(verify_chonk(proof, vk));
249};
250
258TEST_F(ChonkTests, BadProofFailure)
259{
260 BB_DISABLE_ASSERTS(); // Disable assert in HN prover
261
262 const size_t NUM_APP_CIRCUITS = 2;
263 // Confirm that the IVC verifies if nothing is tampered with
264 {
265
266 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
267 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
268 Chonk ivc{ circuit_producer.circuit_kinds() };
269 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
270
271 // Construct and accumulate a set of mocked private function execution circuits
272 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
273 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
274 }
275 auto proof = ivc.prove();
276 EXPECT_TRUE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
277 }
278
279 // The IVC throws an exception if the FIRST fold proof is tampered with
280 {
281 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
282 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
283 Chonk ivc{ circuit_producer.circuit_kinds() };
284
285 // Construct and accumulate a set of mocked private function execution circuits
286 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
287 circuit_producer.construct_and_accumulate_next_circuit(ivc, { .log2_num_gates = SMALL_LOG_2_NUM_GATES });
288
289 if (idx == 2) {
290 tamper_with_proof(ivc.verification_queue[0].proof,
291 ivc.verification_queue[0].num_public_inputs()); // tamper with first proof
292 }
293 }
294 auto proof = ivc.prove();
295 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
296 }
297
298 // The IVC fails if the SECOND fold proof is tampered with
299 {
300 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
301 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
302 Chonk ivc{ circuit_producer.circuit_kinds() };
303
304 // Construct and accumulate a set of mocked private function execution circuits
305 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
306 circuit_producer.construct_and_accumulate_next_circuit(ivc, { .log2_num_gates = SMALL_LOG_2_NUM_GATES });
307
308 if (idx == 1) {
309 tamper_with_proof(ivc.verification_queue[1].proof,
310 ivc.verification_queue[1].num_public_inputs()); // tamper with second proof
311 }
312 }
313 auto proof = ivc.prove();
314 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
315 }
316};
317
322TEST_F(ChonkTests, VKIndependenceFromNumberOfCircuits)
323{
324 const TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
325
326 auto [unused_1, vk_and_hash_1] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
327 auto [unused_2, vk_and_hash_2] = accumulate_and_prove_ivc(/*num_app_circuits=*/3, settings);
328
329 // Check the equality of the hiding kernel VKeys
330 EXPECT_EQ(*vk_and_hash_1->vk.get(), *vk_and_hash_2->vk.get());
331};
332
337TEST_F(ChonkTests, VKIndependenceFromCircuitSize)
338{
339 // Run IVC for two sets of circuits
340 const size_t NUM_APP_CIRCUITS = 1;
341 const size_t log2_num_gates_small = 5;
342 const size_t log2_num_gates_big = 18;
343
344 const TestSettings settings_1{ .log2_num_gates = log2_num_gates_small };
345 const TestSettings settings_2{ .log2_num_gates = log2_num_gates_big };
346
347 auto [unused_1, vk_and_hash_1] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, settings_1);
348 auto [unused_2, vk_and_hash_2] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, settings_2);
349
350 // Check the equality of the hiding kernel VKeys
351 EXPECT_EQ(*vk_and_hash_1->vk.get(), *vk_and_hash_2->vk.get());
352};
353
358#ifdef NDEBUG
359HEAVY_TEST(ChonkKernelCapacity, MaxCapacityPassing)
360{
362
363 auto [proof, vk] = ChonkTests::accumulate_and_prove_ivc(CHONK_MAX_NUM_APPS);
364
365 bool verified = ChonkTests::verify_chonk(proof, vk);
366 EXPECT_TRUE(verified);
367};
368#endif
369
374TEST_F(ChonkTests, MsgpackProofFromFileOrBuffer)
375{
376 // Generate an arbitrary valid CICV proof
377 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
378 auto [proof, vk] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
379
380 { // Serialize/deserialize the proof to/from a file, check that it verifies
381 const std::string filename = "proof.msgpack";
382 proof.to_file_msgpack(filename);
383 auto proof_deserialized = ChonkProof::from_file_msgpack(filename);
384
385 EXPECT_TRUE(verify_chonk(proof_deserialized, vk));
386 }
387
388 { // Serialize/deserialize proof to/from a heap buffer, check that it verifies
389 uint8_t* buffer = proof.to_msgpack_heap_buffer();
390 auto uint8_buffer = from_buffer<std::vector<uint8_t>>(buffer);
391 uint8_t const* uint8_ptr = uint8_buffer.data();
392 auto proof_deserialized = ChonkProof::from_msgpack_buffer(uint8_ptr);
393
394 EXPECT_TRUE(verify_chonk(proof_deserialized, vk));
395 }
396
397 { // Check that attempting to deserialize a proof from a buffer with random bytes fails gracefully
398 msgpack::sbuffer buffer = proof.to_msgpack_buffer();
399 auto proof_deserialized = ChonkProof::from_msgpack_buffer(buffer);
400 EXPECT_TRUE(verify_chonk(proof_deserialized, vk));
401
402 std::vector<uint8_t> random_bytes(buffer.size());
403 std::generate(random_bytes.begin(), random_bytes.end(), []() { return static_cast<uint8_t>(rand() % 256); });
404 std::copy(random_bytes.begin(), random_bytes.end(), buffer.data());
405
406 // Expect deserialization to fail (either msgpack parse error, type mismatch, trailing data,
407 // or non-canonical field encoding)
408 EXPECT_ANY_THROW(ChonkProof::from_msgpack_buffer(buffer));
409 }
410};
411
412class KernelIOTamperingTests : public ChonkTests, public testing::WithParamInterface<KernelIOField> {};
413
419TEST_F(ChonkTests, AppPairingInputsTamperingFailure)
420{
422}
423
424TEST_P(KernelIOTamperingTests, CausesVerificationFailure)
425{
426 test_kernel_io_tampering(GetParam());
427}
428
431 testing::Values(KernelIOField::PAIRING_INPUTS,
432 KernelIOField::ACCUMULATOR_HASH,
433 KernelIOField::KERNEL_RETURN_DATA,
434 KernelIOField::APP_RETURN_DATA,
435 KernelIOField::ECC_OP_HASH),
436 [](const testing::TestParamInfo<KernelIOField>& info) {
437 switch (info.param) {
438 case KernelIOField::PAIRING_INPUTS:
439 return "PairingInputs";
440 case KernelIOField::ACCUMULATOR_HASH:
441 return "AccumulatorHash";
442 case KernelIOField::KERNEL_RETURN_DATA:
443 return "KernelReturnData";
444 case KernelIOField::APP_RETURN_DATA:
445 return "AppReturnData";
446 case KernelIOField::ECC_OP_HASH:
447 return "EccOpHash";
448 }
449 return "Unknown";
450 });
451
462TEST_F(ChonkTests, AccumulatorBinding)
463{
465
466 TestSettings settings_one{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
467
468 // ── Step 1: Run a parallel VALID IVC to capture a valid accumulator ──────
469
470 // We need to have more than MAX_APPS_PER_KERNEL apps to reach a kernel
471 const size_t num_app_circuits = MAX_APPS_PER_KERNEL + 1;
472 CircuitProducer producer_one(num_app_circuits);
473 const size_t num_circuits = producer_one.total_num_circuits;
474 Chonk chonk_one{ producer_one.circuit_kinds() };
475
476 // Accumulate the first MAX_APPS_PER_KERNEL apps and the kernel
477 for (size_t idx = 0; idx < MAX_APPS_PER_KERNEL + 1; idx++) {
478 producer_one.construct_and_accumulate_next_circuit(chonk_one, settings_one);
479 }
480 auto valid_accumulator = chonk_one.prover_accumulator;
481
482 // ── Step 2: Run the IVC with an INVALID first app + accumulator substitution ─
483
484 MockDatabusProducer mock_databus;
485 Chonk invalid_chonk{ producer_one.circuit_kinds() };
486 CircuitProducer producer_two(num_app_circuits);
487 TestSettings settings_two{ .log2_num_gates = SMALL_LOG_2_NUM_GATES + 1 };
488
489 for (size_t circuit_idx = 0; circuit_idx < num_circuits; ++circuit_idx) {
490 producer_two.construct_and_accumulate_next_circuit(invalid_chonk, settings_two);
491
492 // *** ACCUMULATOR SUBSTITUTION ***
493 // After MAX_APPS_PER_KERNEL apps and a kernel have been accumulated, replace the prover accumulator with the
494 // one from the parallel (valid but distinct) IVC.
495 if (circuit_idx == MAX_APPS_PER_KERNEL) {
496 BB_ASSERT_NEQ(valid_accumulator.non_shifted_commitment,
497 invalid_chonk.prover_accumulator.non_shifted_commitment,
498 "Accumulators should be different.");
499 invalid_chonk.prover_accumulator = valid_accumulator;
500 }
501 }
502
503 // ── Step 3: prove and verify ─────────────────────────────────────────────
504 auto proof = invalid_chonk.prove();
505 auto vk_and_hash = invalid_chonk.get_hiding_kernel_vk_and_hash();
506 ChonkVerifier verifier(vk_and_hash);
507 auto result = verifier.verify(proof);
508
509 EXPECT_FALSE(result) << "Substituting the accumulator should cause verification to fail, but it passed";
510}
511
517TEST_F(ChonkTests, SmallAppProvingMemory)
518{
519 auto get_peak_rss_mib = []() -> size_t {
520 struct rusage usage{};
521 getrusage(RUSAGE_SELF, &usage);
522 return static_cast<size_t>(usage.ru_maxrss) / 1024; // Linux: ru_maxrss is in KB
523 };
524
525 constexpr size_t LOG2_NUM_GATES = 10;
526 const size_t NUM_APP_CIRCUITS = 1;
527
528 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
529 const size_t num_circuits = circuit_producer.total_num_circuits;
530 Chonk ivc{ circuit_producer.circuit_kinds() };
531 TestSettings settings{ .log2_num_gates = LOG2_NUM_GATES };
532
533 for (size_t j = 0; j < num_circuits; ++j) {
534 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
535 }
536
537 info("Peak RSS before prove: ", get_peak_rss_mib(), " MiB");
538
539 ChonkProof proof = ivc.prove();
540
541 info("Peak RSS after prove: ", get_peak_rss_mib(), " MiB");
542
543 // Verify the proof is valid
544 EXPECT_TRUE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
545}
546
547TEST_F(ChonkTests, ProofCompressionRoundtrip)
548{
549 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
550 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
551
552 auto original_flat = proof.to_field_elements();
553 info("Original proof size: ", original_flat.size(), " Fr elements (", original_flat.size() * 32, " bytes)");
554
555 auto compressed = ProofCompressor::compress_chonk_proof(proof);
556 double ratio = static_cast<double>(original_flat.size() * 32) / static_cast<double>(compressed.size());
557 info("Compressed proof size: ", compressed.size(), " bytes");
558 info("Compression ratio: ", ratio, "x");
559
560 // Compression should achieve at least 1.5x (commitments 4 Fr → 32 bytes, scalars 1:1)
561 EXPECT_GE(ratio, 1.5) << "Compression ratio " << ratio << "x is below the expected minimum of 1.5x";
562
563 size_t mega_num_pub_inputs =
564 proof.hiding_oink_proof.size() - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
565 ChonkProof decompressed = ProofCompressor::decompress_chonk_proof(compressed, mega_num_pub_inputs);
566
567 // Verify element-by-element roundtrip
568 auto decompressed_flat = decompressed.to_field_elements();
569 ASSERT_EQ(decompressed_flat.size(), original_flat.size());
570 for (size_t i = 0; i < original_flat.size(); i++) {
571 ASSERT_EQ(decompressed_flat[i], original_flat[i]) << "Mismatch at element " << i;
572 }
573
574 // Verify the decompressed proof
575 EXPECT_TRUE(verify_chonk(decompressed, vk_and_hash));
576}
577
581static std::vector<uint8_t> compress_and_corrupt(const ChonkProof& proof, size_t element_idx, const uint256_t& value)
582{
583 auto compressed = ProofCompressor::compress_chonk_proof(proof);
584 size_t byte_offset = element_idx * 32;
585 EXPECT_LT(byte_offset + 32, compressed.size());
586 // Overwrite the element with the given value (big-endian)
587 std::vector<uint8_t> buf;
588 write(buf, value);
589 std::copy(buf.begin(), buf.end(), compressed.begin() + static_cast<ptrdiff_t>(byte_offset));
590 return compressed;
591}
592
593// Rejects a BN scalar value >= Fr::modulus
594TEST_F(ChonkTests, DecompressionRejectsNonCanonicalBN254Scalar)
595{
596 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
597 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
598 size_t mega_num_pub_inputs =
599 proof.hiding_oink_proof.size() - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
600 ASSERT_GT(mega_num_pub_inputs, 0); // Need at least one public input (BN254 scalar)
601
602 // Element 0 is the first public input (a BN254 scalar). Set it to Fr::modulus (non-canonical).
604
605 auto corrupted = compress_and_corrupt(proof, 0, Fr::modulus);
606 EXPECT_THROW_OR_ABORT(ProofCompressor::decompress_chonk_proof(corrupted, mega_num_pub_inputs), "");
607}
608
609// Rejects a BN commitment x-coordinate >= Fq::modulus
610TEST_F(ChonkTests, DecompressionRejectsNonCanonicalBN254CommitmentX)
611{
613
614 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
615 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
616 size_t mega_num_pub_inputs =
617 proof.hiding_oink_proof.size() - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
618
619 // First BN254 commitment is at element index mega_num_pub_inputs.
620 // Set x-coordinate to Fq::modulus + 1 (non-canonical, with no sign bit).
621 auto corrupted = compress_and_corrupt(proof, mega_num_pub_inputs, Fq::modulus + 1);
622 EXPECT_THROW_OR_ABORT(ProofCompressor::decompress_chonk_proof(corrupted, mega_num_pub_inputs), "");
623}
624
625// Rejects a canonical x-coordinate that is not on the BN254 curve
626TEST_F(ChonkTests, DecompressionRejectsInvalidBN254CurvePoint)
627{
629
630 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
631 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
632 size_t mega_num_pub_inputs =
633 proof.hiding_oink_proof.size() - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
634
635 // x=4 is not on BN254
636 Fq x_not_on_curve(4);
637 Fq rhs = x_not_on_curve * x_not_on_curve * x_not_on_curve + Fq(3);
638 auto [is_sq, _] = rhs.sqrt();
639 ASSERT_FALSE(is_sq);
640
641 auto corrupted = compress_and_corrupt(proof, mega_num_pub_inputs, uint256_t(x_not_on_curve));
642 EXPECT_THROW_OR_ABORT(ProofCompressor::decompress_chonk_proof(corrupted, mega_num_pub_inputs), "");
643}
644
645// Rejects a compressed proof that is too short (read_u256 bounds check)
646TEST_F(ChonkTests, DecompressionRejectsTruncatedProof)
647{
648 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
649 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
650 size_t mega_num_pub_inputs =
651 proof.hiding_oink_proof.size() - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
652
653 auto compressed = ProofCompressor::compress_chonk_proof(proof);
654 // Truncate by removing the last 32-byte element
655 compressed.resize(compressed.size() - 32);
656 EXPECT_THROW_OR_ABORT(ProofCompressor::decompress_chonk_proof(compressed, mega_num_pub_inputs), "");
657}
#define BB_ASSERT_NEQ(actual, expected,...)
Definition assert.hpp:98
#define EXPECT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:223
#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.
static void test_kernel_io_tampering(KernelIOField field_to_tamper)
Helper function to test tampering with KernelIO fields.
Chonk::FF FF
Chonk::Commitment Commitment
static void SetUpTestSuite()
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:42
HypernovaDeciderProver DeciderProver
Definition chonk.hpp:84
std::vector< CircuitKind > circuit_kinds
Definition chonk.hpp:159
curve::BN254::AffineElement Commitment
Definition chonk.hpp:56
MegaCircuitBuilder ClientCircuit
Definition chonk.hpp:58
bb::fr FF
Definition chonk.hpp:55
VerificationQueue verification_queue
Definition chonk.hpp:182
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.
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)
bb::fq BaseField
Definition bn254.hpp:19
bb::fr ScalarField
Definition bn254.hpp:18
Native representation and serde for AppIO 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
void write(B &buf, field2< base_field, Params > const &value)
ChonkVerifier< false > ChonkNativeVerifier
::testing::Types< BN254Settings > TestSettings
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
bb::VectorAffineElementPushSpan< BaseParams > rhs
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
VectorField result