Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
eccvm.test.cpp
Go to the documentation of this file.
1#include <cstddef>
2#include <cstdint>
3#include <gtest/gtest.h>
4#include <vector>
5
27
28using namespace bb;
35
36// Test helper: Create a VK by committing to proving key polynomials (for comparing with fixed VK)
38{
40 // Overwrite fixed commitments with computed commitments from the proving key
41 for (auto [polynomial, commitment] : zip_view(proving_key->polynomials.get_precomputed(), vk.get_all())) {
42 commitment = proving_key->commitment_key.commit(polynomial);
43 }
44 return vk;
45}
46
47// Compute VK hash from fixed commitments (for test verification that vk_hash() is correct)
49{
51 // Serialize commitments using the Codec
52 for (const auto& commitment : ECCVMHardcodedVKAndHash::get_all()) {
53 auto frs = ECCVMFlavor::Codec::serialize_to_fields(commitment);
54 for (const auto& fr : frs) {
55 elements.push_back(fr);
56 }
57 }
58 return ECCVMFlavor::HashFunction::hash(elements);
59}
60
61class ECCVMTests : public ::testing::Test {
62 protected:
64};
65namespace {
67} // namespace
68
76{
77 using Curve = curve::BN254;
78 using G1 = Curve::Element;
79 using Fr = Curve::ScalarField;
80
82 G1 a = G1::random_element(engine);
83 G1 b = G1::random_element(engine);
84 G1 c = G1::random_element(engine);
87
88 op_queue->add_accumulate(a);
89 op_queue->mul_accumulate(a, x);
90 op_queue->mul_accumulate(b, x);
91 op_queue->mul_accumulate(b, y);
92 op_queue->add_accumulate(a);
93 op_queue->mul_accumulate(b, x);
94 op_queue->eq_and_reset();
95 op_queue->add_accumulate(c);
96 op_queue->mul_accumulate(a, x);
97 op_queue->mul_accumulate(b, x);
98 op_queue->eq_and_reset();
99 op_queue->mul_accumulate(a, x);
100 op_queue->mul_accumulate(b, x);
101 op_queue->mul_accumulate(c, x);
102 op_queue->merge();
103 add_hiding_op_for_test(op_queue);
104 ECCVMCircuitBuilder builder{ op_queue };
105 return builder;
106}
107
108// returns a CircuitBuilder consisting of mul_add ops of the following form: either 0*g, for a group element, or
109// x * e, where x is a scalar and e is the identity element of the group.
110ECCVMCircuitBuilder generate_zero_circuit([[maybe_unused]] numeric::RNG* engine = nullptr, bool zero_scalars = 1)
111{
112 using Curve = curve::BN254;
113 using G1 = Curve::Element;
114 using Fr = Curve::ScalarField;
115
117
118 if (!zero_scalars) {
119 for (auto i = 0; i < 8; i++) {
121 op_queue->mul_accumulate(Curve::Group::affine_point_at_infinity, x);
122 }
123 } else {
124 for (auto i = 0; i < 8; i++) {
125 G1 g = G1::random_element(engine);
126 op_queue->mul_accumulate(g, 0);
127 }
128 }
129 op_queue->merge();
130 add_hiding_op_for_test(op_queue);
131
132 ECCVMCircuitBuilder builder{ op_queue };
133 return builder;
134}
135
138 std::vector<FF>& gate_challenges)
139{
140 // Prepare the inputs for the sumcheck prover:
141 // Compute and add beta to relation parameters
142 const FF beta = FF::random_element();
143 const FF gamma = FF::random_element();
144 const FF beta_sqr = beta * beta;
145 const FF beta_quartic = beta_sqr * beta_sqr;
146 relation_parameters.gamma = gamma;
147 relation_parameters.beta = beta;
148 relation_parameters.beta_sqr = beta_sqr;
149 relation_parameters.beta_cube = beta_sqr * beta;
150 relation_parameters.beta_quartic = beta_quartic;
151 auto first_term_tag = beta_quartic; // FIRST_TERM_TAG (= 1) * beta_quartic
152 relation_parameters.eccvm_set_permutation_delta = (gamma + first_term_tag) * (gamma + beta_sqr + first_term_tag) *
153 (gamma + beta_sqr + beta_sqr + first_term_tag) *
154 (gamma + beta_sqr + beta_sqr + beta_sqr + first_term_tag);
155 relation_parameters.eccvm_set_permutation_delta = relation_parameters.eccvm_set_permutation_delta.invert();
156
157 // Compute z_perm and inverse polynomial for our logarithmic-derivative lookup method
158 // Skip the disabled head region to preserve masking values
159 compute_logderivative_inverse<FF, ECCVMFlavor::LookupRelation, ECCVMFlavor::ProverPolynomials, true>(
160 pk->polynomials, relation_parameters, ECCVMFlavor::TRACE_OFFSET);
161 compute_grand_products<ECCVMFlavor>(pk->polynomials, relation_parameters);
162
163 // Generate gate challenges
164 for (size_t idx = 0; idx < CONST_ECCVM_LOG_N; idx++) {
165 gate_challenges[idx] = FF::random_element();
166 }
167}
168TEST_F(ECCVMTests, ZeroesCoefficients)
169{
171
172 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
173 ECCVMProver prover(builder, prover_transcript);
174 auto [proof, opening_claim] = prover.construct_proof();
175
176 // Compute IPA proof
177 auto ipa_transcript = std::make_shared<Transcript>();
178 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
179
180 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
181 ECCVMVerifier verifier(verifier_transcript, proof);
182 auto eccvm_result = verifier.reduce_to_ipa_opening();
183
184 // Verify IPA
185 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
187 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
188
189 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
190}
191// Calling get_eccvm_ops without a hiding op should throw
192TEST_F(ECCVMTests, MissingHidingOpThrows)
193{
195 // get_eccvm_ops() requires a hiding op to have been set; throws "Hiding op must be set before calling
196 // get_eccvm_ops()"
197 EXPECT_THROW(op_queue->get_eccvm_ops(), std::runtime_error);
198}
199
200// Note that `NullOpQueue` is somewhat misleading, as we add a hiding operation.
201TEST_F(ECCVMTests, NullOpQUeue)
202{
204 add_hiding_op_for_test(op_queue);
205 ECCVMCircuitBuilder builder{ op_queue };
206 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
207 ECCVMProver prover(builder, prover_transcript);
208 auto [proof, opening_claim] = prover.construct_proof();
209
210 // Compute IPA proof
211 auto ipa_transcript = std::make_shared<Transcript>();
212 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
213
214 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
215 ECCVMVerifier verifier(verifier_transcript, proof);
216 auto eccvm_result = verifier.reduce_to_ipa_opening();
217
218 // Verify IPA
219 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
221 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
222
223 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
224}
225
226TEST_F(ECCVMTests, PointAtInfinity)
227{
229
230 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
231 ECCVMProver prover(builder, prover_transcript);
232 auto [proof, opening_claim] = prover.construct_proof();
233
234 auto ipa_transcript = std::make_shared<Transcript>();
235 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
236
237 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
238 ECCVMVerifier verifier(verifier_transcript, proof);
239 auto eccvm_result = verifier.reduce_to_ipa_opening();
240
241 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
243 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
244
245 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
246}
247
248TEST_F(ECCVMTests, ShortMonomialProverVerifies)
249{
251
252 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
253 ECCVMProver prover(builder, prover_transcript);
254 auto [proof, opening_claim] = prover.construct_proof();
255
256 EXPECT_EQ(proof.size(), ECCVMFlavor::PROOF_LENGTH);
257
258 auto ipa_transcript = std::make_shared<Transcript>();
259 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
260
261 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
262 ECCVMVerifier verifier(verifier_transcript, proof);
263 auto eccvm_result = verifier.reduce_to_ipa_opening();
264
265 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
267 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
268
269 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
270}
271
272namespace {
273// Build a length-2 edge input (one ProverUnivariate per entity), mirroring the translator equivalence test.
274ECCVMFlavor::ProverUnivariates<2> get_short_edge_input(bool random_inputs)
275{
277 FF value = 0;
278 for (auto& edge : result.get_all()) {
279 if (random_inputs) {
281 } else {
282 value += 1;
283 edge = bb::Univariate<FF, 2>({ value, value + 1 });
284 value += 1;
285 }
286 }
287 return result;
288}
289
290template <typename ShortRelation>
291auto accumulate_short_relation(const ECCVMFlavor::ProverUnivariates<2>& in,
292 const RelationParameters<FF>& params,
293 const FF& scaling_factor)
294{
295 typename ShortRelation::SumcheckTupleOfUnivariatesOverSubrelations accumulators{};
296 ShortRelation::accumulate(accumulators, in, params, scaling_factor);
297 return accumulators;
298}
299
300// Compare one short relation's subrelations against the full relation's subrelations starting at `Offset`. ECCVM
301// legacy relations over-provision every subrelation to a uniform length while the short relations declare the true
302// (smaller) per-subrelation degree, so each short subrelation is extended up to the legacy length before comparison.
303template <size_t Offset, typename FullTuple, typename ShortTuple, size_t... Js>
304void compare_subrelation_block(const FullTuple& full_acc, const ShortTuple& short_acc, std::index_sequence<Js...>)
305{
306 (
307 [&] {
308 constexpr size_t full_idx = Offset + Js;
309 constexpr size_t full_length = std::tuple_element_t<full_idx, FullTuple>::LENGTH;
310 EXPECT_EQ(std::get<Js>(short_acc).template extend_to<full_length>(), std::get<full_idx>(full_acc));
311 }(),
312 ...);
313}
314
315// Base case: every legacy subrelation must have been covered by exactly one short subrelation.
316template <size_t Offset, typename FullTuple> void compare_short_blocks(const FullTuple&)
317{
318 static_assert(Offset == std::tuple_size_v<FullTuple>,
319 "short relations must cover exactly the legacy relation's subrelations");
320}
321
322// Walk the short relations in flavor order, matching each against the corresponding contiguous slice of the full
323// relation's subrelations. Index-aligned equality also confirms the split short relations reproduce the legacy
324// global subrelation ordering (and hence the alpha batching).
325template <size_t Offset, typename FullTuple, typename ShortHead, typename... ShortTail>
326void compare_short_blocks(const FullTuple& full_acc, const ShortHead& head, const ShortTail&... tail)
327{
328 constexpr size_t head_size = std::tuple_size_v<ShortHead>;
329 compare_subrelation_block<Offset>(full_acc, head, std::make_index_sequence<head_size>{});
330 compare_short_blocks<Offset + head_size>(full_acc, tail...);
331}
332
333template <typename FullRelation, typename... ShortRelations>
334void expect_short_relations_match_full_edges(const ECCVMFlavor::ProverUnivariates<2>& in,
335 const RelationParameters<FF>& params,
336 const FF& scaling_factor)
337{
338 ECCVMFlavor::ExtendedEdges extended_edges;
339 for (auto [extended_edge, short_edge] : zip_view(extended_edges.get_all(), in.get_all())) {
340 extended_edge = short_edge.template extend_to<ECCVMFlavor::MAX_PARTIAL_RELATION_LENGTH>();
341 }
342
343 typename FullRelation::SumcheckTupleOfUnivariatesOverSubrelations full_acc{};
344 FullRelation::accumulate(full_acc, extended_edges, params, scaling_factor);
345
346 compare_short_blocks<0>(full_acc, accumulate_short_relation<ShortRelations>(in, params, scaling_factor)...);
347}
348} // namespace
349
350// Each ECCVM short relation (some of which split a single legacy relation across several short relations) must
351// produce, edge-for-edge, the same per-subrelation contributions as its legacy counterpart. This is the soundness
352// guard for the prover-only short path: the legacy verifier (native and recursive) only ever evaluates the full
353// relations, so a divergence here would make the short-prover's proof unverifiable.
354TEST_F(ECCVMTests, ShortMonomialRelationsMatchFullEdgeRelations)
355{
356 const auto run_test = [&](bool random_inputs) {
357 const auto input = get_short_edge_input(random_inputs);
358 const auto params = RelationParameters<FF>::get_random();
359 const FF scaling_factor = random_inputs ? FF::random_element() : FF(7);
360
361 expect_short_relations_match_full_edges<ECCVMTranscriptRelation<FF>,
364 input, params, scaling_factor);
365 expect_short_relations_match_full_edges<ECCVMPointTableRelation<FF>,
367 ECCVMPointTableShortRelation<FF>>(input, params, scaling_factor);
368 expect_short_relations_match_full_edges<ECCVMWnafRelation<FF>, ECCVMWnafShortRelation<FF>>(
369 input, params, scaling_factor);
370 expect_short_relations_match_full_edges<ECCVMMSMRelation<FF>,
374 ECCVMMSMShortRelation<FF>>(input, params, scaling_factor);
375 expect_short_relations_match_full_edges<ECCVMSetRelation<FF>, ECCVMSetShortRelation<FF>>(
376 input, params, scaling_factor);
377 expect_short_relations_match_full_edges<ECCVMLookupRelation<FF>, ECCVMLookupShortRelation<FF>>(
378 input, params, scaling_factor);
379 expect_short_relations_match_full_edges<ECCVMBoolsRelation<FF>,
381 ECCVMBoolsMsmShortRelation<FF>>(input, params, scaling_factor);
382 };
383
384 run_test(/*random_inputs=*/false);
385 run_test(/*random_inputs=*/true);
386}
387
388TEST_F(ECCVMTests, ScalarEdgeCase)
389{
390 using Curve = curve::BN254;
391 using G1 = Curve::Element;
392 using Fr = Curve::ScalarField;
393
395 G1 a = G1::one();
396
397 op_queue->mul_accumulate(a, Fr(uint256_t(1) << 128));
398 op_queue->eq_and_reset();
399 op_queue->merge();
400 add_hiding_op_for_test(op_queue);
401 ECCVMCircuitBuilder builder{ op_queue };
402
403 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
404 ECCVMProver prover(builder, prover_transcript);
405 auto [proof, opening_claim] = prover.construct_proof();
406
407 auto ipa_transcript = std::make_shared<Transcript>();
408 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
409
410 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
411 ECCVMVerifier verifier(verifier_transcript, proof);
412 auto eccvm_result = verifier.reduce_to_ipa_opening();
413
414 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
416 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
417
418 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
419}
427TEST_F(ECCVMTests, ProofLengthCheck)
428{
430
431 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
432 ECCVMProver prover(builder, prover_transcript);
433 auto [proof, opening_claim] = prover.construct_proof();
434 EXPECT_EQ(proof.size(), ECCVMFlavor::PROOF_LENGTH);
435}
436
437TEST_F(ECCVMTests, BaseCaseFixedSize)
438{
439 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
440 ECCVMProver prover = [&]() {
442 return ECCVMProver(builder, prover_transcript);
443 }();
444
445 auto [proof, opening_claim] = prover.construct_proof();
446
447 auto ipa_transcript = std::make_shared<Transcript>();
448 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
449
450 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
451 ECCVMVerifier verifier(verifier_transcript, proof);
452 auto eccvm_result = verifier.reduce_to_ipa_opening();
453
454 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
456 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
457
458 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
459}
460
461TEST_F(ECCVMTests, EqFailsFixedSize)
462{
464 // Tamper with the eq op such that the expected value is incorect
465 builder.op_queue->add_erroneous_equality_op_for_testing();
466 builder.op_queue->merge();
467
468 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
469 ECCVMProver prover(builder, prover_transcript);
470
471 auto [proof, opening_claim] = prover.construct_proof();
472
473 auto ipa_transcript = std::make_shared<Transcript>();
474 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
475
476 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
477 ECCVMVerifier verifier(verifier_transcript, proof);
478 auto eccvm_result = verifier.reduce_to_ipa_opening();
479
480 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
482 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
483
484 ASSERT_FALSE(ipa_verified && eccvm_result.reduction_succeeded);
485}
486
487TEST_F(ECCVMTests, CommittedSumcheck)
488{
489 using Flavor = ECCVMFlavor;
490 using ProvingKey = ECCVMFlavor::ProvingKey;
491 using FF = ECCVMFlavor::FF;
493 using ZKData = ZKSumcheckData<Flavor>;
494
495 bb::RelationParameters<FF> relation_parameters;
496 std::vector<FF> gate_challenges(CONST_ECCVM_LOG_N);
497
499 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
500 ECCVMProver prover(builder, prover_transcript);
502
503 // Prepare the inputs for the sumcheck prover:
504 // Compute and add beta to relation parameters
505 const FF alpha = FF::random_element();
506 complete_proving_key_for_test(relation_parameters, pk, gate_challenges);
507
508 // Clear the transcript
509 prover_transcript = std::make_shared<Transcript>();
510
511 // Run Sumcheck on the ECCVM Prover polynomials
513 SumcheckProver sumcheck_prover(pk->circuit_size,
514 pk->polynomials,
515 prover_transcript,
516 alpha,
517 gate_challenges,
518 relation_parameters,
519 CONST_ECCVM_LOG_N);
520
521 ZKData zk_sumcheck_data = ZKData(CONST_ECCVM_LOG_N, prover_transcript);
522 auto prover_output = sumcheck_prover.prove(zk_sumcheck_data);
523
524 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>(prover_transcript->export_proof());
525
526 // Execute Sumcheck Verifier
527 SumcheckVerifier<Flavor> sumcheck_verifier(verifier_transcript, alpha, CONST_ECCVM_LOG_N);
528 SumcheckOutput<ECCVMFlavor> verifier_output = sumcheck_verifier.verify(relation_parameters, gate_challenges);
529
530 // Evaluate prover's round univariates at corresponding challenges and compare them with the claimed evaluations
531 // computed by the verifier
532 for (size_t idx = 0; idx < CONST_ECCVM_LOG_N; idx++) {
533 FF true_eval_at_the_challenge = prover_output.round_univariates[idx].evaluate(prover_output.challenge[idx]);
534 FF verifier_eval_at_the_challenge = verifier_output.round_univariate_evaluations[idx][2];
535 EXPECT_TRUE(true_eval_at_the_challenge == verifier_eval_at_the_challenge);
536 }
537
538 // Check that the first sumcheck univariate is consistent with the claimed ZK Sumchek Sum
539 FF prover_target_sum = zk_sumcheck_data.libra_challenge * zk_sumcheck_data.libra_total_sum;
540
541 EXPECT_TRUE(prover_target_sum == verifier_output.round_univariate_evaluations[0][0] +
542 verifier_output.round_univariate_evaluations[0][1]);
543
544 EXPECT_TRUE(verifier_output.verified);
545}
546
562TEST_F(ECCVMTests, BaseInfinityForgedCoordinatesRejected)
563{
564 using Curve = curve::BN254;
565 using G1 = Curve::Element;
566 using Fr = Curve::ScalarField;
567
568 auto generators = Curve::Group::derive_generators("base_infinity_regression", 2);
569 G1 a = generators[0];
570 G1 b = generators[1]; // the point the attacker tries to smuggle in
573
574 // Honest vs forged results
575 G1 honest_result = a * x + b * y;
576 G1 forged_result = a * x;
577 ASSERT_NE(honest_result, forged_result) << "Need b*y != 0 for a meaningful attack";
578
579 // Build the circuit: mul(a, x), mul(infinity, y), eq_and_reset()
580 // The op queue honestly computes a*x + infinity*y = a*x. Eq point = a*x.
581 // The builder sets base_infinity=1 at the second mul row.
583 op_queue->mul_accumulate(a, x);
584 op_queue->mul_accumulate(Curve::Group::affine_point_at_infinity, y);
585 op_queue->eq_and_reset();
586 op_queue->merge();
587 add_hiding_op_for_test(op_queue);
588
589 ECCVMCircuitBuilder builder{ op_queue };
590
591 // Create prover (polynomials are built in the constructor)
592 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
593 ECCVMProver prover(builder, prover_transcript);
594
595 // Find the mul row with base_infinity=1
596 auto& polys = prover.key->polynomials;
597 const size_t num_rows = polys.get_polynomial_size();
598 size_t forged_row = 0;
599 for (size_t i = 0; i < num_rows; i++) {
600 if (polys.transcript_op[i] == FF(4) && polys.transcript_base_infinity[i] == FF(1)) {
601 forged_row = i;
602 break;
603 }
604 }
605 ASSERT_GT(forged_row, size_t(0)) << "Could not find infinity mul row";
606
607 // Replace transcript_Px/Py with valid on-curve point b.
608 // After this, the committed transcript says: mul(a,x), mul(b,y), eq(a*x)
609 // Honest result = a*x + b*y, but the proof will claim a*x.
610 auto b_affine = Curve::Group::affine_element(b);
611 polys.transcript_Px.at(forged_row) = b_affine.x;
612 polys.transcript_Py.at(forged_row) = b_affine.y;
613
614 // Generate proof from modified polynomials
615 auto [proof, opening_claim] = prover.construct_proof();
616
617 // Compute IPA opening proof
618 auto ipa_transcript = std::make_shared<Transcript>();
619 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
620
621 // Verify the ECCVM proof
622 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
623 ECCVMVerifier verifier(verifier_transcript, proof);
624 auto eccvm_result = verifier.reduce_to_ipa_opening();
625
626 // Verify the IPA proof
627 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
629 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
630
631 bool proof_verified = ipa_verified && eccvm_result.reduction_succeeded;
632
633 EXPECT_FALSE(proof_verified)
634 << "REGRESSION: Forged ECCVM proof must NOT verify after base_infinity coordinate constraints";
635}
636
644{
645 // Generate a circuit and its verification key (computed at runtime from the proving key)
647 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
648 ECCVMProver prover(builder, prover_transcript);
649 auto [proof, opening_claim] = prover.construct_proof();
650
651 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
652 ECCVMVerifier verifier(verifier_transcript, proof);
653
654 // Generate the default fixed VK
656 // Generate a VK from PK
657 ECCVMFlavor::VerificationKey vk_computed_by_prover = create_vk_from_proving_key(prover.key);
658
659 const auto& labels = bb::ECCVMFlavor::VerificationKey::get_labels();
660 size_t index = 0;
661 for (auto [vk_commitment, fixed_commitment] : zip_view(vk_computed_by_prover.get_all(), fixed_vk.get_all())) {
662 EXPECT_EQ(vk_commitment, fixed_commitment)
663 << "Mismatch between vk_commitment and fixed_commitment at label: " << labels[index];
664 ++index;
665 }
666
667 // Check that the fixed VK is equal to the generated VK
668 EXPECT_EQ(fixed_vk, vk_computed_by_prover);
669
670 // Verify that the hardcoded VK hash matches the computed hash
671 auto computed_hash = compute_eccvm_vk_hash();
672 auto hardcoded_hash = ECCVMHardcodedVKAndHash::vk_hash();
673 if (computed_hash != hardcoded_hash) {
674 info("VK hash mismatch! Update ECCVMHardcodedVKAndHash::vk_hash() with:");
675 info("0x", computed_hash);
676 }
677 EXPECT_EQ(computed_hash, hardcoded_hash) << "Hardcoded VK hash does not match computed hash";
678}
679
685TEST_F(ECCVMTests, WitnessPolynomialsMasked)
686{
689
690 // Every witness polynomial should have at least one non-zero masking value
691 auto check_masked = [](const auto& poly, const std::string& label) {
692 bool has_masking = false;
693 for (size_t j = 0; j < NUM_MASKED_ROWS; j++) {
694 has_masking |= !poly[NUM_ZERO_ROWS + j].is_zero();
695 }
696 EXPECT_TRUE(has_masking) << label << " should be masked but has all zeros in masking region";
697 };
698
700 for (auto [poly, label] : zip_view(polynomials.get_wires(), labels.get_wires())) {
701 check_masked(poly, label);
702 }
703 check_masked(polynomials.z_perm, "z_perm");
704 check_masked(polynomials.lookup_inverses, "lookup_inverses");
705}
706
714TEST_F(ECCVMTests, RepeatedCommitmentsIndicesCorrect)
715{
717 auto pk = std::make_shared<PK>(builder);
718 pk->commitment_key = ECCVMFlavor::CommitmentKey(pk->circuit_size);
719
720 auto unshifted = pk->polynomials.get_unshifted();
721 auto to_be_shifted = pk->polynomials.get_to_be_shifted();
722
723 constexpr auto repeated = ECCVMFlavor::REPEATED_COMMITMENTS;
724 ASSERT_EQ(to_be_shifted.size(), repeated.first.count);
725
726 // Build the commitment vector exactly as Shplemini does: [Q, unshifted..., to_be_shifted...]
727 const auto& ck = pk->commitment_key;
729 commitments.push_back(ECCVMFlavor::Commitment::one()); // dummy Q
730 for (auto& poly : unshifted) {
731 commitments.push_back(ck.commit(poly));
732 }
733 for (auto& poly : to_be_shifted) {
734 commitments.push_back(ck.commit(poly));
735 }
736
737 // Same offset logic as remove_repeated_commitments
738 constexpr size_t offset = ECCVMFlavor::HasZK ? 2 : 1;
739 for (size_t i = 0; i < repeated.first.count; i++) {
740 EXPECT_EQ(commitments[repeated.first.original_start + offset + i],
741 commitments[repeated.first.duplicate_start + offset + i])
742 << "REPEATED_COMMITMENTS commitment mismatch at index " << i;
743 }
744}
void SetUp() override
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
A base class labelling all entities (for instance, all of the polynomials used by the prover during s...
A container for commitment labels.
A container for the prover polynomials.
The proving key is responsible for storing the polynomials used by the prover.
static constexpr size_t ECCVM_FIXED_SIZE
static constexpr bool HasZK
typename Curve::ScalarField FF
typename Curve::BaseField BF
static constexpr size_t PROOF_LENGTH
bb::CommitmentKey< Curve > CommitmentKey
static constexpr RepeatedCommitmentsData REPEATED_COMMITMENTS
BaseTranscript< Codec, HashFunction > Transcript
static constexpr size_t TRACE_OFFSET
static std::vector< Commitment > get_all()
std::pair< Proof, OpeningClaim > construct_proof()
std::shared_ptr< ProvingKey > key
Unified ECCVM verifier class for both native and recursive verification.
ReductionResult reduce_to_ipa_opening()
Reduce the ECCVM proof to an IPA opening claim.
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.
IPA (inner product argument) commitment scheme class.
Definition ipa.hpp:86
A wrapper for Relations to expose methods used by the Sumcheck prover or verifier to add the contribu...
The implementation of the sumcheck Prover for statements of the form for multilinear polynomials .
Definition sumcheck.hpp:294
SumcheckOutput< Flavor > prove()
Non-ZK version: Compute round univariate, place it in transcript, compute challenge,...
Definition sumcheck.hpp:392
Implementation of the sumcheck Verifier for statements of the form for multilinear polynomials .
Definition sumcheck.hpp:813
SumcheckOutput< Flavor > verify(const bb::RelationParameters< FF > &relation_parameters, const std::vector< FF > &gate_challenges)
The Sumcheck verification method. First it extracts round univariate, checks sum (the sumcheck univar...
Definition sumcheck.hpp:868
A univariate polynomial represented by its values on {0, 1,..., domain_end - 1}.
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
typename Group::element Element
Definition grumpkin.hpp:63
#define info(...)
Definition log.hpp:93
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
void complete_proving_key_for_test(bb::RelationParameters< FF > &relation_parameters, std::shared_ptr< PK > &pk, std::vector< FF > &gate_challenges)
ECCVMFlavor::VerificationKey create_vk_from_proving_key(const std::shared_ptr< PK > &proving_key)
ECCVMFlavor::BF compute_eccvm_vk_hash()
ECCVMFlavor::FF FF
ECCVMCircuitBuilder generate_zero_circuit(numeric::RNG *engine=nullptr, bool zero_scalars=1)
ECCVMCircuitBuilder generate_circuit(numeric::RNG *engine=nullptr)
Adds operations in BN254 to the op_queue and then constructs and ECCVM circuit from the op_queue.
numeric::RNG & engine
ssize_t offset
Definition engine.cpp:62
void add_hiding_op_for_test(const std::shared_ptr< ECCOpQueue > &op_queue)
Set a hiding op on the op_queue for testing.
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
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:155
CommitmentKey< Curve > ck
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::AffineElement G1
Container for parameters used by the grand product (permutation, lookup) Honk relations.
static RelationParameters get_random()
Contains the evaluations of multilinear polynomials at the challenge point . These are computed by S...
std::vector< std::array< FF, 3 > > round_univariate_evaluations
This structure is created to contain various polynomials and constants required by ZK Sumcheck.
static field random_element(numeric::RNG *engine=nullptr) noexcept