Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
protogalaxy_recursive_verifier.test.cpp
Go to the documentation of this file.
13
15
17class ProtogalaxyRecursiveTests : public testing::Test {
18 public:
19 // Define types for the inner circuit, i.e. the circuit whose proof will be recursively verified
31
32 // Defines types for the outer circuit, i.e. the circuit of the recursive verifier
38
51
63 static void create_function_circuit(InnerBuilder& builder, size_t log_num_gates = 10)
64 {
65 using fr_ct = typename InnerCurve::ScalarField;
68 using witness_ct = typename InnerCurve::witness_ct;
70 using fr = typename InnerCurve::ScalarFieldNative;
71
72 // Create 2^log_n many add gates based on input log num gates
73 const size_t num_gates = 1 << log_num_gates;
74 for (size_t i = 0; i < num_gates; ++i) {
76 uint32_t a_idx = builder.add_variable(a);
77
80 fr d = a + b + c;
81 uint32_t b_idx = builder.add_variable(b);
82 uint32_t c_idx = builder.add_variable(c);
83 uint32_t d_idx = builder.add_variable(d);
84
85 builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
86 }
87
88 // Define some additional non-trivial but arbitrary circuit logic
92
93 for (size_t i = 0; i < 32; ++i) {
94 a = (a * b) + b + a;
95 a = a.madd(b, c);
96 }
98 byte_array_ct to_hash(&builder, "nonsense test data");
100
101 fr bigfield_data = fr::random_element(&engine);
102 fr bigfield_data_a{ bigfield_data.data[0], bigfield_data.data[1], 0, 0 };
103 fr bigfield_data_b{ bigfield_data.data[2], bigfield_data.data[3], 0, 0 };
104
105 fq_ct big_a(fr_ct(witness_ct(&builder, bigfield_data_a.to_montgomery_form())), fr_ct(witness_ct(&builder, 0)));
106 fq_ct big_b(fr_ct(witness_ct(&builder, bigfield_data_b.to_montgomery_form())), fr_ct(witness_ct(&builder, 0)));
107
108 big_a* big_b;
109
111 };
112
115 {
116 InnerBuilder builder1;
117 create_function_circuit(builder1);
118 InnerBuilder builder2;
119 builder2.add_public_variable(FF(1));
120 create_function_circuit(builder2);
121
122 auto decider_pk_1 = std::make_shared<InnerDeciderProvingKey>(builder1);
123 auto honk_vk_1 = std::make_shared<InnerVerificationKey>(decider_pk_1->get_precomputed());
124 auto decider_vk_1 = std::make_shared<InnerDeciderVerificationKey>(honk_vk_1);
125 auto decider_pk_2 = std::make_shared<InnerDeciderProvingKey>(builder2);
126 auto honk_vk_2 = std::make_shared<InnerVerificationKey>(decider_pk_2->get_precomputed());
127 auto decider_vk_2 = std::make_shared<InnerDeciderVerificationKey>(honk_vk_2);
128 InnerFoldingProver folding_prover({ decider_pk_1, decider_pk_2 },
129 { decider_vk_1, decider_vk_2 },
131 InnerFoldingVerifier folding_verifier({ decider_vk_1, decider_vk_2 },
133
134 auto [prover_accumulator, folding_proof] = folding_prover.prove();
135 auto verifier_accumulator = folding_verifier.verify_folding_proof(folding_proof);
136 return { prover_accumulator, verifier_accumulator };
137 }
138
142 static void test_circuit()
143 {
145
147
148 bool result = CircuitChecker::check(builder);
149 EXPECT_EQ(result, true);
150 };
151
157 static void test_new_evaluate()
158 {
162
163 std::vector<fr> coeffs;
164 std::vector<fr_ct> coeffs_ct;
165 for (size_t idx = 0; idx < 8; idx++) {
166 auto el = fr::random_element(&engine);
167 coeffs.emplace_back(el);
168 coeffs_ct.emplace_back(fr_ct(&builder, el));
169 }
170 Polynomial<fr> poly(coeffs);
171 fr point = fr::random_element(&engine);
172 fr_ct point_ct(fr_ct(&builder, point));
173 auto res1 = poly.evaluate(point);
174
175 auto res2 = evaluate_perturbator(coeffs_ct, point_ct);
176 EXPECT_EQ(res1, res2.get_value());
177 };
178
183 static void test_recursive_folding(const size_t num_verifiers = 1)
184 {
185 // Create two arbitrary circuits for the first round of folding
186 InnerBuilder builder1;
187 create_function_circuit(builder1);
188 InnerBuilder builder2;
189 builder2.add_public_variable(FF(1));
190 create_function_circuit(builder2);
191
192 auto decider_pk_1 = std::make_shared<InnerDeciderProvingKey>(builder1);
193 auto honk_vk_1 = std::make_shared<InnerVerificationKey>(decider_pk_1->get_precomputed());
194 auto decider_vk_1 = std::make_shared<InnerDeciderVerificationKey>(honk_vk_1);
195 auto decider_pk_2 = std::make_shared<InnerDeciderProvingKey>(builder2);
196 auto honk_vk_2 = std::make_shared<InnerVerificationKey>(decider_pk_2->get_precomputed());
197 auto decider_vk_2 = std::make_shared<InnerDeciderVerificationKey>(honk_vk_2);
198 // Generate a folding proof
199 InnerFoldingProver folding_prover({ decider_pk_1, decider_pk_2 },
200 { decider_vk_1, decider_vk_2 },
202 auto folding_proof = folding_prover.prove();
203
204 // Create a folding verifier circuit
205 OuterBuilder folding_circuit;
206
207 auto recursive_decider_vk_1 = std::make_shared<RecursiveDeciderVerificationKey>(&folding_circuit, decider_vk_1);
208 auto recursive_vk_and_hash_2 = std::make_shared<RecursiveVKAndHash>(folding_circuit, decider_vk_2->vk);
209 stdlib::Proof<OuterBuilder> stdlib_proof(folding_circuit, folding_proof.proof);
210
211 auto verifier = FoldingRecursiveVerifier{ &folding_circuit,
212 recursive_decider_vk_1,
213 { recursive_vk_and_hash_2 },
216 for (size_t idx = 0; idx < num_verifiers; idx++) {
217 verifier.transcript->enable_manifest();
218 accumulator = verifier.verify_folding_proof(stdlib_proof);
219 if (idx < num_verifiers - 1) { // else the transcript is null in the test below
220 auto recursive_vk_and_hash = std::make_shared<RecursiveVKAndHash>(folding_circuit, decider_vk_1->vk);
221 verifier =
222 FoldingRecursiveVerifier{ &folding_circuit,
223 accumulator,
224 { recursive_vk_and_hash },
226 }
227 }
228 info("Folding Recursive Verifier: num gates unfinalized = ", folding_circuit.num_gates);
229 EXPECT_EQ(folding_circuit.failed(), false) << folding_circuit.err();
230
231 // Perform native folding verification and ensure it returns the same result (either true or false) as
232 // calling check_circuit on the recursive folding verifier
233 InnerFoldingVerifier native_folding_verifier({ decider_vk_1, decider_vk_2 },
235 native_folding_verifier.transcript->enable_manifest();
237 for (size_t idx = 0; idx < num_verifiers; idx++) {
238 native_accumulator = native_folding_verifier.verify_folding_proof(folding_proof.proof);
239 if (idx < num_verifiers - 1) { // else the transcript is null in the test below
240 native_folding_verifier =
241 InnerFoldingVerifier{ { native_accumulator, decider_vk_1 },
243 native_folding_verifier.transcript->enable_manifest();
244 }
245 }
246
247 // Ensure that the underlying native and recursive folding verification algorithms agree by ensuring the
248 // manifests produced by each agree.
249 auto recursive_folding_manifest = verifier.transcript->get_manifest();
250 auto native_folding_manifest = native_folding_verifier.transcript->get_manifest();
251
252 ASSERT_GT(recursive_folding_manifest.size(), 0);
253 for (size_t i = 0; i < recursive_folding_manifest.size(); ++i) {
254 EXPECT_EQ(recursive_folding_manifest[i], native_folding_manifest[i])
255 << "Recursive Verifier/Verifier manifest discrepency in round " << i;
256 }
257
258 // Check for a failure flag in the recursive verifier circuit
259 {
261 // inefficiently check finalized size
262 folding_circuit.finalize_circuit(/* ensure_nonzero= */ true);
263 info("Folding Recursive Verifier: num gates finalized = ", folding_circuit.num_gates);
264 auto decider_pk = std::make_shared<OuterDeciderProvingKey>(folding_circuit);
265 info("Dyadic size of verifier circuit: ", decider_pk->dyadic_size());
266 auto honk_vk = std::make_shared<typename OuterFlavor::VerificationKey>(decider_pk->get_precomputed());
267 OuterProver prover(decider_pk, honk_vk);
268 OuterVerifier verifier(honk_vk);
269 auto proof = prover.construct_proof();
270 bool verified = verifier.template verify_proof<bb::DefaultIO>(proof).result;
271
272 ASSERT_TRUE(verified);
273 }
274 };
275
282 {
283 using NativeVerifierCommitmentKey = typename InnerFlavor::VerifierCommitmentKey;
284 // Create two arbitrary circuits for the first round of folding
285 InnerBuilder builder1;
286 create_function_circuit(builder1);
287 InnerBuilder builder2;
288 builder2.add_public_variable(FF(1));
289
290 create_function_circuit(builder2);
291
292 auto decider_pk_1 = std::make_shared<InnerDeciderProvingKey>(builder1);
293 auto honk_vk_1 = std::make_shared<InnerVerificationKey>(decider_pk_1->get_precomputed());
294 auto decider_vk_1 = std::make_shared<InnerDeciderVerificationKey>(honk_vk_1);
295 auto decider_pk_2 = std::make_shared<InnerDeciderProvingKey>(builder2);
296 auto honk_vk_2 = std::make_shared<InnerVerificationKey>(decider_pk_2->get_precomputed());
297 auto decider_vk_2 = std::make_shared<InnerDeciderVerificationKey>(honk_vk_2);
298 // Generate a folding proof
299 InnerFoldingProver folding_prover({ decider_pk_1, decider_pk_2 },
300 { decider_vk_1, decider_vk_2 },
302 auto folding_proof = folding_prover.prove();
303
304 // Create a folding verifier circuit
305 OuterBuilder folding_circuit;
306 auto recursive_decider_vk_1 = std::make_shared<RecursiveDeciderVerificationKey>(&folding_circuit, decider_vk_1);
307 auto recursive_vk_and_hash_2 = std::make_shared<RecursiveVKAndHash>(folding_circuit, decider_vk_2->vk);
308 stdlib::Proof<OuterBuilder> stdlib_proof(folding_circuit, folding_proof.proof);
309
310 auto verifier = FoldingRecursiveVerifier{ &folding_circuit,
311 recursive_decider_vk_1,
312 { recursive_vk_and_hash_2 },
314 verifier.transcript->enable_manifest();
315 auto recursive_verifier_native_accum = verifier.verify_folding_proof(stdlib_proof);
316 auto native_verifier_acc =
317 std::make_shared<InnerDeciderVerificationKey>(recursive_verifier_native_accum->get_value());
318 info("Folding Recursive Verifier: num gates = ", folding_circuit.get_estimated_num_finalized_gates());
319
320 // Check for a failure flag in the recursive verifier circuit
321 EXPECT_EQ(folding_circuit.failed(), false) << folding_circuit.err();
322
323 // Perform native folding verification and ensure it returns the same result (either true or false) as
324 // calling check_circuit on the recursive folding verifier
325 InnerFoldingVerifier native_folding_verifier({ decider_vk_1, decider_vk_2 },
327 native_folding_verifier.transcript->enable_manifest();
328 auto verifier_accumulator = native_folding_verifier.verify_folding_proof(folding_proof.proof);
329
330 // Ensure that the underlying native and recursive folding verification algorithms agree by ensuring the
331 // manifests produced by each agree.
332 auto recursive_folding_manifest = verifier.transcript->get_manifest();
333 auto native_folding_manifest = native_folding_verifier.transcript->get_manifest();
334
335 ASSERT_GT(recursive_folding_manifest.size(), 0);
336 for (size_t i = 0; i < recursive_folding_manifest.size(); ++i) {
337 EXPECT_EQ(recursive_folding_manifest[i], native_folding_manifest[i])
338 << "Recursive Verifier/Verifier manifest discrepency in round " << i;
339 }
340
341 InnerDeciderProver decider_prover(folding_proof.accumulator);
342 decider_prover.construct_proof();
343 auto decider_proof = decider_prover.export_proof();
344
345 OuterBuilder decider_circuit;
346 DeciderRecursiveVerifier decider_verifier{ &decider_circuit, native_verifier_acc };
347 auto pairing_points = decider_verifier.verify_proof(decider_proof);
348
349 // IO
351 inputs.pairing_inputs = pairing_points;
352 inputs.set_public();
353
354 info("Decider Recursive Verifier: num gates = ", decider_circuit.num_gates);
355 // Check for a failure flag in the recursive verifier circuit
356 EXPECT_EQ(decider_circuit.failed(), false) << decider_circuit.err();
357
358 // Perform native verification then perform the pairing on the outputs of the recursive decider verifier and
359 // check that the result agrees.
360 InnerDeciderVerifier native_decider_verifier(verifier_accumulator);
361 auto native_decider_output = native_decider_verifier.verify_proof(decider_proof);
362 auto native_result = native_decider_output.check();
363 NativeVerifierCommitmentKey pcs_vkey{};
364 auto recursive_result = pcs_vkey.pairing_check(pairing_points.P0.get_value(), pairing_points.P1.get_value());
365 EXPECT_EQ(native_result, recursive_result);
366
367 {
368 auto decider_pk = std::make_shared<OuterDeciderProvingKey>(decider_circuit);
369 auto honk_vk = std::make_shared<typename OuterFlavor::VerificationKey>(decider_pk->get_precomputed());
370 OuterProver prover(decider_pk, honk_vk);
371 OuterVerifier verifier(honk_vk);
372 auto proof = prover.construct_proof();
373 bool verified = verifier.template verify_proof<bb::DefaultIO>(proof).result;
374
375 ASSERT_TRUE(verified);
376 }
377 };
378
380 {
381 // Natively fold two circuits
382 auto [prover_accumulator, verifier_accumulator] = fold_and_verify_native();
383
384 // Tamper with the accumulator by changing the target sum
385 verifier_accumulator->target_sum = FF::random_element(&engine);
386
387 // Create a decider proof for accumulator obtained through folding
388 InnerDeciderProver decider_prover(prover_accumulator);
389 decider_prover.construct_proof();
390 auto decider_proof = decider_prover.export_proof();
391
392 // Create a decider verifier circuit for recursively verifying the decider proof
393 OuterBuilder decider_circuit;
394 DeciderRecursiveVerifier decider_verifier{ &decider_circuit, verifier_accumulator };
395 [[maybe_unused]] auto output = decider_verifier.verify_proof(decider_proof);
396 info("Decider Recursive Verifier: num gates = ", decider_circuit.num_gates);
397
398 // We expect the decider circuit check to fail due to the bad proof
399 EXPECT_FALSE(CircuitChecker::check(decider_circuit));
400 };
401
403 {
404 // Fold two circuits natively
405 auto [prover_accumulator, verifier_accumulator] = fold_and_verify_native();
406
407 // Create another circuit to do a second round of folding
411 auto verification_key = std::make_shared<InnerVerificationKey>(prover_inst->get_precomputed());
412 auto verifier_inst = std::make_shared<InnerDeciderVerificationKey>(verification_key);
413
414 // Corrupt a wire value in the accumulator
415 prover_accumulator->polynomials.w_l.at(1) = FF::random_element(&engine);
416
417 // Generate a folding proof with the incorrect polynomials which would result in the prover having the wrong
418 // target sum
419 InnerFoldingProver folding_prover({ prover_accumulator, prover_inst },
420 { verifier_accumulator, verifier_inst },
422 auto folding_proof = folding_prover.prove();
423
424 // Create a folding verifier circuit
425 // commitments
426 OuterBuilder folding_circuit;
427 auto recursive_decider_vk_1 =
428 std::make_shared<RecursiveDeciderVerificationKey>(&folding_circuit, verifier_accumulator);
429 auto recursive_vk_and_hash_2 = std::make_shared<RecursiveVKAndHash>(folding_circuit, verifier_inst->vk);
430 stdlib::Proof<OuterBuilder> stdlib_proof(folding_circuit, folding_proof.proof);
431
432 auto verifier = FoldingRecursiveVerifier{ &folding_circuit,
433 recursive_decider_vk_1,
434 { recursive_vk_and_hash_2 },
436 auto recursive_verifier_acc = verifier.verify_folding_proof(stdlib_proof);
437
438 // Validate that the target sum between prover and verifier is now different
439 EXPECT_FALSE(folding_proof.accumulator->target_sum == recursive_verifier_acc->target_sum.get_value());
440 };
441
442 // Ensure that the PG recursive verifier circuit is independent of the size of the circuits being folded
444 {
445 struct ProofAndVerifier {
446 HonkProof fold_proof;
447 OuterBuilder verifier_circuit;
448 };
449
450 // Fold two circuits of a given size then construct a recursive PG verifier circuit
451 auto produce_proof_and_verifier_circuit = [](size_t log_num_gates) -> ProofAndVerifier {
452 InnerBuilder builder1;
453 create_function_circuit(builder1, log_num_gates);
454 InnerBuilder builder2;
455 create_function_circuit(builder2, log_num_gates);
456
457 // Generate a folding proof
458 auto decider_pk_1 = std::make_shared<InnerDeciderProvingKey>(builder1);
459 auto decider_pk_2 = std::make_shared<InnerDeciderProvingKey>(builder2);
460 auto honk_vk_1 = std::make_shared<InnerVerificationKey>(decider_pk_1->get_precomputed());
461 auto decider_vk_1 = std::make_shared<InnerDeciderVerificationKey>(honk_vk_1);
462 auto honk_vk_2 = std::make_shared<InnerVerificationKey>(decider_pk_2->get_precomputed());
463 auto decider_vk_2 = std::make_shared<InnerDeciderVerificationKey>(honk_vk_2);
464 InnerFoldingProver folding_prover({ decider_pk_1, decider_pk_2 },
465 { decider_vk_1, decider_vk_2 },
467 auto fold_result = folding_prover.prove();
468
469 // Create a folding verifier circuit
470 OuterBuilder verifier_circuit;
471 auto recursive_decider_vk_1 =
472 std::make_shared<RecursiveDeciderVerificationKey>(&verifier_circuit, decider_vk_1);
473 auto recursive_vk_and_hash_2 = std::make_shared<RecursiveVKAndHash>(verifier_circuit, decider_vk_2->vk);
474 stdlib::Proof<OuterBuilder> stdlib_proof(verifier_circuit, fold_result.proof);
475
476 auto verifier =
477 FoldingRecursiveVerifier{ &verifier_circuit,
478 recursive_decider_vk_1,
479 { recursive_vk_and_hash_2 },
481 auto recursive_verifier_native_accum = verifier.verify_folding_proof(stdlib_proof);
482
483 return { fold_result.proof, verifier_circuit };
484 };
485
486 // Create fold proofs and verifier circuits from folding circuits of different sizes
487 auto [proof_1, verifier_circuit_1] = produce_proof_and_verifier_circuit(10);
488 auto [proof_2, verifier_circuit_2] = produce_proof_and_verifier_circuit(11);
489
490 EXPECT_TRUE(CircuitChecker::check(verifier_circuit_1));
491 EXPECT_TRUE(CircuitChecker::check(verifier_circuit_2));
492
493 // Check that the proofs are the same size and that the verifier circuits have the same number of gates
494 EXPECT_EQ(proof_1.size(), proof_2.size());
495 EXPECT_EQ(verifier_circuit_1.get_estimated_num_finalized_gates(),
496 verifier_circuit_2.get_estimated_num_finalized_gates());
497
498 // The circuit blocks (selectors + wires) fully determine the circuit - check that they are identical
499 EXPECT_EQ(verifier_circuit_1.blocks, verifier_circuit_2.blocks);
500 }
501};
502
507
512
517
518TEST_F(ProtogalaxyRecursiveTests, RecursiveFoldingTwiceTest)
519{
521}
522
528
533
538
543
544} // namespace bb::stdlib::recursion::honk
virtual uint32_t add_public_variable(const FF &in)
A DeciderProvingKey is normally constructed from a finalized circuit and it contains all the informat...
The DeciderVerificationKey encapsulates all the necessary information for a Mega Honk Verifier to ver...
Output verify_proof(const DeciderProof &)
Verify a decider proof relative to a decider verification key (ϕ, \vec{β*}, e*).
The verification key is responsible for storing the commitments to the precomputed (non-witness) poly...
Curve::ScalarField FF
bb::VerifierCommitmentKey< Curve > VerifierCommitmentKey
MegaCircuitBuilder CircuitBuilder
Curve::AffineElement Commitment
The recursive counterpart to the "native" Mega flavor.
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
Fr evaluate(const Fr &z, size_t target_size) const
BB_PROFILE FoldingResult< Flavor > prove()
Execute the folding prover.
std::shared_ptr< Transcript > transcript
std::shared_ptr< DeciderVK > verify_folding_proof(const std::vector< FF > &)
Run the folding protocol on the verifier side to establish whether the public data ϕ of the new accum...
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
static byte_array_ct hash(const byte_array_ct &input)
Definition blake3s.cpp:183
A simple wrapper around a vector of stdlib field elements representing a proof.
Definition proof.hpp:19
Represents a dynamic array of bytes in-circuit.
static field_ct hash(const std::vector< field_ct > &in, GeneratorContext context={})
Definition pedersen.cpp:15
PairingPoints verify_proof(const HonkProof &proof)
Creates a circuit that executes the decider verifier algorithm up to the final pairing check.
Manages the data that is propagated on the public inputs of an application/function circuit.
void set_public()
Set each IO component to be a public input of the underlying circuit.
static void add_default(Builder &builder)
Add default public inputs when they are not present.
static std::tuple< std::shared_ptr< InnerDeciderProvingKey >, std::shared_ptr< InnerDeciderVerificationKey > > fold_and_verify_native()
static void test_full_protogalaxy_recursive()
Perform two rounds of folding valid circuits and then recursive verify the final decider proof,...
RecursiveDeciderVerificationKeys::VerificationKey RecursiveVerificationKey
static void create_function_circuit(InnerBuilder &builder, size_t log_num_gates=10)
Create a non-trivial arbitrary inner circuit, the proof of which will be recursively verified.
static void test_new_evaluate()
Ensure that evaluating the perturbator in the recursive folding verifier returns the same result as e...
static void test_recursive_folding(const size_t num_verifiers=1)
Tests that a valid recursive fold works as expected.
static void test_circuit()
Create inner circuit and call check_circuit on it.
std::shared_ptr< DeciderVK > verify_folding_proof(const stdlib::Proof< Builder > &)
Run the folding protocol on the verifier side to establish whether the public data ϕ of the new accum...
The stdlib counterpart of DeciderVerificationKey, used in recursive folding verification.
void info(Args... args)
Definition log.hpp:70
AluTraceBuilder builder
Definition alu.test.cpp:123
FF a
FF b
numeric::RNG & engine
bn254::ScalarField fr_ct
bn254::BaseField fq_ct
bn254::public_witness_ct public_witness_ct
bn254::witness_ct witness_ct
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:190
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
TEST_F(BoomerangGoblinRecursiveVerifierTests, graph_description_basic)
Construct and check a goblin recursive verification circuit.
std::vector< fr > HonkProof
Definition proof.hpp:15
field< Bn254FrParams > fr
Definition fr.hpp:174
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static field random_element(numeric::RNG *engine=nullptr) noexcept
field_t< CircuitBuilder > ScalarField
Definition bn254.hpp:33
byte_array< CircuitBuilder > byte_array_ct
Definition bn254.hpp:43
curve::BN254::ScalarField ScalarFieldNative
Definition bn254.hpp:24
public_witness_t< CircuitBuilder > public_witness_ct
Definition bn254.hpp:42
witness_t< CircuitBuilder > witness_ct
Definition bn254.hpp:41