Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
sumcheck.test.cpp
Go to the documentation of this file.
1#include "sumcheck.hpp"
4
9#include <gtest/gtest.h>
10
11using namespace bb;
12
13namespace {
14template <typename Flavor> class SumcheckTests : public ::testing::Test {
15 public:
16 using FF = typename Flavor::FF;
18 using SubrelationSeparators = Flavor::SubrelationSeparators;
19 using ZKData = ZKSumcheckData<Flavor>;
20
21 const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES;
22 static void SetUpTestSuite() { bb::srs::init_file_crs_factory(bb::srs::bb_crs_path()); }
23
24 Polynomial<FF> random_poly(size_t size)
25 {
26 auto poly = bb::Polynomial<FF>(size);
27 for (auto& coeff : poly.coeffs()) {
28 coeff = FF::random_element();
29 }
30 return poly;
31 }
32
33 ProverPolynomials construct_ultra_full_polynomials(auto& input_polynomials)
34 {
35 ProverPolynomials full_polynomials;
36 for (auto [full_poly, input_poly] : zip_view(full_polynomials.get_all(), input_polynomials)) {
37 full_poly = input_poly.share();
38 }
39 return full_polynomials;
40 }
41
42 void test_polynomial_normalization()
43 {
44 // TODO(#225)(Cody): We should not use real constants like this in the tests, at least not in so many of them.
45 const size_t multivariate_d(3);
46 const size_t multivariate_n(1 << multivariate_d);
47
48 // Randomly construct the prover polynomials that are input to Sumcheck.
49 // Note: ProverPolynomials are defined as spans so the polynomials they point to need to exist in memory.
50 std::vector<bb::Polynomial<FF>> random_polynomials(NUM_POLYNOMIALS);
51 for (auto& poly : random_polynomials) {
52 poly = random_poly(multivariate_n);
53 }
54 auto full_polynomials = construct_ultra_full_polynomials(random_polynomials);
55
56 auto transcript = Flavor::Transcript::prover_init_empty();
57
58 SubrelationSeparators alpha;
59 for (size_t idx = 0; idx < alpha.size(); idx++) {
60 alpha[idx] = transcript->template get_challenge<FF>("Sumcheck:alpha_" + std::to_string(idx));
61 }
62
63 std::vector<FF> gate_challenges(multivariate_d);
64 for (size_t idx = 0; idx < multivariate_d; idx++) {
65 gate_challenges[idx] =
66 transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
67 }
68
70 multivariate_n, full_polynomials, transcript, alpha, gate_challenges, {}, multivariate_d);
71
72 auto output = sumcheck.prove();
73
74 FF u_0 = output.challenge[0];
75 FF u_1 = output.challenge[1];
76 FF u_2 = output.challenge[2];
77
78 /* sumcheck.prove() terminates with sumcheck.multivariates.folded_polynoimals as an array such that
79 * sumcheck.multivariates.folded_polynoimals[i][0] is the evaluatioin of the i'th multivariate at the vector of
80 challenges u_i. What does this mean?
81
82 Here we show that if the multivariate is F(X0, X1, X2) defined as above, then what we get is F(u0, u1, u2) and
83 not, say F(u2, u1, u0). This is in accordance with Adrian's thesis (cf page 9).
84 */
85
86 // Get the values of the Lagrange basis polys L_i defined
87 // by: L_i(v) = 1 if i = v, 0 otherwise, for v from 0 to 7.
88 FF one{ 1 };
89 // clang-format off
90 FF l_0 = (one - u_0) * (one - u_1) * (one - u_2);
91 FF l_1 = (u_0) * (one - u_1) * (one - u_2);
92 FF l_2 = (one - u_0) * (u_1) * (one - u_2);
93 FF l_3 = (u_0) * (u_1) * (one - u_2);
94 FF l_4 = (one - u_0) * (one - u_1) * (u_2);
95 FF l_5 = (u_0) * (one - u_1) * (u_2);
96 FF l_6 = (one - u_0) * (u_1) * (u_2);
97 FF l_7 = (u_0) * (u_1) * (u_2);
98 // clang-format on
99 FF hand_computed_value;
100 for (auto [full_poly, partial_eval_poly] :
101 zip_view(full_polynomials.get_all(), sumcheck.partially_evaluated_polynomials.get_all())) {
102 // full_polynomials[0][0] = w_l[0], full_polynomials[1][1] = w_r[1], and so on.
103 hand_computed_value = l_0 * full_poly[0] + l_1 * full_poly[1] + l_2 * full_poly[2] + l_3 * full_poly[3] +
104 l_4 * full_poly[4] + l_5 * full_poly[5] + l_6 * full_poly[6] + l_7 * full_poly[7];
105 EXPECT_EQ(hand_computed_value, partial_eval_poly[0]);
106 }
107
108 // We can also check the correctness of the multilinear evaluations produced by Sumcheck by directly evaluating
109 // the full polynomials at challenge u via the evaluate_mle() function
110 std::vector<FF> u_challenge = { u_0, u_1, u_2 };
111 for (auto [full_poly, claimed_eval] :
112 zip_view(full_polynomials.get_all(), output.claimed_evaluations.get_all())) {
113 Polynomial<FF> poly(full_poly);
114 auto v_expected = poly.evaluate_mle(u_challenge);
115 EXPECT_EQ(v_expected, claimed_eval);
116 }
117 }
118
119 void test_prover()
120 {
121 const size_t multivariate_d(2);
122 const size_t multivariate_n(1 << multivariate_d);
123
124 // Randomly construct the prover polynomials that are input to Sumcheck.
125 // Note: ProverPolynomials are defined as spans so the polynomials they point to need to exist in memory.
126 std::vector<Polynomial<FF>> random_polynomials(NUM_POLYNOMIALS);
127 for (auto& poly : random_polynomials) {
128 poly = random_poly(multivariate_n);
129 }
130 auto full_polynomials = construct_ultra_full_polynomials(random_polynomials);
131
132 auto transcript = Flavor::Transcript::prover_init_empty();
133
134 SubrelationSeparators alpha;
135 for (size_t idx = 0; idx < alpha.size(); idx++) {
136 alpha[idx] = transcript->template get_challenge<FF>("Sumcheck:alpha_" + std::to_string(idx));
137 }
138
139 std::vector<FF> gate_challenges(multivariate_d);
140 for (size_t idx = 0; idx < gate_challenges.size(); idx++) {
141 gate_challenges[idx] =
142 transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
143 }
144
145 SumcheckProver<Flavor> sumcheck(
146 multivariate_n, full_polynomials, transcript, alpha, gate_challenges, {}, CONST_PROOF_SIZE_LOG_N);
147
149
150 if constexpr (Flavor::HasZK) {
151 ZKData zk_sumcheck_data = ZKData(multivariate_d, transcript);
152 output = sumcheck.prove(zk_sumcheck_data);
153 } else {
154 output = sumcheck.prove();
155 }
156 FF u_0 = output.challenge[0];
157 FF u_1 = output.challenge[1];
158 std::vector<FF> expected_values;
159 for (auto& polynomial_ptr : full_polynomials.get_all()) {
160 auto& polynomial = polynomial_ptr;
161 // using knowledge of inputs here to derive the evaluation
162 FF expected_lo = polynomial[0] * (FF(1) - u_0) + polynomial[1] * u_0;
163 expected_lo *= (FF(1) - u_1);
164 FF expected_hi = polynomial[2] * (FF(1) - u_0) + polynomial[3] * u_0;
165 expected_hi *= u_1;
166 expected_values.emplace_back(expected_lo + expected_hi);
167 }
168
169 for (auto [eval, expected] : zip_view(output.claimed_evaluations.get_all(), expected_values)) {
170 eval = expected;
171 }
172 }
173
174 // TODO(#225): make the inputs to this test more interesting, e.g. non-trivial permutations
175 void test_prover_verifier_flow()
176 {
177 const size_t multivariate_d(3);
178 const size_t multivariate_n(1 << multivariate_d);
179
180 const size_t virtual_log_n = 6;
181 // Construct prover polynomials where each is the zero polynomial.
182 // Note: ProverPolynomials are defined as spans so the polynomials they point to need to exist in memory.
183 std::vector<Polynomial<FF>> zero_polynomials(NUM_POLYNOMIALS);
184 for (auto& poly : zero_polynomials) {
185 poly = bb::Polynomial<FF>(multivariate_n);
186 }
187 auto full_polynomials = construct_ultra_full_polynomials(zero_polynomials);
188
189 // Add some non-trivial values to certain polynomials so that the arithmetic relation will have non-trivial
190 // contribution. Note: since all other polynomials are set to 0, all other relations are trivially
191 // satisfied.
192 std::array<FF, multivariate_n> w_l = { 0, 1, 2, 0 };
193 std::array<FF, multivariate_n> w_r = { 0, 1, 2, 0 };
194 std::array<FF, multivariate_n> w_o = { 0, 2, 4, 0 };
195 std::array<FF, multivariate_n> w_4 = { 0, 0, 0, 0 };
196 std::array<FF, multivariate_n> q_m = { 0, 0, 1, 0 };
197 std::array<FF, multivariate_n> q_l = { 0, 1, 0, 0 };
198 std::array<FF, multivariate_n> q_r = { 0, 1, 0, 0 };
199 std::array<FF, multivariate_n> q_o = { 0, -1, -1, 0 };
200 std::array<FF, multivariate_n> q_c = { 0, 0, 0, 0 };
201 std::array<FF, multivariate_n> q_arith = { 0, 1, 1, 0 };
202 // Setting all of these to 0 ensures the GrandProductRelation is satisfied
203
204 // For ZK Flavors: add some randomness to ProverPolynomials
205 if constexpr (Flavor::HasZK) {
206 w_l[7] = FF::random_element();
207 w_r[6] = FF::random_element();
208 w_4[6] = FF::random_element();
209 auto z_1 = FF::random_element();
210 auto z_2 = FF::random_element();
211 auto r = FF::random_element();
212
213 std::array<FF, multivariate_n> z_perm = { 0, 0, 0, 0, 0, 0, z_1, z_2 };
214 std::array<FF, multivariate_n> lookup_inverses = { 0, 0, 0, 0, 0, r, r * r, r * r * r };
215 // To avoid triggering the skipping mechanism in LogDerivativeRelation, we have to ensure
216 // that the condition (in.q_lookup.is_zero() && in.lookup_read_counts.is_zero()) is not satisfied in the
217 // blinded rows
218 std::array<FF, multivariate_n> skipping_disabler = { 0, 0, 0, 0, 0, 1, 1, 1 };
219 full_polynomials.z_perm = bb::Polynomial<FF>(z_perm);
220 full_polynomials.lookup_inverses = bb::Polynomial<FF>(lookup_inverses);
221 full_polynomials.lookup_read_counts = bb::Polynomial<FF>(skipping_disabler);
223 std::array<FF, multivariate_n> return_data_inverses = { 0, 0, 0, 0, 0, 0, r * r, -r };
224 full_polynomials.return_data_inverses = bb::Polynomial<FF>(return_data_inverses);
225
226 // To avoid triggering the skipping mechanism in DatabusLookupRelation, we have to ensure that the
227 // condition (in.calldata_read_counts.is_zero() && in.secondary_calldata_read_counts.is_zero() &&
228 // in.return_data_read_counts.is_zero()) is not satisfied in the blinded rows
229 full_polynomials.calldata_read_counts = bb::Polynomial<FF>(skipping_disabler);
230 }
231 }
232 full_polynomials.w_l = bb::Polynomial<FF>(w_l);
233 full_polynomials.w_r = bb::Polynomial<FF>(w_r);
234 full_polynomials.w_o = bb::Polynomial<FF>(w_o);
235 full_polynomials.w_4 = bb::Polynomial<FF>(w_4);
236 full_polynomials.q_m = bb::Polynomial<FF>(q_m);
237 full_polynomials.q_l = bb::Polynomial<FF>(q_l);
238 full_polynomials.q_r = bb::Polynomial<FF>(q_r);
239 full_polynomials.q_o = bb::Polynomial<FF>(q_o);
240 full_polynomials.q_c = bb::Polynomial<FF>(q_c);
241 full_polynomials.q_arith = bb::Polynomial<FF>(q_arith);
242
243 // Set aribitrary random relation parameters
244 RelationParameters<FF> relation_parameters{
245 .beta = FF::random_element(),
246 .gamma = FF::random_element(),
247 .public_input_delta = FF::one(),
248 };
249 auto prover_transcript = Flavor::Transcript::prover_init_empty();
250 SubrelationSeparators prover_alpha{ 1 };
251 for (size_t idx = 1; idx < prover_alpha.size(); idx++) {
252 prover_alpha[idx] = prover_transcript->template get_challenge<FF>("Sumcheck:alpha_" + std::to_string(idx));
253 }
254
255 std::vector<FF> prover_gate_challenges(virtual_log_n);
256 for (size_t idx = 0; idx < virtual_log_n; idx++) {
257 prover_gate_challenges[idx] =
258 prover_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
259 }
260
261 SumcheckProver<Flavor> sumcheck_prover(multivariate_n,
262 full_polynomials,
263 prover_transcript,
264 prover_alpha,
265 prover_gate_challenges,
266 relation_parameters,
267 virtual_log_n);
268
270 if constexpr (Flavor::HasZK) {
271 ZKData zk_sumcheck_data = ZKData(multivariate_d, prover_transcript);
272 output = sumcheck_prover.prove(zk_sumcheck_data);
273 } else {
274 output = sumcheck_prover.prove();
275 }
276
277 auto verifier_transcript = Flavor::Transcript::verifier_init_empty(prover_transcript);
278
279 SubrelationSeparators verifier_alpha{ 1 };
280 for (size_t idx = 1; idx < verifier_alpha.size(); idx++) {
281 verifier_alpha[idx] =
282 verifier_transcript->template get_challenge<FF>("Sumcheck:alpha_" + std::to_string(idx));
283 }
284 auto sumcheck_verifier = SumcheckVerifier<Flavor>(verifier_transcript, verifier_alpha, virtual_log_n);
285
286 std::vector<FF> verifier_gate_challenges(virtual_log_n);
287 for (size_t idx = 0; idx < virtual_log_n; idx++) {
288 verifier_gate_challenges[idx] =
289 verifier_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
290 }
291
292 std::vector<FF> padding_indicator_array(virtual_log_n, 1);
293 if constexpr (Flavor::HasZK) {
294 for (size_t idx = 0; idx < virtual_log_n; idx++) {
295 padding_indicator_array[idx] = (idx < multivariate_d) ? FF{ 1 } : FF{ 0 };
296 }
297 }
298
299 auto verifier_output =
300 sumcheck_verifier.verify(relation_parameters, verifier_gate_challenges, padding_indicator_array);
301
302 auto verified = verifier_output.verified;
303
304 EXPECT_EQ(verified, true);
305 };
306
307 void test_failure_prover_verifier_flow()
308 {
309 // Since the last 4 rows in ZK Flavors are disabled, we extend an invalid circuit of size 4 to size 8 by padding
310 // with 0.
311 const size_t multivariate_d(3);
312 const size_t multivariate_n(1 << multivariate_d);
313
314 // Construct prover polynomials where each is the zero polynomial.
315 // Note: ProverPolynomials are defined as spans so the polynomials they point to need to exist in memory.
316 std::vector<Polynomial<FF>> zero_polynomials(NUM_POLYNOMIALS);
317 for (auto& poly : zero_polynomials) {
318 poly = bb::Polynomial<FF>(multivariate_n);
319 }
320 auto full_polynomials = construct_ultra_full_polynomials(zero_polynomials);
321
322 // Add some non-trivial values to certain polynomials so that the arithmetic relation will have non-trivial
323 // contribution. Note: since all other polynomials are set to 0, all other relations are trivially
324 // satisfied.
326 w_l = { 0, 0, 2, 0 }; // this witness value makes the circuit from previous test invalid
327 std::array<FF, multivariate_n> w_r = { 0, 1, 2, 0 };
328 std::array<FF, multivariate_n> w_o = { 0, 2, 4, 0 };
329 std::array<FF, multivariate_n> w_4 = { 0, 0, 0, 0 };
330 std::array<FF, multivariate_n> q_m = { 0, 0, 1, 0 };
331 std::array<FF, multivariate_n> q_l = { 0, 1, 0, 0 };
332 std::array<FF, multivariate_n> q_r = { 0, 1, 0, 0 };
333 std::array<FF, multivariate_n> q_o = { 0, -1, -1, 0 };
334 std::array<FF, multivariate_n> q_c = { 0, 0, 0, 0 };
335 std::array<FF, multivariate_n> q_arith = { 0, 1, 1, 0 };
336 // Setting all of these to 0 ensures the GrandProductRelation is satisfied
337
338 full_polynomials.w_l = bb::Polynomial<FF>(w_l);
339 full_polynomials.w_r = bb::Polynomial<FF>(w_r);
340 full_polynomials.w_o = bb::Polynomial<FF>(w_o);
341 full_polynomials.w_4 = bb::Polynomial<FF>(w_4);
342 full_polynomials.q_m = bb::Polynomial<FF>(q_m);
343 full_polynomials.q_l = bb::Polynomial<FF>(q_l);
344 full_polynomials.q_r = bb::Polynomial<FF>(q_r);
345 full_polynomials.q_o = bb::Polynomial<FF>(q_o);
346 full_polynomials.q_c = bb::Polynomial<FF>(q_c);
347 full_polynomials.q_arith = bb::Polynomial<FF>(q_arith);
348
349 // Set aribitrary random relation parameters
350 RelationParameters<FF> relation_parameters{
351 .beta = FF::random_element(),
352 .gamma = FF::random_element(),
353 .public_input_delta = FF::one(),
354 };
355 auto prover_transcript = Flavor::Transcript::prover_init_empty();
356 SubrelationSeparators prover_alpha;
357 for (size_t idx = 0; idx < prover_alpha.size(); idx++) {
358 prover_alpha[idx] = prover_transcript->template get_challenge<FF>("Sumcheck:alpha_" + std::to_string(idx));
359 }
360 std::vector<FF> prover_gate_challenges(multivariate_d);
361 for (size_t idx = 0; idx < multivariate_d; idx++) {
362 prover_gate_challenges[idx] =
363 prover_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
364 }
365
366 SumcheckProver<Flavor> sumcheck_prover(multivariate_n,
367 full_polynomials,
368 prover_transcript,
369 prover_alpha,
370 prover_gate_challenges,
371 relation_parameters,
372 multivariate_d);
373
375 if constexpr (Flavor::HasZK) {
376 // construct libra masking polynomials and compute auxiliary data
377 ZKData zk_sumcheck_data = ZKData(multivariate_d, prover_transcript);
378 output = sumcheck_prover.prove(zk_sumcheck_data);
379 } else {
380 output = sumcheck_prover.prove();
381 }
382
383 auto verifier_transcript = Flavor::Transcript::verifier_init_empty(prover_transcript);
384
385 SubrelationSeparators verifier_alpha;
386 for (size_t idx = 0; idx < verifier_alpha.size(); idx++) {
387 verifier_alpha[idx] =
388 verifier_transcript->template get_challenge<FF>("Sumcheck:alpha_" + std::to_string(idx));
389 }
390 SumcheckVerifier<Flavor> sumcheck_verifier(verifier_transcript, verifier_alpha, multivariate_d);
391
392 std::vector<FF> verifier_gate_challenges(multivariate_d);
393 for (size_t idx = 0; idx < multivariate_d; idx++) {
394 verifier_gate_challenges[idx] =
395 verifier_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
396 }
397
398 std::vector<FF> padding_indicator_array(multivariate_d);
399 std::ranges::fill(padding_indicator_array, FF{ 1 });
400 auto verifier_output =
401 sumcheck_verifier.verify(relation_parameters, verifier_gate_challenges, padding_indicator_array);
402
403 auto verified = verifier_output.verified;
404
405 EXPECT_EQ(verified, false);
406 };
407};
408
409// Define the FlavorTypes
410using FlavorTypes = testing::Types<UltraFlavor,
414#ifdef STARKNET_GARAGA_FLAVORS
415 UltraStarknetFlavor,
416 UltraStarknetZKFlavor,
417#endif
420
421TYPED_TEST_SUITE(SumcheckTests, FlavorTypes);
422
423TYPED_TEST(SumcheckTests, PolynomialNormalization)
424{
426 this->test_polynomial_normalization();
427 } else {
428 GTEST_SKIP() << "Skipping test for ZK-enabled flavors";
429 }
430}
431// Test the prover
432TYPED_TEST(SumcheckTests, Prover)
433{
434 this->test_prover();
435}
436// Tests the prover-verifier flow
437TYPED_TEST(SumcheckTests, ProverAndVerifierSimple)
438{
439 this->test_prover_verifier_flow();
440}
441// This tests is fed an invalid circuit and checks that the verifier would output false.
442TYPED_TEST(SumcheckTests, ProverAndVerifierSimpleFailure)
443{
444 this->test_failure_prover_verifier_flow();
445}
446} // namespace
A container for the prover polynomials.
Curve::ScalarField FF
std::array< FF, NUM_SUBRELATIONS - 1 > SubrelationSeparators
static constexpr size_t NUM_ALL_ENTITIES
static constexpr bool HasZK
Child class of MegaFlavor that runs with ZK Sumcheck. See more in Sumcheck Outline.
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
The implementation of the sumcheck Prover for statements of the form for multilinear polynomials .
Definition sumcheck.hpp:123
SumcheckOutput< Flavor > prove()
Non-ZK version: Compute round univariate, place it in transcript, compute challenge,...
Definition sumcheck.hpp:231
Implementation of the sumcheck Verifier for statements of the form for multilinear polynomials .
Definition sumcheck.hpp:645
Child class of UltraFlavor that runs with ZK Sumcheck.
typename ECCVMFlavor::ProverPolynomials ProverPolynomials
testing::Types< MegaFlavor, UltraFlavor, UltraZKFlavor, UltraRollupFlavor > FlavorTypes
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
typename Flavor::FF FF
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
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.
Contains the evaluations of multilinear polynomials at the challenge point . These are computed by S...
This structure is created to contain various polynomials and constants required by ZK Sumcheck.