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 <ranges>
2
17#include "gtest/gtest.h"
18
19using namespace bb;
20
21static constexpr size_t SMALL_LOG_2_NUM_GATES = 5;
22
23class ChonkTests : public ::testing::Test {
24 protected:
26
28 using FF = typename Flavor::FF;
35 using CircuitProducer = PrivateFunctionExecutionMockCircuitProducer;
37
38 public:
45 static void tamper_with_proof(HonkProof& proof, size_t public_inputs_offset)
46 {
47 // Tamper with the commitment in the proof
48 Commitment commitment = FrCodec::deserialize_from_fields<Commitment>(
49 std::span{ proof }.subspan(public_inputs_offset, FrCodec::template calc_num_fields<Commitment>()));
50 commitment = commitment + Commitment::one();
51 auto commitment_frs = FrCodec::serialize_to_fields<Commitment>(commitment);
52 for (size_t idx = 0; idx < 4; ++idx) {
53 proof[public_inputs_offset + idx] = commitment_frs[idx];
54 }
55 }
56
58 size_t num_app_circuits, TestSettings settings = {}, bool check_circuit_sizes = false)
59 {
60 CircuitProducer circuit_producer(num_app_circuits);
61 const size_t num_circuits = circuit_producer.total_num_circuits;
62 Chonk ivc{ num_circuits };
63
64 for (size_t j = 0; j < num_circuits; ++j) {
65 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings, check_circuit_sizes);
66 }
67 return { ivc.prove(), ivc.get_hiding_kernel_vk_and_hash() };
68 };
69
70 static bool verify_chonk(const ChonkProof& proof, const std::shared_ptr<MegaZKFlavor::VKAndHash>& vk_and_hash)
71 {
72 ChonkVerifier verifier(vk_and_hash);
73 return verifier.verify(proof);
74 }
75
80
85
92 {
94
95 const size_t NUM_APP_CIRCUITS = 2;
96 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
97 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
98 Chonk ivc{ NUM_CIRCUITS };
99 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
100
101 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
102 auto [circuit, vk] = circuit_producer.create_next_circuit_and_vk(ivc, settings);
103 ivc.accumulate(circuit, vk);
104
105 // After accumulating 3 circuits (app, kernel, app), we have 2 proofs in the queue
106 if (idx == 2) {
107 EXPECT_EQ(ivc.verification_queue.size(), 2);
108
109 auto& app_entry = ivc.verification_queue[1];
110 ASSERT_FALSE(app_entry.is_kernel) << "Expected second queue entry to be an app";
111
113 size_t num_public_inputs = app_entry.honk_vk->num_public_inputs;
114 AppIOSerde app_io = AppIOSerde::from_proof(app_entry.proof, num_public_inputs);
115
116 // Double the pairing points (multiply by 2) - creates valid but different points
117 app_io.pairing_inputs.P0() = app_io.pairing_inputs.P0() + app_io.pairing_inputs.P0();
118 app_io.pairing_inputs.P1() = app_io.pairing_inputs.P1() + app_io.pairing_inputs.P1();
119
120 EXPECT_TRUE(app_io.pairing_inputs.check());
121
122 app_io.to_proof(app_entry.proof, num_public_inputs);
123 }
124 }
125
126 auto proof = ivc.prove();
127 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
128 }
129
135 static void test_kernel_io_tampering(KernelIOField field_to_tamper)
136 {
138
139 const size_t NUM_APP_CIRCUITS = 2;
140 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
141 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
142 Chonk ivc{ NUM_CIRCUITS };
143 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
144
145 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
146 auto [circuit, vk] = circuit_producer.create_next_circuit_and_vk(ivc, settings);
147 ivc.accumulate(circuit, vk);
148
149 // After accumulating 3 circuits (app, kernel, app), we have 2 proofs in the queue
150 if (idx == 2) {
151 EXPECT_EQ(ivc.verification_queue.size(), 2);
152
153 auto& kernel_entry = ivc.verification_queue[0];
154 ASSERT_TRUE(kernel_entry.is_kernel) << "Expected first queue entry to be a kernel";
155
156 using KernelIOSerde = bb::stdlib::recursion::honk::KernelIOSerde;
157 size_t num_public_inputs = kernel_entry.honk_vk->num_public_inputs;
158 KernelIOSerde kernel_io = KernelIOSerde::from_proof(kernel_entry.proof, num_public_inputs);
159
160 // Tamper with the specified field
161 switch (field_to_tamper) {
163 // Replace with valid pairing points at infinity (different from actual accumulated values)
164 kernel_io.pairing_inputs.P0() = Commitment::infinity();
165 kernel_io.pairing_inputs.P1() = Commitment::infinity();
166 EXPECT_TRUE(kernel_io.pairing_inputs.check());
167 break;
168 }
170 kernel_io.output_hn_accum_hash += FF(1);
171 break;
173 kernel_io.kernel_return_data = kernel_io.kernel_return_data + Commitment::one();
174 break;
176 kernel_io.app_return_data = kernel_io.app_return_data + Commitment::one();
177 break;
179 kernel_io.ecc_op_tables[0] = kernel_io.ecc_op_tables[0] + Commitment::one();
180 break;
181 }
182
183 kernel_io.to_proof(kernel_entry.proof, num_public_inputs);
184 }
185 }
186
187 auto proof = ivc.prove();
188 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
189 }
190
202 {
203 using HidingKernelIOSerde = bb::stdlib::recursion::honk::HidingKernelIOSerde;
204
205 const size_t NUM_APP_CIRCUITS = 2;
206 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
207 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
208 Chonk ivc{ NUM_CIRCUITS };
209 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
210
211 // Accumulate all circuits
212 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
213 auto [circuit, vk] = circuit_producer.create_next_circuit_and_vk(ivc, settings);
214 ivc.accumulate(circuit, vk);
215 }
216
217 // Extract field from Tail kernel's proof before prove() generates HidingKernel
218 HidingKernelIOSerde tail_io;
219 for (auto& it : std::ranges::reverse_view(ivc.verification_queue)) {
220 if (it.is_kernel) {
221 size_t num_public_inputs = it.honk_vk->num_public_inputs;
222 ASSERT_EQ(num_public_inputs, HidingKernelIOSerde::PUBLIC_INPUTS_SIZE)
223 << "Tail kernel should use HidingKernelIO format";
224 tail_io = HidingKernelIOSerde::from_proof(it.proof, num_public_inputs);
225 break;
226 }
227 }
228
229 // Generate the final proof (creates HidingKernel)
230 auto proof = ivc.prove();
231 auto vk_and_hash = ivc.get_hiding_kernel_vk_and_hash();
232
233 // Extract field from HidingKernel's proof (final mega_proof)
234 size_t hiding_kernel_pub_inputs = vk_and_hash->vk->num_public_inputs;
235 ASSERT_EQ(hiding_kernel_pub_inputs, HidingKernelIOSerde::PUBLIC_INPUTS_SIZE)
236 << "HidingKernel should use HidingKernelIO format";
237 HidingKernelIOSerde hiding_io = HidingKernelIOSerde::from_proof(proof.mega_proof, hiding_kernel_pub_inputs);
238
239 // Verify field propagated correctly from Tail kernel to HidingKernel
240 switch (field_to_test) {
242 EXPECT_EQ(tail_io.pairing_inputs.P0(), hiding_io.pairing_inputs.P0())
243 << "P0 mismatch: Tail has " << tail_io.pairing_inputs.P0() << " but HidingKernel has "
244 << hiding_io.pairing_inputs.P0();
245 EXPECT_EQ(tail_io.pairing_inputs.P1(), hiding_io.pairing_inputs.P1())
246 << "P1 mismatch: Tail has " << tail_io.pairing_inputs.P1() << " but HidingKernel has "
247 << hiding_io.pairing_inputs.P1();
248 break;
250 EXPECT_EQ(tail_io.kernel_return_data, hiding_io.kernel_return_data)
251 << "kernel_return_data mismatch: Tail has " << tail_io.kernel_return_data << " but HidingKernel has "
252 << hiding_io.kernel_return_data;
253 break;
255 for (size_t i = 0; i < tail_io.ecc_op_tables.size(); ++i) {
256 EXPECT_EQ(tail_io.ecc_op_tables[i], hiding_io.ecc_op_tables[i])
257 << "M_tail[" << i << "] mismatch: Tail has " << tail_io.ecc_op_tables[i] << " but HidingKernel has "
258 << hiding_io.ecc_op_tables[i];
259 }
260 break;
261 }
262 }
263};
264
272TEST_F(ChonkTests, TestCircuitSizes)
273{
274 const size_t NUM_APP_CIRCUITS = 2;
275
276 // Check circuit sizes when no settings are passed
277 {
278 auto [proof, vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, {}, true);
279 EXPECT_TRUE(verify_chonk(proof, vk));
280 }
281
282 // Check circuit sizes when no settings are passed
283 {
284 auto [proof, vk] =
285 accumulate_and_prove_ivc(NUM_APP_CIRCUITS, { .log2_num_gates = SMALL_LOG_2_NUM_GATES }, true);
286 EXPECT_TRUE(verify_chonk(proof, vk));
287 }
288};
289
297{
298 const size_t NUM_APP_CIRCUITS = 2;
299 auto [proof, vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS);
300
301 EXPECT_TRUE(verify_chonk(proof, vk));
302};
303
311TEST_F(ChonkTests, BadProofFailure)
312{
313 BB_DISABLE_ASSERTS(); // Disable assert in HN prover
314
315 const size_t NUM_APP_CIRCUITS = 2;
316 // Confirm that the IVC verifies if nothing is tampered with
317 {
318
319 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
320 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
321 Chonk ivc{ NUM_CIRCUITS };
322 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
323
324 // Construct and accumulate a set of mocked private function execution circuits
325 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
326 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
327 }
328 auto proof = ivc.prove();
329 EXPECT_TRUE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
330 }
331
332 // The IVC throws an exception if the FIRST fold proof is tampered with
333 {
334 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
335 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
336 Chonk ivc{ NUM_CIRCUITS };
337
338 size_t num_public_inputs = 0;
339
340 // Construct and accumulate a set of mocked private function execution circuits
341 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
342 auto [circuit, vk] =
343 circuit_producer.create_next_circuit_and_vk(ivc, { .log2_num_gates = SMALL_LOG_2_NUM_GATES });
344 ivc.accumulate(circuit, vk);
345
346 if (idx == 1) {
347 num_public_inputs = circuit.num_public_inputs();
348 }
349
350 if (idx == 2) {
351 EXPECT_EQ(ivc.verification_queue.size(), 2); // two proofs after 3 calls to accumulation
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 == 2) {
373 EXPECT_EQ(ivc.verification_queue.size(), 2); // two proofs after 3 calls to accumulation
374 tamper_with_proof(ivc.verification_queue[1].proof,
375 circuit.num_public_inputs()); // tamper with second proof
376 }
377 }
378 auto proof = ivc.prove();
379 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
380 }
381};
382
387TEST_F(ChonkTests, VKIndependenceFromNumberOfCircuits)
388{
389 const TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
390
391 auto [unused_1, vk_and_hash_1] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
392 auto [unused_2, vk_and_hash_2] = accumulate_and_prove_ivc(/*num_app_circuits=*/3, settings);
393
394 // Check the equality of the hiding kernel VKeys
395 EXPECT_EQ(*vk_and_hash_1->vk.get(), *vk_and_hash_2->vk.get());
396};
397
402TEST_F(ChonkTests, VKIndependenceFromCircuitSize)
403{
404 // Run IVC for two sets of circuits
405 const size_t NUM_APP_CIRCUITS = 1;
406 const size_t log2_num_gates_small = 5;
407 const size_t log2_num_gates_big = 18;
408
409 const TestSettings settings_1{ .log2_num_gates = log2_num_gates_small };
410 const TestSettings settings_2{ .log2_num_gates = log2_num_gates_big };
411
412 auto [unused_1, vk_and_hash_1] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, settings_1);
413 auto [unused_2, vk_and_hash_2] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, settings_2);
414
415 // Check the equality of the hiding kernel VKeys
416 EXPECT_EQ(*vk_and_hash_1->vk.get(), *vk_and_hash_2->vk.get());
417};
418
423HEAVY_TEST(ChonkKernelCapacity, MaxCapacityPassing)
424{
426
427 const size_t NUM_APP_CIRCUITS = 17;
428 auto [proof, vk] = ChonkTests::accumulate_and_prove_ivc(NUM_APP_CIRCUITS);
429
430 bool verified = ChonkTests::verify_chonk(proof, vk);
431 EXPECT_TRUE(verified);
432};
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 with error msgpack::v1::type_error with description "std::bad_cast"
471 EXPECT_THROW(ChonkProof::from_msgpack_buffer(buffer), msgpack::v1::type_error);
472 }
473};
474
480TEST_F(ChonkTests, KernelPairingInputsTamperingFailure)
481{
482 ChonkTests::test_kernel_io_tampering(KernelIOField::PAIRING_INPUTS);
483}
484
490TEST_F(ChonkTests, AppPairingInputsTamperingFailure)
491{
493}
494
501TEST_F(ChonkTests, AccumulatorHashTamperingFailure)
502{
503 ChonkTests::test_kernel_io_tampering(KernelIOField::ACCUMULATOR_HASH);
504}
505
511TEST_F(ChonkTests, KernelReturnDataTamperingFailure)
512{
513 ChonkTests::test_kernel_io_tampering(KernelIOField::KERNEL_RETURN_DATA);
514}
515
521TEST_F(ChonkTests, AppReturnDataTamperingFailure)
522{
523 ChonkTests::test_kernel_io_tampering(KernelIOField::APP_RETURN_DATA);
524}
525
531TEST_F(ChonkTests, EccOpTablesTamperingFailure)
532{
533 ChonkTests::test_kernel_io_tampering(KernelIOField::ECC_OP_TABLES);
534}
535
542TEST_F(ChonkTests, PairingPointsPropagationConsistency)
543{
544 ChonkTests::test_hiding_kernel_io_propagation(HidingKernelIOField::PAIRING_INPUTS);
545}
546
552TEST_F(ChonkTests, KernelReturnDataPropagationConsistency)
553{
554 ChonkTests::test_hiding_kernel_io_propagation(HidingKernelIOField::KERNEL_RETURN_DATA);
555}
556
562TEST_F(ChonkTests, MTailPropagationConsistency)
563{
564 ChonkTests::test_hiding_kernel_io_propagation(HidingKernelIOField::ECC_OP_TABLES);
565}
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
TEST_F(ChonkTests, TestCircuitSizes)
Test sizes of the circuits generated by MockCircuitProducer.
PrivateFunctionExecutionMockCircuitProducer CircuitProducer
static void test_hiding_kernel_io_propagation(HidingKernelIOField field_to_test)
Helper function to test HidingKernelIO field propagation consistency.
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.
HidingKernelIOField
Enum for specifying which HidingKernelIO field to test for propagation consistency.
Flavor::Commitment Commitment
static void test_kernel_io_tampering(KernelIOField field_to_tamper)
Helper function to test tampering with KernelIO fields.
static void SetUpTestSuite()
typename Flavor::FF FF
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.
KernelIOField
Enum for specifying which KernelIO field to tamper with in tests.
The IVC scheme used by the aztec client for private function execution.
Definition chonk.hpp:38
HypernovaDeciderProver DeciderProver
Definition chonk.hpp:77
ChonkProof prove()
Construct Chonk proof, which, if verified, fully establishes the correctness of RCG.
Definition chonk.cpp:538
ProverInstance_< Flavor > ProverInstance
Definition chonk.hpp:49
MegaFlavor Flavor
Definition chonk.hpp:42
VerifierInstance_< Flavor > VerifierInstance
Definition chonk.hpp:51
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:391
MegaCircuitBuilder ClientCircuit
Definition chonk.hpp:52
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
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...
Native representation and serde for AppIO public inputs.
Native representation and serde for HidingKernelIO public inputs.
For test purposes only: Native representation and serde for KernelIO public inputs
uint8_t buffer[RANDOM_BUFFER_SIZE]
Definition engine.cpp:48
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::vector< fr > HonkProof
Definition proof.hpp:15
::testing::Types< BN254Settings, GrumpkinSettings > TestSettings
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)
static ChonkProof_ from_file_msgpack(const std::string &filename)
#define HEAVY_TEST(x, y)
Definition test.hpp:9