Skip to content

Commit c572e95

Browse files
chore: chonk proof compression poc (#20645)
Adds Chonk proof compression logic that utilizes two things: 1. Suyash's proposed EC point compression: store (x, sign_bit) instead of (x, y); leverage curve equation to reconstruct y-coord 2. Store all fields as u256 / 32 bytes. (Similar effect to Snappy compression but seems to do slightly better) Overall effect is that each single "component" of the proof is represented as 32 bytes. E.g. an Fq (naively 2 Frs) becomes one u256. A bn254 commitment (naively 2 Fqs = 4 Frs) becomes one u256 representing the x-coord. High level stats: | Metric | Value | |-------------------|------------------------------------------| | Original size | 1,935 Fr elements = ~60 KB| | Snappy compressed size | ~47 KB | | Compressed size | ~35 KB | | Compression ratio | 1.72x | | Compress time | 0.64 ms | | Decompress time | 3.5 ms | --------- Co-authored-by: Jonathan Hao <jonathan@aztec-labs.com>
1 parent 2799c35 commit c572e95

8 files changed

Lines changed: 726 additions & 1 deletion

File tree

barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "barretenberg/bbapi/bbapi_chonk.hpp"
22
#include "barretenberg/chonk/chonk_verifier.hpp"
33
#include "barretenberg/chonk/mock_circuit_producer.hpp"
4+
#include "barretenberg/chonk/proof_compression.hpp"
45
#include "barretenberg/common/log.hpp"
56
#include "barretenberg/common/serialize.hpp"
67
#include "barretenberg/common/throw_or_abort.hpp"
@@ -253,4 +254,17 @@ ChonkStats::Response ChonkStats::execute([[maybe_unused]] BBApiRequest& request)
253254
return response;
254255
}
255256

257+
ChonkCompressProof::Response ChonkCompressProof::execute(const BBApiRequest& /*request*/) &&
258+
{
259+
BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
260+
return { .compressed_proof = ProofCompressor::compress_chonk_proof(proof) };
261+
}
262+
263+
ChonkDecompressProof::Response ChonkDecompressProof::execute(const BBApiRequest& /*request*/) &&
264+
{
265+
BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
266+
size_t mega_num_pub = ProofCompressor::compressed_mega_num_public_inputs(compressed_proof.size());
267+
return { .proof = ProofCompressor::decompress_chonk_proof(compressed_proof, mega_num_pub) };
268+
}
269+
256270
} // namespace bb::bbapi

barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.hpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,48 @@ struct ChonkStats {
239239
bool operator==(const ChonkStats&) const = default;
240240
};
241241

242+
/**
243+
* @struct ChonkCompressProof
244+
* @brief Compress a Chonk proof to a compact byte representation
245+
*
246+
* @details Uses point compression and uniform 32-byte encoding to reduce proof size (~1.72x).
247+
*/
248+
struct ChonkCompressProof {
249+
static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkCompressProof";
250+
251+
struct Response {
252+
static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkCompressProofResponse";
253+
std::vector<uint8_t> compressed_proof;
254+
MSGPACK_FIELDS(compressed_proof);
255+
bool operator==(const Response&) const = default;
256+
};
257+
258+
ChonkProof proof;
259+
Response execute(const BBApiRequest& request = {}) &&;
260+
MSGPACK_FIELDS(proof);
261+
bool operator==(const ChonkCompressProof&) const = default;
262+
};
263+
264+
/**
265+
* @struct ChonkDecompressProof
266+
* @brief Decompress a compressed Chonk proof back to field elements
267+
*
268+
* @details Derives mega_num_public_inputs from the compressed size automatically.
269+
*/
270+
struct ChonkDecompressProof {
271+
static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkDecompressProof";
272+
273+
struct Response {
274+
static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkDecompressProofResponse";
275+
ChonkProof proof;
276+
MSGPACK_FIELDS(proof);
277+
bool operator==(const Response&) const = default;
278+
};
279+
280+
std::vector<uint8_t> compressed_proof;
281+
Response execute(const BBApiRequest& request = {}) &&;
282+
MSGPACK_FIELDS(compressed_proof);
283+
bool operator==(const ChonkDecompressProof&) const = default;
284+
};
285+
242286
} // namespace bb::bbapi

barretenberg/cpp/src/barretenberg/bbapi/bbapi_execute.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ using Command = NamedUnion<CircuitProve,
2828
CircuitWriteSolidityVerifier,
2929
ChonkCheckPrecomputedVk,
3030
ChonkStats,
31+
ChonkCompressProof,
32+
ChonkDecompressProof,
3133
Poseidon2Hash,
3234
Poseidon2Permutation,
3335
PedersenCommit,
@@ -82,6 +84,8 @@ using CommandResponse = NamedUnion<ErrorResponse,
8284
CircuitWriteSolidityVerifier::Response,
8385
ChonkCheckPrecomputedVk::Response,
8486
ChonkStats::Response,
87+
ChonkCompressProof::Response,
88+
ChonkDecompressProof::Response,
8589
Poseidon2Hash::Response,
8690
Poseidon2Permutation::Response,
8791
PedersenCommit::Response,

barretenberg/cpp/src/barretenberg/benchmark/chonk_bench/chonk.bench.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <chrono>
88

99
#include "barretenberg/chonk/chonk_verifier.hpp"
10+
#include "barretenberg/chonk/proof_compression.hpp"
1011
#include "barretenberg/chonk/test_bench_shared.hpp"
1112
#include "barretenberg/common/google_bb_bench.hpp"
1213

