3#include <sys/resource.h>
22#include "gtest/gtest.h"
28constexpr size_t SMALL_LOG_2_NUM_GATES = 5;
33enum class KernelIOField : uint8_t {
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];
79 size_t num_app_circuits,
82 bool check_circuit_sizes =
false)
85 return run_ivc_impl(circuit_producer, settings, post_hook, check_circuit_sizes);
89 std::vector<bool> leading_is_kernel_flags,
92 bool check_circuit_sizes =
false)
95 return run_ivc_impl(circuit_producer, settings, post_hook, check_circuit_sizes);
99 size_t num_app_circuits,
TestSettings settings = {},
bool check_circuit_sizes =
false)
101 return run_ivc(num_app_circuits, settings,
nullptr, check_circuit_sizes);
107 return verifier.
verify(proof);
119 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
120 auto [proof,
vk] =
run_ivc(2, settings, [](
Chonk& ivc,
size_t idx) {
123 ASSERT_FALSE(app_entry.is_kernel()) <<
"Expected second queue entry to be an app";
126 size_t num_public_inputs = app_entry.num_public_inputs();
127 AppIOSerde app_io = AppIOSerde::from_proof(app_entry.proof, num_public_inputs);
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();
133 EXPECT_TRUE(app_io.pairing_inputs.check());
135 app_io.to_proof(app_entry.proof, num_public_inputs);
150 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
152 run_ivc(MAX_APPS_PER_KERNEL + 1, settings, [field_to_tamper](
Chonk& ivc,
size_t idx) {
153 if (idx == MAX_APPS_PER_KERNEL) {
155 ASSERT_TRUE(kernel_entry.is_kernel()) <<
"Expected first queue entry to be a kernel";
158 size_t num_public_inputs = kernel_entry.num_public_inputs();
159 KernelIOSerde kernel_io = KernelIOSerde::from_proof(kernel_entry.proof, num_public_inputs);
162 switch (field_to_tamper) {
163 case KernelIOField::PAIRING_INPUTS: {
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();
169 EXPECT_TRUE(kernel_io.pairing_inputs.check());
172 case KernelIOField::ACCUMULATOR_HASH:
173 kernel_io.output_hn_accum_hash +=
FF(1);
175 case KernelIOField::KERNEL_RETURN_DATA:
176 kernel_io.kernel_return_data = kernel_io.kernel_return_data + Commitment::one();
178 case KernelIOField::APP_RETURN_DATA:
179 kernel_io.app_return_data[0] = kernel_io.app_return_data[0] + Commitment::one();
181 case KernelIOField::ECC_OP_HASH:
182 kernel_io.ecc_op_hash +=
FF(1);
186 kernel_io.to_proof(kernel_entry.proof, num_public_inputs);
197 bool check_circuit_sizes)
199 const size_t num_circuits = circuit_producer.total_num_circuits;
202 for (
size_t idx = 0; idx < num_circuits; ++idx) {
203 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings, check_circuit_sizes);
208 return { ivc.prove(), ivc.get_hiding_kernel_vk_and_hash() };
221 const size_t NUM_APP_CIRCUITS = 2;
225 auto [proof,
vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, {},
true);
226 EXPECT_TRUE(verify_chonk(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));
245 const size_t NUM_APP_CIRCUITS = 2;
246 auto [proof,
vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS);
248 EXPECT_TRUE(verify_chonk(proof,
vk));
262 const size_t NUM_APP_CIRCUITS = 2;
266 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
267 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
269 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
272 for (
size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
273 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
275 auto proof = ivc.prove();
276 EXPECT_TRUE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
281 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
282 const size_t NUM_CIRCUITS = circuit_producer.total_num_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 });
290 tamper_with_proof(ivc.verification_queue[0].proof,
291 ivc.verification_queue[0].num_public_inputs());
294 auto proof = ivc.prove();
295 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
300 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
301 const size_t NUM_CIRCUITS = circuit_producer.total_num_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 });
309 tamper_with_proof(ivc.verification_queue[1].proof,
310 ivc.verification_queue[1].num_public_inputs());
313 auto proof = ivc.prove();
314 EXPECT_FALSE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
324 const TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
326 auto [unused_1, vk_and_hash_1] = accumulate_and_prove_ivc(1, settings);
327 auto [unused_2, vk_and_hash_2] = accumulate_and_prove_ivc(3, settings);
330 EXPECT_EQ(*vk_and_hash_1->vk.get(), *vk_and_hash_2->vk.get());
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;
344 const TestSettings settings_1{ .log2_num_gates = log2_num_gates_small };
345 const TestSettings settings_2{ .log2_num_gates = log2_num_gates_big };
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);
351 EXPECT_EQ(*vk_and_hash_1->vk.get(), *vk_and_hash_2->vk.get());
359HEAVY_TEST(ChonkKernelCapacity, MaxCapacityPassing)
366 EXPECT_TRUE(verified);
377 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
378 auto [proof,
vk] = accumulate_and_prove_ivc(1, settings);
381 const std::string filename =
"proof.msgpack";
382 proof.to_file_msgpack(filename);
385 EXPECT_TRUE(verify_chonk(proof_deserialized,
vk));
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();
394 EXPECT_TRUE(verify_chonk(proof_deserialized,
vk));
398 msgpack::sbuffer
buffer = proof.to_msgpack_buffer();
400 EXPECT_TRUE(verify_chonk(proof_deserialized,
vk));
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); });
426 test_kernel_io_tampering(GetParam());
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:
466 TestSettings settings_one{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
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;
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);
480 auto valid_accumulator = chonk_one.prover_accumulator;
484 MockDatabusProducer mock_databus;
486 CircuitProducer producer_two(num_app_circuits);
487 TestSettings settings_two{ .log2_num_gates = SMALL_LOG_2_NUM_GATES + 1 };
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);
495 if (circuit_idx == MAX_APPS_PER_KERNEL) {
497 invalid_chonk.prover_accumulator.non_shifted_commitment,
498 "Accumulators should be different.");
499 invalid_chonk.prover_accumulator = valid_accumulator;
504 auto proof = invalid_chonk.prove();
505 auto vk_and_hash = invalid_chonk.get_hiding_kernel_vk_and_hash();
509 EXPECT_FALSE(
result) <<
"Substituting the accumulator should cause verification to fail, but it passed";
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;
525 constexpr size_t LOG2_NUM_GATES = 10;
526 const size_t NUM_APP_CIRCUITS = 1;
528 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
529 const size_t num_circuits = circuit_producer.total_num_circuits;
531 TestSettings settings{ .log2_num_gates = LOG2_NUM_GATES };
533 for (
size_t j = 0; j < num_circuits; ++j) {
534 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
537 info(
"Peak RSS before prove: ", get_peak_rss_mib(),
" MiB");
541 info(
"Peak RSS after prove: ", get_peak_rss_mib(),
" MiB");
544 EXPECT_TRUE(verify_chonk(proof, ivc.get_hiding_kernel_vk_and_hash()));
549 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
550 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(1, settings);
552 auto original_flat = proof.to_field_elements();
553 info(
"Original proof size: ", original_flat.size(),
" Fr elements (", original_flat.size() * 32,
" bytes)");
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");
561 EXPECT_GE(ratio, 1.5) <<
"Compression ratio " << ratio <<
"x is below the expected minimum of 1.5x";
563 size_t mega_num_pub_inputs =
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;
575 EXPECT_TRUE(verify_chonk(decompressed, vk_and_hash));
581static std::vector<uint8_t> compress_and_corrupt(
const ChonkProof& proof,
size_t element_idx,
const uint256_t&
value)
584 size_t byte_offset = element_idx * 32;
585 EXPECT_LT(byte_offset + 32, compressed.size());
587 std::vector<uint8_t> buf;
589 std::copy(buf.begin(), buf.end(), compressed.begin() +
static_cast<ptrdiff_t
>(byte_offset));
596 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
597 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(1, settings);
598 size_t mega_num_pub_inputs =
600 ASSERT_GT(mega_num_pub_inputs, 0);
605 auto corrupted = compress_and_corrupt(proof, 0,
Fr::modulus);
614 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
615 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(1, settings);
616 size_t mega_num_pub_inputs =
621 auto corrupted = compress_and_corrupt(proof, mega_num_pub_inputs,
Fq::modulus + 1);
630 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
631 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(1, settings);
632 size_t mega_num_pub_inputs =
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();
641 auto corrupted = compress_and_corrupt(proof, mega_num_pub_inputs,
uint256_t(x_not_on_curve));
648 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
649 auto [proof, vk_and_hash] = accumulate_and_prove_ivc(1, settings);
650 size_t mega_num_pub_inputs =
655 compressed.resize(compressed.size() - 32);
#define BB_ASSERT_NEQ(actual, expected,...)
#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.
static void test_kernel_io_tampering(KernelIOField field_to_tamper)
Helper function to test tampering with KernelIO fields.
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.
HypernovaDeciderProver DeciderProver
std::vector< CircuitKind > circuit_kinds
curve::BN254::AffineElement Commitment
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.
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)
Native representation and serde for AppIO 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
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
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