-
Notifications
You must be signed in to change notification settings - Fork 595
Expand file tree
/
Copy pathclient_ivc.test.cpp
More file actions
407 lines (336 loc) · 14.8 KB
/
client_ivc.test.cpp
File metadata and controls
407 lines (336 loc) · 14.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
#include "barretenberg/client_ivc/client_ivc.hpp"
#include "barretenberg/client_ivc/test_bench_shared.hpp"
#include "barretenberg/goblin/goblin.hpp"
#include "barretenberg/goblin/mock_circuits.hpp"
#include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp"
#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp"
#include <gtest/gtest.h>
using namespace bb;
class ClientIVCTests : public ::testing::Test {
protected:
static void SetUpTestSuite()
{
srs::init_crs_factory("../srs_db/ignition");
srs::init_grumpkin_crs_factory("../srs_db/grumpkin");
}
using Flavor = ClientIVC::Flavor;
using FF = typename Flavor::FF;
using VerificationKey = Flavor::VerificationKey;
using Builder = ClientIVC::ClientCircuit;
using DeciderProvingKey = ClientIVC::DeciderProvingKey;
using DeciderVerificationKey = ClientIVC::DeciderVerificationKey;
using FoldProof = ClientIVC::FoldProof;
using DeciderProver = ClientIVC::DeciderProver;
using DeciderVerifier = ClientIVC::DeciderVerifier;
using DeciderProvingKeys = DeciderProvingKeys_<Flavor>;
using FoldingProver = ProtogalaxyProver_<DeciderProvingKeys>;
using DeciderVerificationKeys = DeciderVerificationKeys_<Flavor>;
using FoldingVerifier = ProtogalaxyVerifier_<DeciderVerificationKeys>;
/**
* @brief Construct mock circuit with arithmetic gates and goblin ops
* @details Defaulted to add 2^16 gates (which will bump to next power of two with the addition of dummy gates).
* The size of the baseline circuit needs to be ~2x the number of gates appended to the kernel circuits via
* recursive verifications (currently ~60k) to ensure that the circuits being folded are equal in size. (This is
* only necessary if the structured trace is not in use).
*
*/
static Builder create_mock_circuit(ClientIVC& ivc, size_t log2_num_gates = 16)
{
Builder circuit{ ivc.goblin.op_queue };
MockCircuits::construct_arithmetic_circuit(circuit, log2_num_gates);
// TODO(https://github.com/AztecProtocol/barretenberg/issues/911): We require goblin ops to be added to the
// function circuit because we cannot support zero commtiments. While the builder handles this at
// finalisation stage via the add_gates_to_ensure_all_polys_are_non_zero function for other MegaHonk
// circuits (where we don't explicitly need to add goblin ops), in ClientIVC merge proving happens prior to
// folding where the absense of goblin ecc ops will result in zero commitments.
MockCircuits::construct_goblin_ecc_op_circuit(circuit);
return circuit;
}
/**
* @brief A test utility for generating alternating mock app and kernel circuits and precomputing verification keys
*
*/
class MockCircuitProducer {
using ClientCircuit = ClientIVC::ClientCircuit;
bool is_kernel = false;
public:
ClientCircuit create_next_circuit(ClientIVC& ivc, size_t log2_num_gates = 16)
{
ClientCircuit circuit{ ivc.goblin.op_queue };
circuit = create_mock_circuit(ivc, log2_num_gates); // construct mock base logic
if (is_kernel) {
ivc.complete_kernel_circuit_logic(circuit); // complete with recursive verifiers etc
}
is_kernel = !is_kernel; // toggle is_kernel on/off alternatingly
return circuit;
}
auto precompute_verification_keys(const size_t num_circuits,
TraceSettings trace_settings,
size_t log2_num_gates = 16)
{
ClientIVC ivc{ trace_settings }; // temporary IVC instance needed to produce the complete kernel circuits
std::vector<std::shared_ptr<VerificationKey>> vkeys;
for (size_t idx = 0; idx < num_circuits; ++idx) {
ClientCircuit circuit = create_next_circuit(ivc, log2_num_gates); // create the next circuit
ivc.accumulate(circuit); // accumulate the circuit
vkeys.emplace_back(ivc.honk_vk); // save the VK for the circuit
}
is_kernel = false;
return vkeys;
}
};
/**
* @brief Tamper with a proof by finding the first non-zero value and incrementing it by 1
*
*/
static void tamper_with_proof(FoldProof& proof)
{
for (auto& val : proof) {
if (val > 0) {
val += 1;
break;
}
}
}
};
/**
* @brief A simple-as-possible test demonstrating IVC for two mock circuits
* @details When accumulating only two circuits, only a single round of folding is performed thus no recursive
* verfication occurs.
*
*/
TEST_F(ClientIVCTests, Basic)
{
ClientIVC ivc;
MockCircuitProducer circuit_producer;
// Initialize the IVC with an arbitrary circuit
Builder circuit_0 = circuit_producer.create_next_circuit(ivc);
ivc.accumulate(circuit_0);
// Create another circuit and accumulate
Builder circuit_1 = circuit_producer.create_next_circuit(ivc);
ivc.accumulate(circuit_1);
EXPECT_TRUE(ivc.prove_and_verify());
};
/**
* @brief A simple test demonstrating IVC for four mock circuits, which is slightly more than minimal.
* @details When accumulating only four circuits, we execute all the functionality of a full ClientIVC run.
*
*/
TEST_F(ClientIVCTests, BasicFour)
{
ClientIVC ivc;
MockCircuitProducer circuit_producer;
for (size_t idx = 0; idx < 4; ++idx) {
Builder circuit = circuit_producer.create_next_circuit(ivc);
ivc.accumulate(circuit);
}
EXPECT_TRUE(ivc.prove_and_verify());
};
/**
* @brief Check that the IVC fails if an intermediate fold proof is invalid
* @details When accumulating 4 circuits, there are 3 fold proofs to verify (the first two are recursively verfied and
* the 3rd is verified as part of the IVC proof). Check that if any of one of these proofs is invalid, the IVC will
* fail.
*
*/
TEST_F(ClientIVCTests, BadProofFailure)
{
// Confirm that the IVC verifies if nothing is tampered with
{
ClientIVC ivc{ { SMALL_TEST_STRUCTURE } };
MockCircuitProducer circuit_producer;
// Construct and accumulate a set of mocked private function execution circuits
size_t NUM_CIRCUITS = 4;
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5);
ivc.accumulate(circuit);
}
EXPECT_TRUE(ivc.prove_and_verify());
}
// The IVC throws an exception if the FIRST fold proof is tampered with
{
ClientIVC ivc{ { SMALL_TEST_STRUCTURE } };
MockCircuitProducer circuit_producer;
// Construct and accumulate a set of mocked private function execution circuits
size_t NUM_CIRCUITS = 4;
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
if (idx == 3) { // At idx = 3, we've tampered with the one of the folding proofs so create the recursive
// folding verifier will throw an error.
EXPECT_ANY_THROW(circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5));
break;
}
auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5);
ivc.accumulate(circuit);
if (idx == 2) {
EXPECT_EQ(ivc.verification_queue.size(), 2); // two proofs after 3 calls to accumulation
tamper_with_proof(ivc.verification_queue[0].proof); // tamper with first proof
}
}
}
// The IVC fails if the SECOND fold proof is tampered with
{
ClientIVC ivc{ { SMALL_TEST_STRUCTURE } };
MockCircuitProducer circuit_producer;
// Construct and accumulate a set of mocked private function execution circuits
size_t NUM_CIRCUITS = 4;
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
if (idx == 3) { // At idx = 3, we've tampered with the one of the folding proofs so create the recursive
// folding verifier will throw an error.
EXPECT_ANY_THROW(circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5));
break;
}
auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5);
ivc.accumulate(circuit);
if (idx == 2) {
EXPECT_EQ(ivc.verification_queue.size(), 2); // two proofs after 3 calls to accumulation
tamper_with_proof(ivc.verification_queue[1].proof); // tamper with second proof
}
}
}
// The IVC fails if the 3rd/FINAL fold proof is tampered with
{
ClientIVC ivc{ { SMALL_TEST_STRUCTURE } };
MockCircuitProducer circuit_producer;
// Construct and accumulate a set of mocked private function execution circuits
size_t NUM_CIRCUITS = 4;
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5);
ivc.accumulate(circuit);
}
// Only a single proof should be present in the queue when verification of the IVC is performed
EXPECT_EQ(ivc.verification_queue.size(), 1);
tamper_with_proof(ivc.verification_queue[0].proof); // tamper with the final fold proof
EXPECT_ANY_THROW(ivc.prove_and_verify());
}
EXPECT_TRUE(true);
};
/**
* @brief Prove and verify accumulation of an arbitrary set of circuits
*
*/
TEST_F(ClientIVCTests, BasicLarge)
{
ClientIVC ivc;
MockCircuitProducer circuit_producer;
// Construct and accumulate a set of mocked private function execution circuits
size_t NUM_CIRCUITS = 6;
std::vector<Builder> circuits;
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
auto circuit = circuit_producer.create_next_circuit(ivc);
ivc.accumulate(circuit);
}
EXPECT_TRUE(ivc.prove_and_verify());
};
/**
* @brief Using a structured trace allows for the accumulation of circuits of varying size
*
*/
TEST_F(ClientIVCTests, BasicStructured)
{
ClientIVC ivc{ { SMALL_TEST_STRUCTURE } };
MockCircuitProducer circuit_producer;
size_t NUM_CIRCUITS = 4;
// Construct and accumulate some circuits of varying size
size_t log2_num_gates = 5;
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates);
ivc.accumulate(circuit);
log2_num_gates += 2;
}
EXPECT_TRUE(ivc.prove_and_verify());
};
/**
* @brief Prove and verify accumulation of an arbitrary set of circuits using precomputed verification keys
*
*/
TEST_F(ClientIVCTests, PrecomputedVerificationKeys)
{
ClientIVC ivc;
size_t NUM_CIRCUITS = 4;
MockCircuitProducer circuit_producer;
auto precomputed_vks = circuit_producer.precompute_verification_keys(NUM_CIRCUITS, TraceSettings{});
// Construct and accumulate set of circuits using the precomputed vkeys
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
auto circuit = circuit_producer.create_next_circuit(ivc);
ivc.accumulate(circuit, precomputed_vks[idx]);
}
EXPECT_TRUE(ivc.prove_and_verify());
};
/**
* @brief Perform accumulation with a structured trace and precomputed verification keys
*
*/
TEST_F(ClientIVCTests, StructuredPrecomputedVKs)
{
ClientIVC ivc{ { SMALL_TEST_STRUCTURE } };
size_t NUM_CIRCUITS = 4;
size_t log2_num_gates = 5; // number of gates in baseline mocked circuit
MockCircuitProducer circuit_producer;
auto precomputed_vks =
circuit_producer.precompute_verification_keys(NUM_CIRCUITS, ivc.trace_settings, log2_num_gates);
// Construct and accumulate set of circuits using the precomputed vkeys
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates);
ivc.accumulate(circuit, precomputed_vks[idx]);
}
EXPECT_TRUE(ivc.prove_and_verify());
};
/**
* @brief Run a test using functions shared with the ClientIVC benchmark.
* @details We do have this in addition to the above tests anyway so we can believe that the benchmark is running on
* real data EXCEPT the verification keys, whose correctness is not needed to assess the performance of the folding
* prover. Before this test was added, we spend more than 50% of the benchmarking time running an entire IVC prover
* protocol just to precompute valid verification keys.
*/
TEST(ClientIVCBenchValidation, Full6)
{
bb::srs::init_crs_factory("../srs_db/ignition");
bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin");
ClientIVC ivc{ { CLIENT_IVC_BENCH_STRUCTURE } };
size_t total_num_circuits{ 12 };
PrivateFunctionExecutionMockCircuitProducer circuit_producer;
auto precomputed_vkeys = circuit_producer.precompute_verification_keys(total_num_circuits, ivc.trace_settings);
perform_ivc_accumulation_rounds(total_num_circuits, ivc, precomputed_vkeys);
auto proof = ivc.prove();
bool verified = verify_ivc(proof, ivc);
EXPECT_TRUE(verified);
}
/**
* @brief Test that running the benchmark suite with movked verification keys will not error out.
*/
TEST(ClientIVCBenchValidation, Full6MockedVKs)
{
const auto run_test = []() {
bb::srs::init_crs_factory("../srs_db/ignition");
bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin");
ClientIVC ivc{ { CLIENT_IVC_BENCH_STRUCTURE } };
size_t total_num_circuits{ 12 };
PrivateFunctionExecutionMockCircuitProducer circuit_producer;
auto mocked_vkeys = mock_verification_keys(total_num_circuits);
perform_ivc_accumulation_rounds(total_num_circuits, ivc, mocked_vkeys, /* mock_vk */ true);
auto proof = ivc.prove();
verify_ivc(proof, ivc);
};
ASSERT_NO_FATAL_FAILURE(run_test());
}
/**
* @brief Test use of structured trace overflow block mechanism
* @details Accumulate 4 circuits which have progressively more arithmetic gates. The final two overflow the prescribed
* arithmetic block size and make use of the overflow block which has sufficient capacity.
*
*/
TEST_F(ClientIVCTests, StructuredTraceOverflow)
{
// Define trace settings with sufficient overflow capacity to accommodate each of the circuits to be accumulated
ClientIVC ivc{ { SMALL_TEST_STRUCTURE, /*overflow_capacity=*/1 << 17 } };
MockCircuitProducer circuit_producer;
size_t NUM_CIRCUITS = 4;
// Construct and accumulate some circuits of varying size
size_t log2_num_gates = 14;
for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates);
ivc.accumulate(circuit);
log2_num_gates += 1;
}
EXPECT_TRUE(ivc.prove_and_verify());
};