@@ -58,10 +59,43 @@ BENCHMARK_DEFINE_F(ChonkBench, Full)(benchmark::State& state)
5859
}
5960
}
6061

62+
/**
63+
* @brief Benchmark proof compression (prover-side cost)
64+
*/
65+
BENCHMARK_DEFINE_F(ChonkBench, ProofCompress)(benchmark::State& state)
66+
{
67+
size_t NUM_APP_CIRCUITS = 1;
68+
auto precomputed_vks = precompute_vks(NUM_APP_CIRCUITS);
69+
auto [proof, vk_and_hash] = accumulate_and_prove_with_precomputed_vks(NUM_APP_CIRCUITS, precomputed_vks);
70+
71+
for (auto _ : state) {
72+
benchmark::DoNotOptimize(ProofCompressor::compress_chonk_proof(proof));
73+
}
74+
}
75+
76+
/**
77+
* @brief Benchmark proof decompression (verifier-side cost)
78+
*/
79+
BENCHMARK_DEFINE_F(ChonkBench, ProofDecompress)(benchmark::State& state)
80+
{
81+
size_t NUM_APP_CIRCUITS = 1;
82+
auto precomputed_vks = precompute_vks(NUM_APP_CIRCUITS);
83+
auto [proof, vk_and_hash] = accumulate_and_prove_with_precomputed_vks(NUM_APP_CIRCUITS, precomputed_vks);
84+
85+
auto compressed = ProofCompressor::compress_chonk_proof(proof);
86+
size_t mega_num_pub_inputs = proof.mega_proof.size() - ChonkProof::HIDING_KERNEL_PROOF_LENGTH_WITHOUT_PUBLIC_INPUTS;
87+
88+
for (auto _ : state) {
89+
benchmark::DoNotOptimize(ProofCompressor::decompress_chonk_proof(compressed, mega_num_pub_inputs));
90+
}
91+
}
92+
6193
#define ARGS Arg(ChonkBench::NUM_ITERATIONS_MEDIUM_COMPLEXITY)->Arg(2)
6294

6395
BENCHMARK_REGISTER_F(ChonkBench, Full)->Unit(benchmark::kMillisecond)->ARGS;
6496
BENCHMARK_REGISTER_F(ChonkBench, VerificationOnly)->Unit(benchmark::kMillisecond);
97+
BENCHMARK_REGISTER_F(ChonkBench, ProofCompress)->Unit(benchmark::kMillisecond);
98+
BENCHMARK_REGISTER_F(ChonkBench, ProofDecompress)->Unit(benchmark::kMillisecond);
6599

66100
} // namespace
67101

barretenberg/cpp/src/barretenberg/chonk/chonk.test.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
#include "barretenberg/chonk/chonk.hpp"
44
#include "barretenberg/chonk/chonk_verifier.hpp"
55
#include "barretenberg/chonk/mock_circuit_producer.hpp"
6+
#include "barretenberg/chonk/proof_compression.hpp"
67
#include "barretenberg/chonk/test_bench_shared.hpp"
78
#include "barretenberg/common/assert.hpp"
9+
#include "barretenberg/common/log.hpp"
810
#include "barretenberg/common/mem.hpp"
911
#include "barretenberg/common/test.hpp"
1012
#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp"
@@ -563,3 +565,33 @@ TEST_F(ChonkTests, MTailPropagationConsistency)
563565
{
564566
ChonkTests::test_hiding_kernel_io_propagation(HidingKernelIOField::ECC_OP_TABLES);
565567
}
568+
569+
TEST_F(ChonkTests, ProofCompressionRoundtrip)
570+
{
571+
TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
572+
auto [proof, vk_and_hash] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
573+
574+
auto original_flat = proof.to_field_elements();
575+
info("Original proof size: ", original_flat.size(), " Fr elements (", original_flat.size() * 32, " bytes)");
576+
577+
auto compressed = ProofCompressor::compress_chonk_proof(proof);
578+
double ratio = static_cast<double>(original_flat.size() * 32) / static_cast<double>(compressed.size());
579+
info("Compressed proof size: ", compressed.size(), " bytes");
580+
info("Compression ratio: ", ratio, "x");
581+
582+
// Compression should achieve at least 1.5x (commitments 4 Fr → 32 bytes, scalars 1:1)
583+
EXPECT_GE(ratio, 1.5) << "Compression ratio " << ratio << "x is below the expected minimum of 1.5x";
584+
585+
size_t mega_num_pub_inputs = proof.mega_proof.size() - ChonkProof::HIDING_KERNEL_PROOF_LENGTH_WITHOUT_PUBLIC_INPUTS;
586+
ChonkProof decompressed = ProofCompressor::decompress_chonk_proof(compressed, mega_num_pub_inputs);
587+
588+
// Verify element-by-element roundtrip
589+
auto decompressed_flat = decompressed.to_field_elements();
590+
ASSERT_EQ(decompressed_flat.size(), original_flat.size());
591+
for (size_t i = 0; i < original_flat.size(); i++) {
592+
ASSERT_EQ(decompressed_flat[i], original_flat[i]) << "Mismatch at element " << i;
593+
}
594+
595+
// Verify the decompressed proof
596+
EXPECT_TRUE(verify_chonk(decompressed, vk_and_hash));
597+
}

0 commit comments

Comments
 (0)