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