3#include <sys/resource.h>
22#include "gtest/gtest.h"
28constexpr size_t SMALL_LOG_2_NUM_GATES = 5;
33enum class KernelIOField : uint8_t {
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];
83 size_t num_app_circuits,
86 bool check_circuit_sizes =
false)
89 return run_ivc_impl(circuit_producer, settings, post_hook, check_circuit_sizes);
93 std::vector<bool> leading_is_kernel_flags,
96 bool check_circuit_sizes =
false)
99 return run_ivc_impl(circuit_producer, settings, post_hook, check_circuit_sizes);
103 size_t num_app_circuits,
TestSettings settings = {},
bool check_circuit_sizes =
false)
105 return run_ivc(num_app_circuits, settings,
nullptr, check_circuit_sizes);
111 return verifier.
verify(proof);
123 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
124 auto [proof,
vk] =
run_ivc(2, settings, [](
Chonk& ivc,
size_t idx) {
127 ASSERT_FALSE(app_entry.is_kernel) <<
"Expected second queue entry to be an app";
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);
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();
137 EXPECT_TRUE(app_io.pairing_inputs.check());
139 app_io.to_proof(app_entry.proof, num_public_inputs);
154 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
155 auto [proof,
vk] =
run_ivc(4, settings, [field_to_tamper](
Chonk& ivc,
size_t idx) {
158 ASSERT_TRUE(kernel_entry.is_kernel) <<
"Expected first queue entry to be a kernel";
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);
165 switch (field_to_tamper) {
166 case KernelIOField::PAIRING_INPUTS: {
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();
172 EXPECT_TRUE(kernel_io.pairing_inputs.check());
175 case KernelIOField::ACCUMULATOR_HASH:
176 kernel_io.output_hn_accum_hash +=
FF(1);
178 case KernelIOField::KERNEL_RETURN_DATA:
179 kernel_io.kernel_return_data = kernel_io.kernel_return_data + Commitment::one();
181 case KernelIOField::APP_RETURN_DATA:
182 kernel_io.app_return_data[0] = kernel_io.app_return_data[0] + Commitment::one();
184 case KernelIOField::ECC_OP_HASH:
185 kernel_io.ecc_op_hash +=
FF(1);
189 kernel_io.to_proof(kernel_entry.proof, num_public_inputs);
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)) +
214 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
217 KernelIOSerde tail_io;
218 auto [proof, vk_and_hash] =
run_ivc(
219 NUM_APP_CIRCUITS, settings, [&tail_io, &NUM_TOTAL_CIRCUITS](
Chonk& ivc,
size_t idx) {
221 if (idx == NUM_TOTAL_CIRCUITS - 2) {
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);
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);
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;
251 bool check_circuit_sizes)
253 const size_t num_circuits = circuit_producer.total_num_circuits;
254 Chonk ivc{ num_circuits };
256 for (
size_t idx = 0; idx < num_circuits; ++idx) {
257 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings, check_circuit_sizes);
262 return { ivc.prove(), ivc.get_hiding_kernel_vk_and_hash() };
275 const size_t NUM_APP_CIRCUITS = 2;
279 auto [proof,
vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, {},
true);
280 EXPECT_TRUE(verify_chonk(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));
299 const size_t NUM_APP_CIRCUITS = 2;
300 auto [proof,
vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS);
302 EXPECT_TRUE(verify_chonk(proof,
vk));
316 const size_t NUM_APP_CIRCUITS = 2;
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 };
326 for (
size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
327 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
329 auto proof = ivc.
prove();
330 EXPECT_TRUE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
335 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
336 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
337 Chonk ivc{ NUM_CIRCUITS };
339 size_t num_public_inputs = 0;
342 for (
size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
344 circuit_producer.create_next_circuit_and_vk(ivc, { .log2_num_gates = SMALL_LOG_2_NUM_GATES });
348 num_public_inputs = circuit.num_public_inputs();
352 tamper_with_proof(ivc.verification_queue[0].proof,
356 auto proof = ivc.prove();
357 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
362 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
363 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
364 Chonk ivc{ NUM_CIRCUITS };
367 for (
size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
369 circuit_producer.create_next_circuit_and_vk(ivc, { .log2_num_gates = SMALL_LOG_2_NUM_GATES });
373 tamper_with_proof(ivc.verification_queue[1].proof,
374 circuit.num_public_inputs());
377 auto proof = ivc.prove();
378 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
388 const TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
390 auto [unused_1, vk_and_hash_1] = accumulate_and_prove_ivc(1, settings);
391 auto [unused_2, vk_and_hash_2] = accumulate_and_prove_ivc(3, settings);
394 EXPECT_EQ(*vk_and_hash_1->vk.get(), *vk_and_hash_2->vk.get());
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;
408 const TestSettings settings_1{ .log2_num_gates = log2_num_gates_small };
409 const TestSettings settings_2{ .log2_num_gates = log2_num_gates_big };
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);
415 EXPECT_EQ(*vk_and_hash_1->vk.get(), *vk_and_hash_2->vk.get());
423HEAVY_TEST(ChonkKernelCapacity, MaxCapacityPassing)
430 EXPECT_TRUE(verified);
441 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
442 auto [proof,
vk] = accumulate_and_prove_ivc(1, settings);
445 const std::string filename =
"proof.msgpack";
446 proof.to_file_msgpack(filename);
449 EXPECT_TRUE(verify_chonk(proof_deserialized,
vk));
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();
458 EXPECT_TRUE(verify_chonk(proof_deserialized,
vk));
462 msgpack::sbuffer
buffer = proof.to_msgpack_buffer();
464 EXPECT_TRUE(verify_chonk(proof_deserialized,
vk));
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); });
490 test_kernel_io_tampering(GetParam());
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:
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;
539 constexpr size_t LOG2_NUM_GATES = 10;
540 const size_t NUM_APP_CIRCUITS = 1;
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 };
547 for (
size_t j = 0; j < num_circuits; ++j) {
548 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
551 info(
"Peak RSS before prove: ", get_peak_rss_mib(),
" MiB");
555 info(
"Peak RSS after prove: ", get_peak_rss_mib(),
" MiB");
558 EXPECT_TRUE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
563 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
564 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(1, settings);
566 auto original_flat = proof.to_field_elements();
567 info(
"Original proof size: ", original_flat.size(),
" Fr elements (", original_flat.size() * 32,
" bytes)");
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");
575 EXPECT_GE(ratio, 1.5) <<
"Compression ratio " << ratio <<
"x is below the expected minimum of 1.5x";
577 size_t mega_num_pub_inputs =
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;
589 EXPECT_TRUE(verify_chonk(decompressed, vk_and_hash));
595static std::vector<uint8_t> compress_and_corrupt(
const ChonkProof& proof,
size_t element_idx,
const uint256_t&
value)
598 size_t byte_offset = element_idx * 32;
599 EXPECT_LT(byte_offset + 32, compressed.size());
601 std::vector<uint8_t> buf;
603 std::copy(buf.begin(), buf.end(), compressed.begin() +
static_cast<ptrdiff_t
>(byte_offset));
610 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
611 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(1, settings);
612 size_t mega_num_pub_inputs =
614 ASSERT_GT(mega_num_pub_inputs, 0);
619 auto corrupted = compress_and_corrupt(proof, 0,
Fr::modulus);
628 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
629 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(1, settings);
630 size_t mega_num_pub_inputs =
635 auto corrupted = compress_and_corrupt(proof, mega_num_pub_inputs,
Fq::modulus + 1);
644 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
645 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(1, settings);
646 size_t mega_num_pub_inputs =
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();
655 auto corrupted = compress_and_corrupt(proof, mega_num_pub_inputs,
uint256_t(x_not_on_curve));
662 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
663 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(1, settings);
664 size_t mega_num_pub_inputs =
669 compressed.resize(compressed.size() - 32);
#define EXPECT_THROW_OR_ABORT(statement, matcher)
#define BB_DISABLE_ASSERTS()
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()
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.
HypernovaDeciderProver DeciderProver
ChonkProof prove()
Construct Chonk proof using the batched MegaZK + Translator protocol.
ProverInstance_< Flavor > ProverInstance
VerifierInstance_< Flavor > VerifierInstance
void accumulate(ClientCircuit &circuit, const std::shared_ptr< MegaVerificationKey > &precomputed_vk) override
Perform prover work for accumulation (e.g. HN folding, merge proving)
MegaCircuitBuilder ClientCircuit
VerificationQueue verification_queue
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::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.
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...
Native representation and serde for AppIO public inputs.
Native representation and serde for HidingKernelIO public inputs.
std::unique_ptr< uint8_t[]> buffer
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.
std::vector< fr > HonkProof
::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
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