Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
client_ivc.test.cpp
Go to the documentation of this file.
14#include "gtest/gtest.h"
15
16using namespace bb;
17
18static constexpr size_t SMALL_LOG_2_NUM_GATES = 5;
19// TODO(https://github.com/AztecProtocol/barretenberg/issues/1511): The CIVC class should enforce the minimum number of
20// circuits in a test flow.
21
22class ClientIVCTests : public ::testing::Test {
23 protected:
25
27 using FF = typename Flavor::FF;
38 using CircuitProducer = PrivateFunctionExecutionMockCircuitProducer;
39
40 public:
47 static void tamper_with_proof(FoldProof& proof, size_t public_inputs_offset)
48 {
49 // Tamper with the commitment in the proof
50 Commitment commitment = FrCodec::deserialize_from_fields<Commitment>(
51 std::span{ proof }.subspan(public_inputs_offset, FrCodec::template calc_num_fields<Commitment>()));
52 commitment = commitment + Commitment::one();
53 auto commitment_frs = FrCodec::serialize_to_fields<Commitment>(commitment);
54 for (size_t idx = 0; idx < 4; ++idx) {
55 proof[public_inputs_offset + idx] = commitment_frs[idx];
56 }
57 }
58
60 TestSettings settings = {})
61 {
62 CircuitProducer circuit_producer(num_app_circuits);
63 const size_t num_circuits = circuit_producer.total_num_circuits;
64 TraceSettings trace_settings{ AZTEC_TRACE_STRUCTURE };
65 ClientIVC ivc{ num_circuits, trace_settings };
66
67 for (size_t j = 0; j < num_circuits; ++j) {
68 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
69 }
70 return { ivc.prove(), ivc.get_vk() };
71 };
72};
73
78TEST_F(ClientIVCTests, BasicStructured)
79{
80 const size_t NUM_APP_CIRCUITS = 1;
81 auto [proof, vk] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS);
82
83 EXPECT_TRUE(ClientIVC::verify(proof, vk));
84};
85
93TEST_F(ClientIVCTests, BadProofFailure)
94{
95 BB_DISABLE_ASSERTS(); // Disable assert in PG prover
96
97 const size_t NUM_APP_CIRCUITS = 2;
98 TraceSettings trace_settings{ SMALL_TEST_STRUCTURE };
99 // Confirm that the IVC verifies if nothing is tampered with
100 {
101
102 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
103 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
104 ClientIVC ivc{ NUM_CIRCUITS, trace_settings };
105 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
106
107 // Construct and accumulate a set of mocked private function execution circuits
108 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
109 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
110 }
111 auto proof = ivc.prove();
112 EXPECT_TRUE(ClientIVC::verify(proof, ivc.get_vk()));
113 }
114
115 // The IVC throws an exception if the FIRST fold proof is tampered with
116 {
117 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
118 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
119 ClientIVC ivc{ NUM_CIRCUITS, trace_settings };
120
121 size_t num_public_inputs = 0;
122
123 // Construct and accumulate a set of mocked private function execution circuits
124 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
125 auto [circuit, vk] =
126 circuit_producer.create_next_circuit_and_vk(ivc, { .log2_num_gates = SMALL_LOG_2_NUM_GATES });
127 ivc.accumulate(circuit, vk);
128
129 if (idx == 1) {
130 num_public_inputs = circuit.num_public_inputs();
131 }
132
133 if (idx == 2) {
134 EXPECT_EQ(ivc.verification_queue.size(), 2); // two proofs after 3 calls to accumulation
135 tamper_with_proof(ivc.verification_queue[0].proof,
136 num_public_inputs); // tamper with first proof
137 }
138 }
139 auto proof = ivc.prove();
140 EXPECT_FALSE(ClientIVC::verify(proof, ivc.get_vk()));
141 }
142
143 // The IVC fails if the SECOND fold proof is tampered with
144 {
145 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
146 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
147 ClientIVC ivc{ NUM_CIRCUITS, trace_settings };
148
149 // Construct and accumulate a set of mocked private function execution circuits
150 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
151 auto [circuit, vk] =
152 circuit_producer.create_next_circuit_and_vk(ivc, { .log2_num_gates = SMALL_LOG_2_NUM_GATES });
153 ivc.accumulate(circuit, vk);
154
155 if (idx == 2) {
156 EXPECT_EQ(ivc.verification_queue.size(), 2); // two proofs after 3 calls to accumulation
157 tamper_with_proof(ivc.verification_queue[1].proof,
158 circuit.num_public_inputs()); // tamper with second proof
159 }
160 }
161 auto proof = ivc.prove();
162 EXPECT_FALSE(ClientIVC::verify(proof, ivc.get_vk()));
163 }
164
165 EXPECT_TRUE(true);
166};
167
173TEST_F(ClientIVCTests, WrongProofComponentFailure)
174{
175 // Produce two valid proofs
176 auto [civc_proof_1, civc_vk_1] = accumulate_and_prove_ivc(/*num_app_circuits=*/1);
177 {
178 EXPECT_TRUE(ClientIVC::verify(civc_proof_1, civc_vk_1));
179 }
180
181 auto [civc_proof_2, civc_vk_2] = accumulate_and_prove_ivc(/*num_app_circuits=*/1);
182 {
183 EXPECT_TRUE(ClientIVC::verify(civc_proof_2, civc_vk_2));
184 }
185
186 {
187 // Replace Merge proof
188 ClientIVC::Proof tampered_proof = civc_proof_1;
189
190 tampered_proof.goblin_proof.merge_proof = civc_proof_2.goblin_proof.merge_proof;
191
192 EXPECT_THROW_OR_ABORT(ClientIVC::verify(tampered_proof, civc_vk_1), ".*IPA verification fails.*");
193 }
194
195 {
196 // Replace hiding circuit proof
197 ClientIVC::Proof tampered_proof = civc_proof_1;
198
199 tampered_proof.mega_proof = civc_proof_2.mega_proof;
200
201 EXPECT_THROW_OR_ABORT(ClientIVC::verify(tampered_proof, civc_vk_1), ".*IPA verification fails.*");
202 }
203
204 {
205 // Replace ECCVM proof
206 ClientIVC::Proof tampered_proof = civc_proof_1;
207
208 tampered_proof.goblin_proof.eccvm_proof = civc_proof_2.goblin_proof.eccvm_proof;
209
210 EXPECT_THROW_OR_ABORT(ClientIVC::verify(tampered_proof, civc_vk_1), ".*IPA verification fails.*");
211 }
212
213 {
214 // Replace Translator proof
215 ClientIVC::Proof tampered_proof = civc_proof_1;
216
217 tampered_proof.goblin_proof.translator_proof = civc_proof_2.goblin_proof.translator_proof;
218
219 EXPECT_FALSE(ClientIVC::verify(tampered_proof, civc_vk_1));
220 }
221};
222
227TEST_F(ClientIVCTests, VKIndependenceTest)
228{
229 const TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
230
231 auto [unused_1, civc_vk_1] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
232 auto [unused_2, civc_vk_2] = accumulate_and_prove_ivc(/*num_app_circuits=*/3, settings);
233
234 // Check the equality of the Mega components of the ClientIVC VKeys.
235 EXPECT_EQ(*civc_vk_1.mega.get(), *civc_vk_2.mega.get());
236
237 // Check the equality of the ECCVM components of the ClientIVC VKeys.
238 EXPECT_EQ(*civc_vk_1.eccvm.get(), *civc_vk_2.eccvm.get());
239
240 // Check the equality of the Translator components of the ClientIVC VKeys.
241 EXPECT_EQ(*civc_vk_1.translator.get(), *civc_vk_2.translator.get());
242};
243
252TEST_F(ClientIVCTests, VKIndependenceWithOverflow)
253{
254 // Run IVC for two sets of circuits: a nomical case where all circuits fit within the structured trace and an
255 // "overflow" case where all (but importantly at least one) circuit overflows the structured trace.
256 const size_t NUM_APP_CIRCUITS = 1;
257 const size_t log2_num_gates_nominal = 5; // number of gates in baseline mocked circuits
258 const size_t log2_num_gates_overflow = 18; // number of gates in the "overflow" mocked circuit
259
260 const TestSettings settings_1{ .log2_num_gates = log2_num_gates_nominal };
261 const TestSettings settings_2{ .log2_num_gates = log2_num_gates_overflow };
262
263 auto [unused_1, civc_vk_1] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, settings_1);
264 auto [unused_2, civc_vk_2] = accumulate_and_prove_ivc(NUM_APP_CIRCUITS, settings_2);
265
266 // Check the equality of the Mega components of the ClientIVC VKeys.
267 EXPECT_EQ(*civc_vk_1.mega.get(), *civc_vk_2.mega.get());
268
269 // Check the equality of the ECCVM components of the ClientIVC VKeys.
270 EXPECT_EQ(*civc_vk_1.eccvm.get(), *civc_vk_2.eccvm.get());
271
272 // Check the equality of the Translator components of the ClientIVC VKeys.
273 EXPECT_EQ(*civc_vk_1.translator.get(), *civc_vk_2.translator.get());
274};
275
280HEAVY_TEST(ClientIVCKernelCapacity, MaxCapacityPassing)
281{
283
284 const size_t NUM_APP_CIRCUITS = 14;
285 auto [proof, vk] = ClientIVCTests::accumulate_and_prove_ivc(NUM_APP_CIRCUITS);
286
287 bool verified = ClientIVC::verify(proof, vk);
288 EXPECT_TRUE(verified);
289};
290
297TEST_F(ClientIVCTests, StructuredTraceOverflow)
298{
299
300 // Define trace settings with sufficient overflow capacity to accommodate each of the circuits to be accumulated
301 size_t NUM_APP_CIRCUITS = 1;
302 CircuitProducer circuit_producer(NUM_APP_CIRCUITS);
303 size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
304 ClientIVC ivc{ NUM_CIRCUITS, { SMALL_TEST_STRUCTURE, /*overflow_capacity=*/1 << 17 } };
305 TestSettings settings;
306
307 // Construct and accumulate some circuits of varying size
308 size_t log2_num_gates = 14;
309 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
310 settings.log2_num_gates = log2_num_gates;
311 circuit_producer.construct_and_accumulate_next_circuit(ivc, settings);
312 log2_num_gates += 1;
313 }
314
315 auto proof = ivc.prove();
316 EXPECT_TRUE(ClientIVC::verify(proof, ivc.get_vk()));
317};
318
323TEST_F(ClientIVCTests, DynamicTraceOverflow)
324{
325 struct TestCase {
326 std::string name;
327 std::vector<size_t> log2_num_arith_gates;
328 };
329
330 // Define some test cases that overflow the structured trace in different ways at different points in the
331 // accumulation. We distinguish between a simple overflow that exceeds one or more structured trace capacities but
332 // does not bump the dyadic circuit size and an overflow that does increase the dyadic circuit size.
333 std::vector<TestCase> test_cases = {
334 { "Case 1", { 14, 18, 14, 16, 14 } }, /* dyadic size overflow then simple overflow */
335 { "Case 2", { 14, 16, 14, 18, 14 } }, /* simple overflow then dyadic size overflow */
336 };
337
338 for (const auto& test : test_cases) {
339 SCOPED_TRACE(test.name); // improves test output readability
340
341 CircuitProducer circuit_producer(/*num_app_circuits=*/1);
342 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
343 ClientIVC ivc{ NUM_CIRCUITS, { SMALL_TEST_STRUCTURE_FOR_OVERFLOWS } };
344
345 // Accumulate
346 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
347 circuit_producer.construct_and_accumulate_next_circuit(
348 ivc, { .log2_num_gates = test.log2_num_arith_gates[idx] });
349 }
350
351 EXPECT_EQ(check_accumulator_target_sum_manual(ivc.prover_accumulator), true);
352 auto proof = ivc.prove();
353 EXPECT_TRUE(ClientIVC::verify(proof, ivc.get_vk()));
354 }
355}
356
361TEST_F(ClientIVCTests, MsgpackProofFromFileOrBuffer)
362{
363 // Generate an arbitrary valid CICV proof
364 TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
365 auto [proof, vk] = accumulate_and_prove_ivc(/*num_app_circuits=*/1, settings);
366
367 { // Serialize/deserialize the proof to/from a file, check that it verifies
368 const std::string filename = "proof.msgpack";
369 proof.to_file_msgpack(filename);
370 auto proof_deserialized = ClientIVC::Proof::from_file_msgpack(filename);
371
372 EXPECT_TRUE(ClientIVC::verify(proof_deserialized, vk));
373 }
374
375 { // Serialize/deserialize proof to/from a heap buffer, check that it verifies
376 uint8_t* buffer = proof.to_msgpack_heap_buffer();
377 auto uint8_buffer = from_buffer<std::vector<uint8_t>>(buffer);
378 uint8_t const* uint8_ptr = uint8_buffer.data();
379 auto proof_deserialized = ClientIVC::Proof::from_msgpack_buffer(uint8_ptr);
380
381 EXPECT_TRUE(ClientIVC::verify(proof_deserialized, vk));
382 }
383
384 { // Check that attempting to deserialize a proof from a buffer with random bytes fails gracefully
385 msgpack::sbuffer buffer = proof.to_msgpack_buffer();
386 auto proof_deserialized = ClientIVC::Proof::from_msgpack_buffer(buffer);
387 EXPECT_TRUE(ClientIVC::verify(proof_deserialized, vk));
388
389 std::vector<uint8_t> random_bytes(buffer.size());
390 std::generate(random_bytes.begin(), random_bytes.end(), []() { return static_cast<uint8_t>(rand() % 256); });
391 std::copy(random_bytes.begin(), random_bytes.end(), buffer.data());
392
393 // Expect deserialization to fail with error msgpack::v1::type_error with description "std::bad_cast"
394 EXPECT_THROW(ClientIVC::Proof::from_msgpack_buffer(buffer), msgpack::v1::type_error);
395 }
396};
397
407TEST_F(ClientIVCTests, DatabusFailure)
408{
409 BB_DISABLE_ASSERTS(); // Disable assert in PG prover
410
411 PrivateFunctionExecutionMockCircuitProducer circuit_producer{ /*num_app_circuits=*/1 };
412 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
413 ClientIVC ivc{ NUM_CIRCUITS, { AZTEC_TRACE_STRUCTURE } };
414
415 // Construct and accumulate a series of mocked private function execution circuits
416 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
417 auto [circuit, vk] = circuit_producer.create_next_circuit_and_vk(ivc);
418
419 // Tamper with the return data of the app circuit before it is processed as input to the next kernel
420 if (idx == 0) {
421 circuit_producer.tamper_with_databus();
422 }
423
424 ivc.accumulate(circuit, vk);
425 }
426
427 auto proof = ivc.prove();
428 EXPECT_FALSE(ClientIVC::verify(proof, ivc.get_vk()));
429};
#define EXPECT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:185
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:32
static std::pair< ClientIVC::Proof, ClientIVC::VerificationKey > accumulate_and_prove_ivc(size_t num_app_circuits, TestSettings settings={})
static void SetUpTestSuite()
PrivateFunctionExecutionMockCircuitProducer CircuitProducer
static void tamper_with_proof(FoldProof &proof, size_t public_inputs_offset)
Tamper with a proof.
typename Flavor::FF FF
Flavor::Commitment Commitment
ClientIVC::FoldProof FoldProof
The IVC scheme used by the aztec client for private function execution.
Proof prove()
Construct a proof for the IVC, which, if verified, fully establishes its correctness.
MegaFlavor Flavor
DeciderVerifier_< Flavor > DeciderVerifier
void accumulate(ClientCircuit &circuit, const std::shared_ptr< MegaVerificationKey > &precomputed_vk) override
Perform prover work for accumulation (e.g. PG folding, merge proving)
VerifierInstance_< Flavor > VerifierInstance
DeciderProver_< Flavor > DeciderProver
std::vector< FF > FoldProof
MegaCircuitBuilder ClientCircuit
static bool verify(const Proof &proof, const VerificationKey &vk)
ProverInstance_< Flavor > ProverInstance
The verification key is responsible for storing the commitments to the precomputed (non-witness) poly...
Curve::ScalarField FF
Curve::AffineElement Commitment
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
The VerifierInstance encapsulates all the necessary information for a Mega Honk Verifier to verify a ...
TEST_F(ClientIVCTests, BasicStructured)
Using a structured trace allows for the accumulation of circuits of varying size.
uint8_t buffer[RANDOM_BUFFER_SIZE]
Definition engine.cpp:34
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
::testing::Types< BN254Settings, GrumpkinSettings > TestSettings
void tamper_with_proof(InnerProver &inner_prover, ProofType &inner_proof, TamperType type)
Test method that provides several ways to tamper with a proof. TODO(https://github....
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
A full proof for the IVC scheme containing a Mega proof showing correctness of the hiding circuit (wh...
static Proof from_msgpack_buffer(uint8_t const *&buffer)
static Proof from_file_msgpack(const std::string &filename)
GoblinProof goblin_proof
ECCVMProof eccvm_proof
Definition types.hpp:22
HonkProof merge_proof
Definition types.hpp:21
HonkProof translator_proof
Definition types.hpp:23
#define HEAVY_TEST(x, y)
Definition test.hpp:9