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
19
20using namespace bb;
27
28// Test helper: Create a VK by committing to proving key polynomials (for comparing with fixed VK)
30{
32 // Overwrite fixed commitments with computed commitments from the proving key
33 for (auto [polynomial, commitment] : zip_view(proving_key->polynomials.get_precomputed(), vk.get_all())) {
34 commitment = proving_key->commitment_key.commit(polynomial);
35 }
36 return vk;
37}
38
39// Compute VK hash from fixed commitments (for test verification that vk_hash() is correct)
41{
43 // Serialize commitments using the Codec
44 for (const auto& commitment : ECCVMHardcodedVKAndHash::get_all()) {
45 auto frs = ECCVMFlavor::Codec::serialize_to_fields(commitment);
46 for (const auto& fr : frs) {
47 elements.push_back(fr);
48 }
49 }
50 return ECCVMFlavor::HashFunction::hash(elements);
51}
52
53class ECCVMTests : public ::testing::Test {
54 protected:
56};
57namespace {
59} // namespace
60
68{
69 using Curve = curve::BN254;
70 using G1 = Curve::Element;
71 using Fr = Curve::ScalarField;
72
74 G1 a = G1::random_element(engine);
75 G1 b = G1::random_element(engine);
76 G1 c = G1::random_element(engine);
79
80 op_queue->add_accumulate(a);
81 op_queue->mul_accumulate(a, x);
82 op_queue->mul_accumulate(b, x);
83 op_queue->mul_accumulate(b, y);
84 op_queue->add_accumulate(a);
85 op_queue->mul_accumulate(b, x);
86 op_queue->eq_and_reset();
87 op_queue->add_accumulate(c);
88 op_queue->mul_accumulate(a, x);
89 op_queue->mul_accumulate(b, x);
90 op_queue->eq_and_reset();
91 op_queue->mul_accumulate(a, x);
92 op_queue->mul_accumulate(b, x);
93 op_queue->mul_accumulate(c, x);
94 op_queue->merge();
95 add_hiding_op_for_test(op_queue);
96 ECCVMCircuitBuilder builder{ op_queue };
97 return builder;
98}
99
100// returns a CircuitBuilder consisting of mul_add ops of the following form: either 0*g, for a group element, or
101// x * e, where x is a scalar and e is the identity element of the group.
102ECCVMCircuitBuilder generate_zero_circuit([[maybe_unused]] numeric::RNG* engine = nullptr, bool zero_scalars = 1)
103{
104 using Curve = curve::BN254;
105 using G1 = Curve::Element;
106 using Fr = Curve::ScalarField;
107
109
110 if (!zero_scalars) {
111 for (auto i = 0; i < 8; i++) {
113 op_queue->mul_accumulate(Curve::Group::affine_point_at_infinity, x);
114 }
115 } else {
116 for (auto i = 0; i < 8; i++) {
117 G1 g = G1::random_element(engine);
118 op_queue->mul_accumulate(g, 0);
119 }
120 }
121 op_queue->merge();
122 add_hiding_op_for_test(op_queue);
123
124 ECCVMCircuitBuilder builder{ op_queue };
125 return builder;
126}
127
130 std::vector<FF>& gate_challenges)
131{
132 // Prepare the inputs for the sumcheck prover:
133 // Compute and add beta to relation parameters
134 const FF beta = FF::random_element();
135 const FF gamma = FF::random_element();
136 const FF beta_sqr = beta * beta;
137 relation_parameters.gamma = gamma;
138 relation_parameters.beta = beta;
139 relation_parameters.beta_sqr = beta_sqr;
140 relation_parameters.beta_cube = beta_sqr * beta;
141 relation_parameters.eccvm_set_permutation_delta =
142 gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr);
143 relation_parameters.eccvm_set_permutation_delta = relation_parameters.eccvm_set_permutation_delta.invert();
144
145 const size_t unmasked_witness_size = pk->circuit_size - NUM_DISABLED_ROWS_IN_SUMCHECK;
146 // Compute z_perm and inverse polynomial for our logarithmic-derivative lookup method
147 compute_logderivative_inverse<FF, ECCVMFlavor::LookupRelation>(
148 pk->polynomials, relation_parameters, unmasked_witness_size);
149 compute_grand_products<ECCVMFlavor>(pk->polynomials, relation_parameters, unmasked_witness_size);
150
151 // Generate gate challenges
152 for (size_t idx = 0; idx < CONST_ECCVM_LOG_N; idx++) {
153 gate_challenges[idx] = FF::random_element();
154 }
155}
156TEST_F(ECCVMTests, ZeroesCoefficients)
157{
159
160 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
161 ECCVMProver prover(builder, prover_transcript);
162 auto [proof, opening_claim] = prover.construct_proof();
163
164 // Compute IPA proof
165 auto ipa_transcript = std::make_shared<Transcript>();
166 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
167
168 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
169 ECCVMVerifier verifier(verifier_transcript, proof);
170 auto eccvm_result = verifier.reduce_to_ipa_opening();
171
172 // Verify IPA
173 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
175 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
176
177 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
178}
179// Calling get_eccvm_ops without a hiding op should throw
180TEST_F(ECCVMTests, MissingHidingOpThrows)
181{
183 // get_eccvm_ops() requires a hiding op to have been set; throws "Hiding op must be set before calling
184 // get_eccvm_ops()"
185 EXPECT_THROW(op_queue->get_eccvm_ops(), std::runtime_error);
186}
187
188// Note that `NullOpQueue` is somewhat misleading, as we add a hiding operation.
189TEST_F(ECCVMTests, NullOpQUeue)
190{
192 add_hiding_op_for_test(op_queue);
193 ECCVMCircuitBuilder builder{ op_queue };
194 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
195 ECCVMProver prover(builder, prover_transcript);
196 auto [proof, opening_claim] = prover.construct_proof();
197
198 // Compute IPA proof
199 auto ipa_transcript = std::make_shared<Transcript>();
200 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
201
202 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
203 ECCVMVerifier verifier(verifier_transcript, proof);
204 auto eccvm_result = verifier.reduce_to_ipa_opening();
205
206 // Verify IPA
207 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
209 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
210
211 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
212}
213
214TEST_F(ECCVMTests, PointAtInfinity)
215{
217
218 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
219 ECCVMProver prover(builder, prover_transcript);
220 auto [proof, opening_claim] = prover.construct_proof();
221
222 auto ipa_transcript = std::make_shared<Transcript>();
223 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
224
225 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
226 ECCVMVerifier verifier(verifier_transcript, proof);
227 auto eccvm_result = verifier.reduce_to_ipa_opening();
228
229 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
231 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
232
233 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
234}
235TEST_F(ECCVMTests, ScalarEdgeCase)
236{
237 using Curve = curve::BN254;
238 using G1 = Curve::Element;
239 using Fr = Curve::ScalarField;
240
242 G1 a = G1::one();
243
244 op_queue->mul_accumulate(a, Fr(uint256_t(1) << 128));
245 op_queue->eq_and_reset();
246 op_queue->merge();
247 add_hiding_op_for_test(op_queue);
248 ECCVMCircuitBuilder builder{ op_queue };
249
250 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
251 ECCVMProver prover(builder, prover_transcript);
252 auto [proof, opening_claim] = prover.construct_proof();
253
254 auto ipa_transcript = std::make_shared<Transcript>();
255 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
256
257 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
258 ECCVMVerifier verifier(verifier_transcript, proof);
259 auto eccvm_result = verifier.reduce_to_ipa_opening();
260
261 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
263 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
264
265 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
266}
274TEST_F(ECCVMTests, ProofLengthCheck)
275{
277
278 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
279 ECCVMProver prover(builder, prover_transcript);
280 auto [proof, opening_claim] = prover.construct_proof();
281 EXPECT_EQ(proof.size(), ECCVMFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS);
282}
283
284TEST_F(ECCVMTests, BaseCaseFixedSize)
285{
287
288 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
289 ECCVMProver prover(builder, prover_transcript);
290 auto [proof, opening_claim] = prover.construct_proof();
291
292 auto ipa_transcript = std::make_shared<Transcript>();
293 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
294
295 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
296 ECCVMVerifier verifier(verifier_transcript, proof);
297 auto eccvm_result = verifier.reduce_to_ipa_opening();
298
299 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
301 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
302
303 ASSERT_TRUE(ipa_verified && eccvm_result.reduction_succeeded);
304}
305
306TEST_F(ECCVMTests, EqFailsFixedSize)
307{
309 // Tamper with the eq op such that the expected value is incorect
310 builder.op_queue->add_erroneous_equality_op_for_testing();
311 builder.op_queue->merge();
312
313 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
314 ECCVMProver prover(builder, prover_transcript);
315
316 auto [proof, opening_claim] = prover.construct_proof();
317
318 auto ipa_transcript = std::make_shared<Transcript>();
319 PCS::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
320
321 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
322 ECCVMVerifier verifier(verifier_transcript, proof);
323 auto eccvm_result = verifier.reduce_to_ipa_opening();
324
325 auto ipa_verifier_transcript = std::make_shared<Transcript>(ipa_transcript->export_proof());
327 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, eccvm_result.ipa_claim, ipa_verifier_transcript);
328
329 ASSERT_FALSE(ipa_verified && eccvm_result.reduction_succeeded);
330}
331
332TEST_F(ECCVMTests, CommittedSumcheck)
333{
334 using Flavor = ECCVMFlavor;
335 using ProvingKey = ECCVMFlavor::ProvingKey;
336 using FF = ECCVMFlavor::FF;
338 using ZKData = ZKSumcheckData<Flavor>;
339
340 bb::RelationParameters<FF> relation_parameters;
341 std::vector<FF> gate_challenges(CONST_ECCVM_LOG_N);
342
344 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
345 ECCVMProver prover(builder, prover_transcript);
347
348 // Prepare the inputs for the sumcheck prover:
349 // Compute and add beta to relation parameters
350 const FF alpha = FF::random_element();
351 complete_proving_key_for_test(relation_parameters, pk, gate_challenges);
352
353 // Clear the transcript
354 prover_transcript = std::make_shared<Transcript>();
355
356 // Run Sumcheck on the ECCVM Prover polynomials
358 SumcheckProver sumcheck_prover(pk->circuit_size,
359 pk->polynomials,
360 prover_transcript,
361 alpha,
362 gate_challenges,
363 relation_parameters,
364 CONST_ECCVM_LOG_N);
365
366 ZKData zk_sumcheck_data = ZKData(CONST_ECCVM_LOG_N, prover_transcript);
367
368 auto prover_output = sumcheck_prover.prove(zk_sumcheck_data);
369
370 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>(prover_transcript->export_proof());
371
372 // Execute Sumcheck Verifier
373 SumcheckVerifier<Flavor> sumcheck_verifier(verifier_transcript, alpha, CONST_ECCVM_LOG_N);
374 std::vector<FF> padding_indicator_array(CONST_ECCVM_LOG_N, FF(1));
375 SumcheckOutput<ECCVMFlavor> verifier_output =
376 sumcheck_verifier.verify(relation_parameters, gate_challenges, padding_indicator_array);
377
378 // Evaluate prover's round univariates at corresponding challenges and compare them with the claimed evaluations
379 // computed by the verifier
380 for (size_t idx = 0; idx < CONST_ECCVM_LOG_N; idx++) {
381 FF true_eval_at_the_challenge = prover_output.round_univariates[idx].evaluate(prover_output.challenge[idx]);
382 FF verifier_eval_at_the_challenge = verifier_output.round_univariate_evaluations[idx][2];
383 EXPECT_TRUE(true_eval_at_the_challenge == verifier_eval_at_the_challenge);
384 }
385
386 // Check that the first sumcheck univariate is consistent with the claimed ZK Sumchek Sum
387 FF prover_target_sum = zk_sumcheck_data.libra_challenge * zk_sumcheck_data.libra_total_sum;
388
389 EXPECT_TRUE(prover_target_sum == verifier_output.round_univariate_evaluations[0][0] +
390 verifier_output.round_univariate_evaluations[0][1]);
391
392 EXPECT_TRUE(verifier_output.verified);
393}
394
402{
403 // Generate a circuit and its verification key (computed at runtime from the proving key)
405 std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
406 ECCVMProver prover(builder, prover_transcript);
407 auto [proof, opening_claim] = prover.construct_proof();
408
409 std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
410 ECCVMVerifier verifier(verifier_transcript, proof);
411
412 // Generate the default fixed VK
414 // Generate a VK from PK
415 ECCVMFlavor::VerificationKey vk_computed_by_prover = create_vk_from_proving_key(prover.key);
416
417 const auto& labels = bb::ECCVMFlavor::VerificationKey::get_labels();
418 size_t index = 0;
419 for (auto [vk_commitment, fixed_commitment] : zip_view(vk_computed_by_prover.get_all(), fixed_vk.get_all())) {
420 EXPECT_EQ(vk_commitment, fixed_commitment)
421 << "Mismatch between vk_commitment and fixed_commitment at label: " << labels[index];
422 ++index;
423 }
424
425 // Check that the fixed VK is equal to the generated VK
426 EXPECT_EQ(fixed_vk, vk_computed_by_prover);
427
428 // Verify that the hardcoded VK hash matches the computed hash
429 auto computed_hash = compute_eccvm_vk_hash();
430 auto hardcoded_hash = ECCVMHardcodedVKAndHash::vk_hash();
431 if (computed_hash != hardcoded_hash) {
432 info("VK hash mismatch! Update ECCVMHardcodedVKAndHash::vk_hash() with:");
433 info("0x", computed_hash);
434 }
435 EXPECT_EQ(computed_hash, hardcoded_hash) << "Hardcoded VK hash does not match computed hash";
436}
void SetUp() override
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
The proving key is responsible for storing the polynomials used by the prover.
static constexpr size_t ECCVM_FIXED_SIZE
typename Curve::ScalarField FF
typename Curve::BaseField BF
static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS
BaseTranscript< Codec, HashFunction > Transcript
static constexpr 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).
Definition flavor.hpp:136
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:92
The implementation of the sumcheck Prover for statements of the form for multilinear polynomials .
Definition sumcheck.hpp:289
SumcheckOutput< Flavor > prove()
Non-ZK version: Compute round univariate, place it in transcript, compute challenge,...
Definition sumcheck.hpp:387
Implementation of the sumcheck Verifier for statements of the form for multilinear polynomials .
Definition sumcheck.hpp:719
SumcheckOutput< Flavor > verify(const bb::RelationParameters< FF > &relation_parameters, const std::vector< FF > &gate_challenges, const std::vector< FF > &padding_indicator_array)
The Sumcheck verification method. First it extracts round univariate, checks sum (the sumcheck univar...
Definition sumcheck.hpp:777
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:62
#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
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:190
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:142
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::ScalarField Fr
Curve::AffineElement G1
Container for parameters used by the grand product (permutation, lookup) Honk relations.
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