Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
translator.test.cpp
Go to the documentation of this file.
20
21#include <gtest/gtest.h>
22using namespace bb;
23
27static auto& engine = numeric::get_debug_randomness();
28
29// Test helper: Create a VK by committing to proving key polynomials (for comparing with fixed VK)
32{
34 // Only ordered_extra_range_constraints_numerator needs a VK commitment (the only non-computable precomputed)
35 vk.ordered_extra_range_constraints_numerator =
36 proving_key->commitment_key.commit(proving_key->polynomials.ordered_extra_range_constraints_numerator);
37 return vk;
38}
39
40// Compute VK hash from fixed commitments (for test verification that vk_hash() is correct)
42{
44 // Serialize commitments using the Codec
45 for (const auto& commitment : TranslatorHardcodedVKAndHash::get_all()) {
47 for (const auto& fr : frs) {
48 elements.push_back(fr);
49 }
50 }
52}
53
54class TranslatorTests : public ::testing::Test {
56 using Fr = fr;
57 using Fq = fq;
59 using FF = Flavor::FF;
61
62 protected:
64
81 {
82 TranscriptManifest manifest;
83 constexpr size_t frs_per_G = FrCodec::calc_num_fields<Flavor::Commitment>();
84 constexpr size_t NUM_SUMCHECK_ROUNDS = Flavor::CONST_TRANSLATOR_LOG_N;
85
86 // Round 0: vk_hash, Gemini masking, wire commitments
87 manifest.add_entry(0, "vk_hash", 1);
88 manifest.add_entry(0, "Gemini:masking_poly_comm", frs_per_G);
89
90 // Wire commitments (10 total: 5 concatenated + 5 ordered)
91 // clang-format off
92 std::vector<std::string> wire_labels = {
93 "CONCATENATED_RANGE_CONSTRAINTS_0", "CONCATENATED_RANGE_CONSTRAINTS_1",
94 "CONCATENATED_RANGE_CONSTRAINTS_2", "CONCATENATED_RANGE_CONSTRAINTS_3",
95 "CONCATENATED_NON_RANGE",
96 "ORDERED_RANGE_CONSTRAINTS_0", "ORDERED_RANGE_CONSTRAINTS_1",
97 "ORDERED_RANGE_CONSTRAINTS_2", "ORDERED_RANGE_CONSTRAINTS_3",
98 "ORDERED_RANGE_CONSTRAINTS_4",
99 };
100 // clang-format on
101 for (const auto& label : wire_labels) {
102 manifest.add_entry(0, label, frs_per_G);
103 }
104 // beta and gamma are consecutive challenges (no data between), so both in round 0
105 manifest.add_challenge(0, "beta");
106 manifest.add_challenge(0, "gamma");
107
108 // Round 1: Z_PERM -> Sumcheck:alpha + all gate challenges (same round, no data between them)
109 manifest.add_entry(1, "Z_PERM", frs_per_G);
110 manifest.add_challenge(1, "Sumcheck:alpha");
111 manifest.add_challenge(1, "Sumcheck:gate_challenge");
112
113 // Round 2: Libra concatenation commitment + Sum -> Libra:Challenge
114 manifest.add_entry(2, "Libra:concatenation_commitment", frs_per_G);
115 manifest.add_entry(2, "Libra:Sum", 1);
116 manifest.add_challenge(2, "Libra:Challenge");
117
118 // Rounds 3-15: Sumcheck univariates for mini-circuit rounds 0..12
119 constexpr size_t LOG_MINI = Flavor::LOG_MINI_CIRCUIT_SIZE;
120 for (size_t i = 0; i < LOG_MINI; ++i) {
121 manifest.add_entry(3 + i, "Sumcheck:univariate_" + std::to_string(i), 9);
122 manifest.add_challenge(3 + i, "Sumcheck:u_" + std::to_string(i));
123 }
124
125 // Round 16: 154 minicircuit wire evaluations sent mid-sumcheck, then univariate_13
126 manifest.add_entry(3 + LOG_MINI, "Sumcheck:minicircuit_evaluations", Flavor::NUM_MINICIRCUIT_EVALUATIONS);
127 manifest.add_entry(3 + LOG_MINI, "Sumcheck:univariate_" + std::to_string(LOG_MINI), 9);
128 manifest.add_challenge(3 + LOG_MINI, "Sumcheck:u_" + std::to_string(LOG_MINI));
129
130 // Rounds 17-19: remaining sumcheck rounds 14..16
131 for (size_t i = LOG_MINI + 1; i < NUM_SUMCHECK_ROUNDS; ++i) {
132 manifest.add_entry(3 + i, "Sumcheck:univariate_" + std::to_string(i), 9);
133 manifest.add_challenge(3 + i, "Sumcheck:u_" + std::to_string(i));
134 }
135
136 // Sumcheck full-circuit evaluations (computable precomputed + minicircuit wires excluded) + Libra commitments
137 // -> rho
138 const size_t eval_round = 3 + NUM_SUMCHECK_ROUNDS;
139 manifest.add_entry(eval_round, "Sumcheck:evaluations", Flavor::NUM_FULL_CIRCUIT_EVALUATIONS);
140 manifest.add_entry(eval_round, "Libra:claimed_evaluation", 1);
141 manifest.add_entry(eval_round, "Libra:grand_sum_commitment", frs_per_G);
142 manifest.add_entry(eval_round, "Libra:quotient_commitment", frs_per_G);
143 manifest.add_challenge(eval_round, "rho");
144
145 // Gemini fold commitments -> Gemini:r
146 const size_t gemini_fold_round = eval_round + 1;
147 for (size_t i = 1; i < NUM_SUMCHECK_ROUNDS; ++i) {
148 manifest.add_entry(gemini_fold_round, "Gemini:FOLD_" + std::to_string(i), frs_per_G);
149 }
150 manifest.add_challenge(gemini_fold_round, "Gemini:r");
151
152 // Gemini evaluations + Libra evals -> Shplonk:nu
153 const size_t gemini_eval_round = gemini_fold_round + 1;
154 for (size_t i = 1; i <= NUM_SUMCHECK_ROUNDS; ++i) {
155 manifest.add_entry(gemini_eval_round, "Gemini:a_" + std::to_string(i), 1);
156 }
157 // No more Gemini:P_pos / Gemini:P_neg (interleaving replaced by concatenation)
158 manifest.add_entry(gemini_eval_round, "Libra:concatenation_eval", 1);
159 manifest.add_entry(gemini_eval_round, "Libra:shifted_grand_sum_eval", 1);
160 manifest.add_entry(gemini_eval_round, "Libra:grand_sum_eval", 1);
161 manifest.add_entry(gemini_eval_round, "Libra:quotient_eval", 1);
162 manifest.add_challenge(gemini_eval_round, "Shplonk:nu");
163
164 // Shplonk:Q -> Shplonk:z
165 const size_t shplonk_round = gemini_eval_round + 1;
166 manifest.add_entry(shplonk_round, "Shplonk:Q", frs_per_G);
167 manifest.add_challenge(shplonk_round, "Shplonk:z");
168
169 // KZG:W
170 const size_t kzg_round = shplonk_round + 1;
171 manifest.add_entry(kzg_round, "KZG:W", frs_per_G);
172
173 return manifest;
174 }
175
176 // Helper function to add no-ops
177 static void add_random_ops(std::shared_ptr<bb::ECCOpQueue>& op_queue, size_t count = 1)
178 {
179 for (size_t i = 0; i < count; i++) {
180 op_queue->random_op_ultra_only();
181 }
182 }
183
184 static void add_mixed_ops(std::shared_ptr<bb::ECCOpQueue>& op_queue, size_t count = 100)
185 {
186 auto P1 = G1::random_element();
187 auto P2 = G1::random_element();
188 auto z = Fr::random_element();
189 for (size_t i = 0; i < count; i++) {
190 op_queue->add_accumulate(P1);
191 op_queue->mul_accumulate(P2, z);
192 }
193 op_queue->eq_and_reset();
194 }
195
196 // Construct a test circuit based on some random operations
197 static CircuitBuilder generate_test_circuit(const Fq& batching_challenge_v,
198 const Fq& evaluation_challenge_x,
199 const size_t circuit_size_parameter = 500)
200 {
201
202 auto op_queue = std::make_shared<ECCOpQueue>();
203 // Construct zk_columns
204 op_queue->construct_zk_columns();
205 // Table with correct final structure for translator
206 add_mixed_ops(op_queue, circuit_size_parameter / 2);
208 // Merge with fixed append
209 op_queue->merge_fixed_append(op_queue->get_append_offset());
210
211 return CircuitBuilder{ batching_challenge_v, evaluation_challenge_x, op_queue };
212 }
213
214 static bool prove_and_verify(const CircuitBuilder& circuit_builder,
215 const Fq& evaluation_challenge_x,
216 const Fq& batching_challenge_v)
217 {
218 // Setup prover transcript
219 auto prover_transcript = std::make_shared<Transcript>();
220 prover_transcript->send_to_verifier("init", Fq::random_element());
221 auto initial_transcript = prover_transcript->export_proof();
222
223 // Setup verifier transcript
224 auto verifier_transcript = std::make_shared<Transcript>(initial_transcript);
225 verifier_transcript->template receive_from_prover<Fq>("init");
226
227 // Create proving key and prover
228 auto proving_key = std::make_shared<TranslatorProvingKey>(circuit_builder);
229 TranslatorProver prover{ proving_key, prover_transcript };
230
231 // Generate proof
232 auto proof = prover.construct_proof();
233
234 // Commit to op queue wires
236 op_queue_commitments[0] =
237 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.op);
238 op_queue_commitments[1] =
239 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.x_lo_y_hi);
240 op_queue_commitments[2] =
241 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.x_hi_z_1);
242 op_queue_commitments[3] =
243 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.y_lo_z_2);
244
245 // Get accumulated_result from the prover
246 uint256_t accumulated_result = prover.get_accumulated_result();
247
248 // Create verifier
249 TranslatorVerifier verifier(verifier_transcript,
250 proof,
251 evaluation_challenge_x,
252 batching_challenge_v,
253 accumulated_result,
254 op_queue_commitments);
255
256 // Verify proof: get reduction result and check all components
257 auto result = verifier.reduce_to_pairing_check();
258 return result.pairing_points.check() && result.reduction_succeeded;
259 }
260
261 static bool prove_and_verify_short_monomial(const CircuitBuilder& circuit_builder,
262 const Fq& evaluation_challenge_x,
263 const Fq& batching_challenge_v)
264 {
265 auto prover_transcript = std::make_shared<Transcript>();
266 prover_transcript->send_to_verifier("init", Fq::random_element());
267 auto initial_transcript = prover_transcript->export_proof();
268
269 auto verifier_transcript = std::make_shared<Transcript>(initial_transcript);
270 verifier_transcript->template receive_from_prover<Fq>("init");
271
272 auto proving_key = std::make_shared<TranslatorProvingKey>(circuit_builder);
273 TranslatorProver prover{ proving_key, prover_transcript };
274 auto proof = prover.construct_proof();
275 EXPECT_EQ(proof.size(), TranslatorFlavor::PROOF_LENGTH);
276
278 op_queue_commitments[0] =
279 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.op);
280 op_queue_commitments[1] =
281 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.x_lo_y_hi);
282 op_queue_commitments[2] =
283 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.x_hi_z_1);
284 op_queue_commitments[3] =
285 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.y_lo_z_2);
286
287 TranslatorVerifier verifier(verifier_transcript,
288 proof,
289 evaluation_challenge_x,
290 batching_challenge_v,
291 prover.get_accumulated_result(),
292 op_queue_commitments);
293
294 auto result = verifier.reduce_to_pairing_check();
295 return result.pairing_points.check() && result.reduction_succeeded;
296 }
297
298 template <typename FullRelation, typename ShortRelation>
300 const RelationParameters<FF>& params,
301 const FF& scaling_factor)
302 {
303 typename TranslatorFlavor::ExtendedEdges extended_edges;
304 for (auto [extended_edge, short_edge] : zip_view(extended_edges.get_all(), in.get_all())) {
305 extended_edge = short_edge.template extend_to<TranslatorFlavor::MAX_PARTIAL_RELATION_LENGTH>();
306 }
307
308 typename FullRelation::SumcheckTupleOfUnivariatesOverSubrelations full_accumulators{};
309 typename ShortRelation::SumcheckTupleOfUnivariatesOverSubrelations short_accumulators{};
310 FullRelation::accumulate(full_accumulators, extended_edges, params, scaling_factor);
311 ShortRelation::accumulate(short_accumulators, in, params, scaling_factor);
312
313 EXPECT_EQ(short_accumulators, full_accumulators);
314 }
315
317 {
319 FF value = 0;
320 for (auto& edge : result.get_all()) {
321 if (random_inputs) {
323 } else {
324 value += 1;
325 edge = bb::Univariate<FF, 2>({ value, value + 1 });
326 value += 1;
327 }
328 }
329 return result;
330 }
331};
332
340TEST_F(TranslatorTests, ProofLengthCheck)
341{
342 using Fq = fq;
343
344 Fq batching_challenge_v = Fq::random_element();
345 Fq evaluation_challenge_x = Fq::random_element();
346
347 // Generate a circuit and its verification key (computed at runtime from the proving key)
348 CircuitBuilder circuit_builder = generate_test_circuit(batching_challenge_v, evaluation_challenge_x);
349
350 // Setup prover transcript
351 auto prover_transcript = std::make_shared<Transcript>();
352 prover_transcript->send_to_verifier("init", Fq::random_element());
353 prover_transcript->export_proof();
354 auto proving_key = std::make_shared<TranslatorProvingKey>(circuit_builder);
355 TranslatorProver prover{ proving_key, prover_transcript };
356
357 // Generate proof
358 auto proof = prover.construct_proof();
359
360 EXPECT_EQ(proof.size(), TranslatorFlavor::PROOF_LENGTH);
361}
362
368{
369 using Fq = fq;
370
371 Fq batching_challenge_v = Fq::random_element();
372 Fq evaluation_challenge_x = Fq::random_element();
373
374 // Generate a circuit without no-ops
375 CircuitBuilder circuit_builder = generate_test_circuit(batching_challenge_v, evaluation_challenge_x);
376
377 EXPECT_TRUE(TranslatorCircuitChecker::check(circuit_builder));
378 bool verified = prove_and_verify(circuit_builder, evaluation_challenge_x, batching_challenge_v);
379 EXPECT_TRUE(verified);
380}
381
382TEST_F(TranslatorTests, ShortMonomialRelationsMatchFullEdgeRelations)
383{
384 const auto run_test = [&](bool random_inputs) {
385 using FF = TranslatorFlavor::FF;
386 const auto input = get_short_edge_input(random_inputs);
387 const auto params = RelationParameters<FF>::get_random();
388 const FF scaling_factor = random_inputs ? FF::random_element() : FF(7);
389
390 expect_short_relation_matches_full_edges<TranslatorPermutationRelation<FF>,
391 TranslatorPermutationShortRelation<FF>>(input, params, scaling_factor);
392 expect_short_relation_matches_full_edges<TranslatorDeltaRangeConstraintRelation<FF>,
394 input, params, scaling_factor);
395 expect_short_relation_matches_full_edges<TranslatorOpcodeConstraintRelation<FF>,
397 input, params, scaling_factor);
398 expect_short_relation_matches_full_edges<TranslatorAccumulatorTransferRelation<FF>,
400 input, params, scaling_factor);
401 expect_short_relation_matches_full_edges<TranslatorDecompositionRelation<FF>,
403 input, params, scaling_factor);
404 expect_short_relation_matches_full_edges<TranslatorNonNativeFieldRelation<FF>,
406 input, params, scaling_factor);
407 expect_short_relation_matches_full_edges<TranslatorZeroConstraintsRelation<FF>,
409 input, params, scaling_factor);
410 };
411
412 run_test(/*random_inputs=*/false);
413 run_test(/*random_inputs=*/true);
414}
415
416TEST_F(TranslatorTests, ShortMonomialProverVerifies)
417{
418 using Fq = fq;
419
420 Fq batching_challenge_v = Fq::random_element();
421 Fq evaluation_challenge_x = Fq::random_element();
422
423 CircuitBuilder circuit_builder = generate_test_circuit(batching_challenge_v, evaluation_challenge_x);
424
425 EXPECT_TRUE(TranslatorCircuitChecker::check(circuit_builder));
426 EXPECT_TRUE(prove_and_verify(circuit_builder, evaluation_challenge_x, batching_challenge_v));
427 EXPECT_TRUE(prove_and_verify_short_monomial(circuit_builder, evaluation_challenge_x, batching_challenge_v));
428}
429
436{
437 using Fq = fq;
438
439 Fq batching_challenge_v = Fq::random_element();
440 Fq evaluation_challenge_x = Fq::random_element();
441
442 // Add the same operations to the ECC op queue; the native computation is performed under the hood.
443 auto op_queue = std::make_shared<bb::ECCOpQueue>();
444 // Seed a no-op to supply the 2 leading zero rows Translator's op-queue wires need for shiftability.
445 op_queue->no_op_ultra_only();
446 add_random_ops(op_queue, CircuitBuilder::NUM_RANDOM_OPS_START);
447 add_mixed_ops(op_queue, 100);
448 op_queue->merge();
449 auto circuit_builder = CircuitBuilder{ batching_challenge_v, evaluation_challenge_x, op_queue, /*avm_mode=*/true };
450
451 EXPECT_TRUE(TranslatorCircuitChecker::check(circuit_builder));
452 bool verified = prove_and_verify(circuit_builder, evaluation_challenge_x, batching_challenge_v);
453 EXPECT_TRUE(verified);
454}
455
465{
466 using Fq = fq;
467
468 auto prover_transcript = std::make_shared<Transcript>();
469 prover_transcript->send_to_verifier("init", Fq::random_element());
470 prover_transcript->export_proof();
471 Fq batching_challenge_v = Fq::random_element();
472 Fq evaluation_challenge_x = Fq::random_element();
473
474 // Generate the default fixed VK
476
477 // Lambda for manually computing a verification key for a given circuit and comparing it to the fixed VK
478 auto compare_computed_vk_against_fixed = [&](size_t circuit_size_parameter) {
479 CircuitBuilder circuit_builder =
480 generate_test_circuit(batching_challenge_v, evaluation_challenge_x, circuit_size_parameter);
481 auto proving_key = std::make_shared<TranslatorProvingKey>(circuit_builder);
482 TranslatorProver prover{ proving_key, prover_transcript };
483 TranslatorFlavor::VerificationKey computed_vk = create_vk_from_proving_key(proving_key->proving_key);
484 auto labels = TranslatorFlavor::VerificationKey::get_labels();
485
486 size_t index = 0;
487 for (auto [vk_commitment, fixed_commitment] : zip_view(computed_vk.get_all(), fixed_vk.get_all())) {
488 if (vk_commitment != fixed_commitment) {
489 info("// ", labels[index]);
490 info("Commitment(uint256_t(\"0x", vk_commitment.x, "\"),");
491 info(" uint256_t(\"0x", vk_commitment.y, "\")),");
492 }
493 EXPECT_EQ(vk_commitment, fixed_commitment) << "Mismatch at label: " << labels[index];
494 ++index;
495 }
496
497 EXPECT_EQ(computed_vk, fixed_vk);
498 };
499
500 // Check consistency of the fixed VK with the computed VK for some different circuit sizes
501 const size_t circuit_size_parameter_1 = 1 << 2;
502 const size_t circuit_size_parameter_2 = 1 << 3;
503
504 compare_computed_vk_against_fixed(circuit_size_parameter_1);
505 compare_computed_vk_against_fixed(circuit_size_parameter_2);
506
507 // Verify that the hardcoded VK hash matches the computed hash
508 auto computed_hash = compute_translator_vk_hash();
509 auto hardcoded_hash = TranslatorHardcodedVKAndHash::vk_hash();
510 if (computed_hash != hardcoded_hash) {
511 info("VK hash mismatch! Update TranslatorHardcodedVKAndHash::vk_hash() with:");
512 info("0x", computed_hash);
513 }
514 EXPECT_EQ(computed_hash, hardcoded_hash) << "Hardcoded VK hash does not match computed hash";
515}
516
522TEST_F(TranslatorTests, TranscriptPinned)
523{
524 using Fq = fq;
525
526 Fq batching_challenge_v = Fq::random_element();
527 Fq evaluation_challenge_x = Fq::random_element();
528
529 CircuitBuilder circuit_builder = generate_test_circuit(batching_challenge_v, evaluation_challenge_x);
530
531 // Create proving key and prover
532 auto prover_transcript = std::make_shared<Transcript>();
533 auto proving_key = std::make_shared<TranslatorProvingKey>(circuit_builder);
534 TranslatorProver prover{ proving_key, prover_transcript };
535
536 // Generate proof
537 auto proof = prover.construct_proof();
538
539 // Setup verifier transcript with manifest tracking
540 auto verifier_transcript = std::make_shared<Transcript>(proof);
541 verifier_transcript->enable_manifest();
542
543 // Get accumulated_result from the prover
544 uint256_t accumulated_result = prover.get_accumulated_result();
545
546 // Commit to op queue wires
548 op_queue_commitments[0] = proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.op);
549 op_queue_commitments[1] =
550 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.x_lo_y_hi);
551 op_queue_commitments[2] =
552 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.x_hi_z_1);
553 op_queue_commitments[3] =
554 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.y_lo_z_2);
555
556 // Create verifier with all required inputs
557 TranslatorVerifier verifier(verifier_transcript,
558 proof,
559 evaluation_challenge_x,
560 batching_challenge_v,
561 accumulated_result,
562 op_queue_commitments);
563
564 // Run verification - just reduce to pairing check to exercise the transcript
565 [[maybe_unused]] auto result = verifier.reduce_to_pairing_check();
566
567 // Compare verifier manifest against hardcoded expected structure
568 auto expected_manifest = build_expected_translator_manifest();
569 auto verifier_manifest = verifier_transcript->get_manifest();
570
571 EXPECT_EQ(verifier_manifest, expected_manifest);
572}
573
590TEST_F(TranslatorTests, EvaluationPartition)
591{
592 using Flavor = TranslatorFlavor;
593 using FF = Flavor::FF;
594
595 // Fill all entities with distinct values (entity index as value)
597 {
598 size_t idx = 0;
599 for (auto& e : evals.get_all()) {
600 e = FF(idx++);
601 }
602 }
603
604 // Collect addresses of all entities touched by each getter
605 std::set<FF*> covered;
606
607 for (auto& e : evals.get_minicircuit_wires()) {
608 EXPECT_TRUE(covered.insert(&e).second) << "minicircuit wire overlaps with a previous entity";
609 }
610 EXPECT_EQ(covered.size(), Flavor::NUM_MINICIRCUIT_WIRES);
611
612 for (auto& e : evals.get_minicircuit_wires_shifted()) {
613 EXPECT_TRUE(covered.insert(&e).second) << "minicircuit wire shift overlaps with a previous entity";
614 }
615 EXPECT_EQ(covered.size(), 2 * Flavor::NUM_MINICIRCUIT_WIRES);
616
617 for (auto& e : evals.get_full_circuit_entities()) {
618 EXPECT_TRUE(covered.insert(&e).second) << "full-circuit entity overlaps with a previous entity";
619 }
620 EXPECT_EQ(covered.size(), 2 * Flavor::NUM_MINICIRCUIT_WIRES + Flavor::NUM_FULL_CIRCUIT_EVALUATIONS);
621
622 // Concat polys are reconstructed (not sent in proof), but still in AllEntities
623 for (auto& e : evals.get_concatenated()) {
624 EXPECT_TRUE(covered.insert(&e).second) << "concatenated poly overlaps with a previous entity";
625 }
626 EXPECT_EQ(covered.size(),
627 2 * Flavor::NUM_MINICIRCUIT_WIRES + Flavor::NUM_FULL_CIRCUIT_EVALUATIONS +
628 Flavor::NUM_CONCATENATED_POLYS);
629
630 // The computable precomputed selectors are the remaining entities
631 size_t remaining = Flavor::NUM_ALL_ENTITIES - covered.size();
632 EXPECT_EQ(remaining, Flavor::NUM_COMPUTABLE_PRECOMPUTED);
633
634 // Verify the remaining entities are exactly the computable precomputed ones
635 for (auto& e : evals.get_all()) {
636 if (covered.find(&e) == covered.end()) {
637 // This entity must be one of the 12 computable precomputed selectors
638 remaining--;
639 }
640 }
641 EXPECT_EQ(remaining, 0UL);
642}
643
655TEST_F(TranslatorTests, RepeatedCommitmentsIndicesCorrect)
656{
657 using Flavor = TranslatorFlavor;
658 using Commitment = Flavor::Commitment;
659
660 fq batching_challenge_v = fq::random_element();
661 fq evaluation_challenge_x = fq::random_element();
662 CircuitBuilder circuit_builder = generate_test_circuit(batching_challenge_v, evaluation_challenge_x);
663 auto pk = std::make_shared<TranslatorProvingKey>(circuit_builder);
664
665 pk->proving_key->commitment_key = Flavor::CommitmentKey(pk->proving_key->circuit_size);
666
667 auto pcs_unshifted = pk->proving_key->polynomials.get_pcs_unshifted();
668 auto pcs_to_be_shifted = pk->proving_key->polynomials.get_pcs_to_be_shifted();
669
670 // Commit to all PCS polynomials
671 const auto& ck = pk->proving_key->commitment_key;
672 std::vector<Commitment> unshifted_comms;
673 for (auto& poly : pcs_unshifted) {
674 unshifted_comms.push_back(ck.commit(poly));
675 }
676 std::vector<Commitment> shifted_comms;
677 for (auto& poly : pcs_to_be_shifted) {
678 shifted_comms.push_back(ck.commit(poly));
679 }
680
681 // Build the commitment vector exactly as Shplemini does: [Q, pcs_unshifted..., pcs_to_be_shifted...]
682 std::vector<Commitment> commitments;
683 commitments.push_back(Commitment::one()); // dummy Q
684 commitments.insert(commitments.end(), unshifted_comms.begin(), unshifted_comms.end());
685 commitments.insert(commitments.end(), shifted_comms.begin(), shifted_comms.end());
686
687 constexpr auto repeated = Flavor::REPEATED_COMMITMENTS;
688 // Same offset logic as remove_repeated_commitments
689 constexpr size_t offset = Flavor::HasZK ? 2 : 1;
690
691 // Verify both ranges using the same indexing as remove_repeated_commitments
692 auto check_range = [&](const auto& range, const std::string& label) {
693 for (size_t i = 0; i < range.count; i++) {
694 EXPECT_EQ(commitments[range.original_start + offset + i], commitments[range.duplicate_start + offset + i])
695 << label << " commitment mismatch at index " << i;
696 }
697 };
698
699 check_range(repeated.first, "Range 1");
700 check_range(repeated.second, "Range 2");
701}
702
703TEST_F(TranslatorTests, VerifierPopulatesAllEntities)
704{
705 using Flavor = TranslatorFlavor;
706 using FF = Flavor::FF;
707
708 // Prepare random minicircuit evaluations (154 values)
710 for (auto& v : mid) {
711 v = FF::random_element(&engine);
712 }
713
714 // Prepare random full-circuit evaluations (26 values)
716 for (auto& v : full_circuit) {
717 v = FF::random_element(&engine);
718 }
719
720 // Random challenge (computable precomputed selectors depend on this)
721 std::vector<FF> challenge(Flavor::CONST_TRANSLATOR_LOG_N);
722 for (auto& u : challenge) {
723 u = FF::random_element(&engine);
724 }
725
726 // Verifier reconstruction: start from zero, populate via the two verifier methods
728 Flavor::set_minicircuit_evaluations(evals, mid);
729 Flavor::complete_full_circuit_evaluations(evals, full_circuit, std::span<const FF>(challenge));
730
731 // Every entity should now be nonzero (probability of a random FF being zero is negligible)
732 auto all = evals.get_all();
733 for (size_t i = 0; i < Flavor::NUM_ALL_ENTITIES; i++) {
734 EXPECT_NE(all[i], FF(0)) << "Entity " << i << " was not populated by verifier methods";
735 }
736}
static bool prove_and_verify(const CircuitBuilder &circuit_builder, const Fq &evaluation_challenge_x, const Fq &batching_challenge_v)
static TranslatorShortMonomialFlavor::ProverUnivariates< 2 > get_short_edge_input(bool random_inputs)
static void add_random_ops(std::shared_ptr< bb::ECCOpQueue > &op_queue, size_t count=1)
Flavor::Commitment Commitment
static void expect_short_relation_matches_full_edges(const TranslatorShortMonomialFlavor::ProverUnivariates< 2 > &in, const RelationParameters< FF > &params, const FF &scaling_factor)
static CircuitBuilder generate_test_circuit(const Fq &batching_challenge_v, const Fq &evaluation_challenge_x, const size_t circuit_size_parameter=500)
static void SetUpTestSuite()
static void add_mixed_ops(std::shared_ptr< bb::ECCOpQueue > &op_queue, size_t count=100)
static TranscriptManifest build_expected_translator_manifest()
Build the expected transcript manifest for Translator verification.
static bool prove_and_verify_short_monomial(const CircuitBuilder &circuit_builder, const Fq &evaluation_challenge_x, const Fq &batching_challenge_v)
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
Manages ECC operations for the Goblin proving system.
A base class labelling all entities (for instance, all of the polynomials used by the prover during s...
static constexpr bool HasZK
typename Curve::ScalarField FF
static constexpr size_t NUM_ALL_ENTITIES
typename G1::affine_element Commitment
bb::CommitmentKey< Curve > CommitmentKey
static constexpr RepeatedCommitmentsData REPEATED_COMMITMENTS
Simple verification key class for fixed-size circuits (ECCVM, Translator, AVM).
Definition flavor.hpp:103
static std::vector< fr > serialize_to_fields(const T &val)
Conversion from transcript values to bb::frs.
A wrapper for Relations to expose methods used by the Sumcheck prover or verifier to add the contribu...
void add_entry(size_t round, const std::string &element_label, size_t element_size)
void add_challenge(size_t round, const std::string &label)
Add a single challenge label to the manifest for the given round.
TranslatorCircuitBuilder creates a circuit that evaluates the correctness of the evaluation of EccOpQ...
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
A base class labelling all entities (for instance, all of the polynomials used by the prover during s...
static constexpr size_t NUM_FULL_CIRCUIT_EVALUATIONS
BaseTranscript< Codec, HashFunction > Transcript
static constexpr size_t CONST_TRANSLATOR_LOG_N
static constexpr size_t PROOF_LENGTH
TranslatorCircuitBuilder CircuitBuilder
Curve::ScalarField FF
Curve::AffineElement Commitment
static constexpr size_t NUM_MINICIRCUIT_EVALUATIONS
static constexpr size_t LOG_MINI_CIRCUIT_SIZE
Translator verifier class that verifies the proof of the Translator circuit.
ReductionResult reduce_to_pairing_check()
Reduce the translator proof to a pairing check.
A univariate polynomial represented by its values on {0, 1,..., domain_end - 1}.
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
static affine_element random_element(numeric::RNG *engine=nullptr) noexcept
Samples a random point on the curve.
group_elements::affine_element< Fq, Fr, Params > affine_element
Definition group.hpp:44
#define info(...)
Definition log.hpp:93
ssize_t offset
Definition engine.cpp:62
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FqParams > fq
Definition fq.hpp:153
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:155
field< Bn254FrParams > fr
Definition fr.hpp:155
CommitmentKey< Curve > ck
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
Container for parameters used by the grand product (permutation, lookup) Honk relations.
static RelationParameters get_random()
static std::vector< Commitment > get_all()
static field random_element(numeric::RNG *engine=nullptr) noexcept
TranslatorFlavor::VerificationKey create_vk_from_proving_key(const std::shared_ptr< TranslatorFlavor::ProvingKey > &proving_key)
TranslatorFlavor::FF compute_translator_vk_hash()