Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
row_disabling_polynomial.test.cpp
Go to the documentation of this file.
6
7#include <gtest/gtest.h>
8
9using namespace bb;
10
15template <typename Flavor> class RowDisablingPolynomialTest : public ::testing::Test {
16 public:
17 using FF = typename Flavor::FF;
22
30
34 static SumcheckSetup create_sumcheck_setup(size_t multivariate_d)
35 {
36 auto transcript = Flavor::Transcript::test_prover_init_empty();
37 FF alpha = transcript->template get_challenge<FF>("Sumcheck:alpha");
38
39 std::vector<FF> gate_challenges(multivariate_d);
40 for (size_t idx = 0; idx < multivariate_d; idx++) {
41 gate_challenges[idx] =
42 transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
43 }
44
45 GateSeparatorPolynomial<FF> gate_separators(gate_challenges, multivariate_d);
46
47 // Compute alphas as consecutive powers of alpha
49 alphas[0] = alpha;
50 for (size_t i = 1; i < Flavor::NUM_SUBRELATIONS - 1; ++i) {
51 alphas[i] = alphas[i - 1] * alpha;
52 }
53
54 RelationParameters<FF> relation_parameters{
55 .beta = FF(2),
56 .gamma = FF(3),
57 .public_input_delta = FF::one(),
58 };
59
60 return SumcheckSetup{
61 .relation_parameters = relation_parameters,
62 .gate_challenges = gate_challenges,
63 .gate_separators = gate_separators,
64 .alphas = alphas,
65 .alpha = alpha,
66 };
67 }
68};
69
81TEST(RowDisablingPolynomial, MasksRandomPaddingRows)
82{
84 using FF = typename Flavor::FF;
86 using ZKData = ZKSumcheckData<Flavor>;
87
88 // Use same circuit size as existing ZK tests
89 const size_t multivariate_d = 3; // log2(circuit_size) = 3 → 8 rows
90 const size_t multivariate_n = 1 << multivariate_d;
91 const size_t virtual_log_n = multivariate_d; // No padding rounds
92 const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES;
93
94 // Setup: Valid relations at rows 4+ (after disabled region), random junk at rows 0-3.
95 // The first 4 rows are disabled by the row-disabling polynomial.
96 std::array<FF, multivariate_n> w_l = { 0, 0, 0, 0, 1, 2, 0, 0 };
97 std::array<FF, multivariate_n> w_r = { 0, 0, 0, 0, 1, 2, 0, 0 };
98 std::array<FF, multivariate_n> w_o = { 0, 0, 0, 0, 2, 4, 0, 0 };
99 std::array<FF, multivariate_n> w_4 = { 0, 0, 0, 0, 0, 0, 0, 0 };
100 std::array<FF, multivariate_n> q_m = { 0, 0, 0, 0, 0, 1, 0, 0 };
101 std::array<FF, multivariate_n> q_l = { 0, 0, 0, 0, 1, 0, 0, 0 };
102 std::array<FF, multivariate_n> q_r = { 0, 0, 0, 0, 1, 0, 0, 0 };
103 std::array<FF, multivariate_n> q_o = { 0, 0, 0, 0, -1, -1, 0, 0 };
104 std::array<FF, multivariate_n> q_c = { 0, 0, 0, 0, 0, 0, 0, 0 };
105 std::array<FF, multivariate_n> q_arith = { 0, 0, 0, 0, 1, 1, 0, 0 };
106
107 // Critical: Add RANDOM values to the first 4 rows (indices 0-3, the disabled region).
108 // These would normally break sumcheck, but RowDisablingPolynomial disables their contribution.
109 for (size_t i = 0; i < NUM_DISABLED_ROWS_IN_SUMCHECK; i++) {
110 w_l[i] = FF::random_element();
111 w_r[i] = FF::random_element();
112 w_o[i] = FF::random_element();
113 w_4[i] = FF::random_element();
114 q_m[i] = FF::random_element();
115 q_l[i] = FF::random_element();
116 q_r[i] = FF::random_element();
117 q_o[i] = FF::random_element();
118 q_c[i] = FF::random_element();
119 q_arith[i] = FF(1); // Enable arithmetic relation
120 }
121
122 // Setup z_perm, lookup_inverses with random values in disabled rows
123 // SumcheckTestFlavor doesn't need z_perm, lookup_inverses, lookup_read_counts
124 // Random values in witness polynomials are sufficient for testing row disabling
125
126 // Create zero polynomials for all entities
127 std::vector<bb::Polynomial<FF>> zero_polynomials(NUM_POLYNOMIALS);
128 for (auto& poly : zero_polynomials) {
129 poly = bb::Polynomial<FF>(multivariate_n);
130 }
131
132 // Assign to ProverPolynomials
133 ProverPolynomials full_polynomials;
134 for (auto [full_poly, zero_poly] : zip_view(full_polynomials.get_all(), zero_polynomials)) {
135 full_poly = zero_poly.share();
136 }
137
138 // Override with our constructed polynomials
139 full_polynomials.w_l = bb::Polynomial<FF>(w_l);
140 full_polynomials.w_r = bb::Polynomial<FF>(w_r);
141 full_polynomials.w_o = bb::Polynomial<FF>(w_o);
142 full_polynomials.w_4 = bb::Polynomial<FF>(w_4);
143 full_polynomials.q_m = bb::Polynomial<FF>(q_m);
144 full_polynomials.q_l = bb::Polynomial<FF>(q_l);
145 full_polynomials.q_r = bb::Polynomial<FF>(q_r);
146 full_polynomials.q_o = bb::Polynomial<FF>(q_o);
147 full_polynomials.q_c = bb::Polynomial<FF>(q_c);
148 full_polynomials.q_arith = bb::Polynomial<FF>(q_arith);
149
150 // SumcheckTestFlavor doesn't have z_perm, lookup_inverses, lookup_read_counts
151 // The row disabling mechanism will handle the random values in witness polynomials
152
153 // Set relation parameters (SumcheckTestFlavor doesn't need beta/gamma)
154 RelationParameters<FF> relation_parameters{};
155
156 // Prover: Run ZK Sumcheck with RowDisablingPolynomial
157 auto prover_transcript = Flavor::Transcript::test_prover_init_empty();
158 FF prover_alpha = prover_transcript->template get_challenge<FF>("Sumcheck:alpha");
159
160 std::vector<FF> prover_gate_challenges(virtual_log_n);
161 for (size_t idx = 0; idx < virtual_log_n; idx++) {
162 prover_gate_challenges[idx] =
163 prover_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
164 }
165
166 SumcheckProver<Flavor> sumcheck_prover(multivariate_n,
167 full_polynomials,
168 prover_transcript,
169 prover_alpha,
170 prover_gate_challenges,
171 relation_parameters,
172 virtual_log_n);
173
174 // ZK Sumcheck: this will use RowDisablingPolynomial to mask the last 4 rows
175 ZKData zk_sumcheck_data = ZKData(multivariate_d, prover_transcript);
176 SumcheckOutput<Flavor> prover_output = sumcheck_prover.prove(zk_sumcheck_data);
177
178 // Verifier
179 auto verifier_transcript = Flavor::Transcript::test_verifier_init_empty(prover_transcript);
180
181 // Extract challenges from verifier transcript (must match prover)
182 FF verifier_alpha = verifier_transcript->template get_challenge<FF>("Sumcheck:alpha");
183 std::vector<FF> verifier_gate_challenges(virtual_log_n);
184 for (size_t idx = 0; idx < virtual_log_n; idx++) {
185 verifier_gate_challenges[idx] =
186 verifier_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
187 }
188
189 auto sumcheck_verifier = SumcheckVerifier<Flavor>(verifier_transcript, verifier_alpha, virtual_log_n);
190
191 auto verifier_output = sumcheck_verifier.verify(relation_parameters, verifier_gate_challenges);
192
193 // Verification: Despite random values in last 4 rows, sumcheck succeeds
194 EXPECT_TRUE(verifier_output.verified)
195 << "ZK Sumcheck should succeed with RowDisablingPolynomial masking random padding rows";
196
197 // Verify challenges match
198 EXPECT_EQ(prover_output.challenge.size(), verifier_output.challenge.size());
199 for (size_t i = 0; i < prover_output.challenge.size(); i++) {
200 EXPECT_EQ(prover_output.challenge[i], verifier_output.challenge[i]);
201 }
202}
203
209TEST(RowDisablingPolynomial, ComputeDisabledContribution)
210{
212 using TestFixture = RowDisablingPolynomialTest<Flavor>;
213 using FF = typename Flavor::FF;
215 using SumcheckRound = typename TestFixture::SumcheckRound;
216
217 const size_t multivariate_d = 4; // 16 rows
218 const size_t multivariate_n = 1 << multivariate_d;
219 const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES;
220 const size_t NUM_DISABLED_ROWS = 4;
221
222 // Create simple test polynomials with known values in the first 4 rows (disabled region)
223 std::vector<bb::Polynomial<FF>> test_polynomials(NUM_POLYNOMIALS);
224 for (auto& poly : test_polynomials) {
225 poly = bb::Polynomial<FF>(multivariate_n);
226 // Set specific values in the disabled rows (0-3) for easier verification
227 for (size_t i = 0; i < NUM_DISABLED_ROWS; i++) {
228 poly.at(i) = FF(i + 1); // Non-zero, predictable values
229 }
230 }
231
232 ProverPolynomials full_polynomials;
233 for (auto [full_poly, test_poly] : zip_view(full_polynomials.get_all(), test_polynomials)) {
234 full_poly = test_poly.share();
235 }
236
237 // Use fixture to create standard sumcheck setup
238 auto setup = TestFixture::create_sumcheck_setup(multivariate_d);
239
240 // Initialize row disabling polynomial
241 RowDisablingPolynomial<FF> row_disabling_polynomial; // Default constructor
242
243 // Test Round 1: After partially evaluating with u_0
244 {
245 FF u_0 = FF::random_element();
246
247 // Create partially evaluated polynomials (simplified for test)
248 typename Flavor::PartiallyEvaluatedMultivariates partially_evaluated(full_polynomials, multivariate_n);
249 for (auto [pe_poly, full_poly] : zip_view(partially_evaluated.get_all(), full_polynomials.get_all())) {
250 // Simulate partial evaluation: P(u_0, X_1, ...)
251 for (size_t i = 0; i < multivariate_n / 2; i++) {
252 // P(u_0, i_1, i_2, ...) = P(0, i_1, ...) + u_0 * (P(1, i_1, ...) - P(0, i_1, ...))
253 pe_poly.at(i) = full_poly[2 * i] + (u_0 * (full_poly[(2 * i) + 1] - full_poly[2 * i]));
254 }
255 }
256
257 SumcheckRound round(multivariate_n / 2);
258
259 // Update row disabling polynomial after round 0 with challenge u_0
260 row_disabling_polynomial.update_evaluations(u_0, 0);
261 // After round 0: no change to eval_at_0 or eval_at_1 (rounds 0,1 don't accumulate products)
262 EXPECT_EQ(row_disabling_polynomial.eval_at_0, FF(1));
263 EXPECT_EQ(row_disabling_polynomial.eval_at_1, FF(1));
264
265 // Now update for round 1
266 FF u_1 = FF::random_element();
267 row_disabling_polynomial.update_evaluations(u_1, 1);
268 // After round 1: eval_at_1 becomes 0 (L collapses to a single edge pair)
269 EXPECT_EQ(row_disabling_polynomial.eval_at_0, FF(1));
270 EXPECT_EQ(row_disabling_polynomial.eval_at_1, FF(0));
271 }
272
273 // Test that row disabling factor equals X_2 × X_3 × ... × X_{d-1}
274 {
275 std::vector<FF> challenges(multivariate_d);
276 for (auto& c : challenges) {
277 c = FF::random_element();
278 }
279
280 // Compute using the optimized method
281 FF eval = RowDisablingPolynomial<FF>::evaluate_at_challenge(challenges, multivariate_d);
282
283 // The disabled rows are 0,1,2,3. Their Lagrange polys are:
284 // L_0 = (1-X_0)(1-X_1)(1-X_2)...(1-X_{d-1})
285 // L_1 = X_0(1-X_1)(1-X_2)...(1-X_{d-1})
286 // L_2 = (1-X_0)X_1(1-X_2)...(1-X_{d-1})
287 // L_3 = X_0 X_1(1-X_2)...(1-X_{d-1})
288 // Sum = (1-X_2)...(1-X_{d-1}) * [(1-X_0)(1-X_1) + X_0(1-X_1) + (1-X_0)X_1 + X_0 X_1]
289 // = ∏_{i≥2}(1-X_i)
290
291 FF expected_sum_lagranges = FF(1);
292 for (size_t i = 2; i < multivariate_d; i++) {
293 expected_sum_lagranges *= (FF(1) - challenges[i]);
294 }
295
296 FF expected_eval = FF(1) - expected_sum_lagranges;
297
298 EXPECT_EQ(eval, expected_eval) << "Row disabling polynomial should equal 1 - ∏_{i≥2}(1 - X_i)";
299 }
300}
301
306TEST(RowDisablingPolynomial, FailsWithoutRowDisabling)
307{
308 using Flavor = SumcheckTestFlavor; // Non-ZK flavor (no RowDisablingPolynomial)
309 using FF = typename Flavor::FF;
311
312 const size_t NUM_RANDOM_ROWS = 4;
313 const size_t multivariate_d = 4;
314 const size_t multivariate_n = 1 << multivariate_d;
315 const size_t virtual_log_n = multivariate_d;
316 const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES;
317 const size_t valid_rows = multivariate_n - NUM_RANDOM_ROWS;
318
319 // Create arrays for polynomial coefficients
320 std::vector<FF> w_l(multivariate_n);
321 std::vector<FF> w_r(multivariate_n);
322 std::vector<FF> w_o(multivariate_n);
323 std::vector<FF> w_4(multivariate_n);
324 std::vector<FF> q_m(multivariate_n);
325 std::vector<FF> q_l(multivariate_n);
326 std::vector<FF> q_r(multivariate_n);
327 std::vector<FF> q_o(multivariate_n);
328 std::vector<FF> q_c(multivariate_n);
329 std::vector<FF> q_arith(multivariate_n);
330
331 // Setup valid circuit in first rows
332 for (size_t i = 0; i < valid_rows; i++) {
333 w_l[i] = FF(i);
334 w_r[i] = FF(i + 1);
335 w_o[i] = -FF((2 * i) + 1);
336 w_4[i] = FF(0);
337 q_m[i] = FF(0);
338 q_l[i] = FF(1);
339 q_r[i] = FF(1);
340 q_o[i] = FF(1);
341 q_c[i] = FF(0);
342 q_arith[i] = FF(1);
343 }
344
345 // Add random values to last rows (this WILL break sumcheck for non-ZK flavor)
346 for (size_t i = valid_rows; i < multivariate_n; i++) {
347 w_l[i] = FF::random_element();
348 w_r[i] = FF::random_element();
349 w_o[i] = FF::random_element();
350 w_4[i] = FF::random_element();
351 q_m[i] = FF::random_element();
352 q_l[i] = FF::random_element();
353 q_r[i] = FF::random_element();
354 q_o[i] = FF::random_element();
355 q_c[i] = FF::random_element();
356 q_arith[i] = FF(1); // Keep enabled
357 }
358
359 // SumcheckTestFlavor doesn't need z_perm, lookup_inverses, lookup_read_counts
360
361 // Create random polynomials for remaining entities
362 std::vector<bb::Polynomial<FF>> random_polynomials(NUM_POLYNOMIALS);
363 for (auto& poly : random_polynomials) {
364 poly = bb::Polynomial<FF>(multivariate_n);
365 for (size_t i = 0; i < multivariate_n; i++) {
366 poly.at(i) = FF::random_element();
367 }
368 }
369
370 ProverPolynomials full_polynomials;
371 for (auto [full_poly, random_poly] : zip_view(full_polynomials.get_all(), random_polynomials)) {
372 full_poly = random_poly.share();
373 }
374
375 // Override with our constructed polynomials
376 full_polynomials.w_l = bb::Polynomial<FF>(w_l);
377 full_polynomials.w_r = bb::Polynomial<FF>(w_r);
378 full_polynomials.w_o = bb::Polynomial<FF>(w_o);
379 full_polynomials.w_4 = bb::Polynomial<FF>(w_4);
380 full_polynomials.q_m = bb::Polynomial<FF>(q_m);
381 full_polynomials.q_l = bb::Polynomial<FF>(q_l);
382 full_polynomials.q_r = bb::Polynomial<FF>(q_r);
383 full_polynomials.q_o = bb::Polynomial<FF>(q_o);
384 full_polynomials.q_c = bb::Polynomial<FF>(q_c);
385 full_polynomials.q_arith = bb::Polynomial<FF>(q_arith);
386
387 // SumcheckTestFlavor doesn't have z_perm, lookup_inverses, lookup_read_counts
388
389 // Set relation parameters (SumcheckTestFlavor doesn't need beta/gamma)
390 RelationParameters<FF> relation_parameters{};
391
392 auto prover_transcript = Flavor::Transcript::test_prover_init_empty();
393 FF prover_alpha = prover_transcript->template get_challenge<FF>("Sumcheck:alpha");
394
395 std::vector<FF> prover_gate_challenges(virtual_log_n);
396 for (size_t idx = 0; idx < virtual_log_n; idx++) {
397 prover_gate_challenges[idx] =
398 prover_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
399 }
400
401 SumcheckProver<Flavor> sumcheck_prover(multivariate_n,
402 full_polynomials,
403 prover_transcript,
404 prover_alpha,
405 prover_gate_challenges,
406 relation_parameters,
407 virtual_log_n);
408
409 // Non-ZK Sumcheck (no RowDisablingPolynomial)
410 SumcheckOutput<Flavor> prover_output = sumcheck_prover.prove();
411
412 auto verifier_transcript = Flavor::Transcript::test_verifier_init_empty(prover_transcript);
413
414 // Extract challenges from verifier transcript (must match prover)
415 FF verifier_alpha = verifier_transcript->template get_challenge<FF>("Sumcheck:alpha");
416 std::vector<FF> verifier_gate_challenges(virtual_log_n);
417 for (size_t idx = 0; idx < virtual_log_n; idx++) {
418 verifier_gate_challenges[idx] =
419 verifier_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
420 }
421
422 auto sumcheck_verifier = SumcheckVerifier<Flavor>(verifier_transcript, verifier_alpha, virtual_log_n);
423
424 auto verifier_output = sumcheck_verifier.verify(relation_parameters, verifier_gate_challenges);
425
426 // Without RowDisablingPolynomial, sumcheck should FAIL with random padding
427 EXPECT_FALSE(verifier_output.verified)
428 << "Non-ZK Sumcheck should FAIL when random values break relations (no RowDisablingPolynomial)";
429}
Test fixture for RowDisablingPolynomial tests.
static SumcheckSetup create_sumcheck_setup(size_t multivariate_d)
Create standard sumcheck setup with transcript-derived challenges.
typename Flavor::ProverPolynomials ProverPolynomials
std::array< FF, Flavor::NUM_SUBRELATIONS - 1 > SubrelationSeparators
A container for the prover polynomials.
typename Curve::ScalarField FF
static constexpr size_t NUM_SUBRELATIONS
static constexpr size_t NUM_ALL_ENTITIES
A container for storing the partially evaluated multivariates produced by sumcheck.
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:294
SumcheckOutput< Flavor > prove()
Non-ZK version: Compute round univariate, place it in transcript, compute challenge,...
Definition sumcheck.hpp:392
Imlementation of the Sumcheck prover round.
Implementation of the sumcheck Verifier for statements of the form for multilinear polynomials .
Definition sumcheck.hpp:804
typename ECCVMFlavor::ProverPolynomials ProverPolynomials
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
SumcheckTestFlavor_< curve::BN254, true, true > SumcheckTestFlavorZK
Zero-knowledge variant.
SumcheckTestFlavor_< curve::BN254, false, true > SumcheckTestFlavor
Base test flavor (BN254, non-ZK, short monomials)
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
Implementation of the methods for the -polynomials used in in Sumcheck.
Container for parameters used by the grand product (permutation, lookup) Honk relations.
Polynomial for Sumcheck with disabled Rows.
static FF evaluate_at_challenge(std::span< const FF > multivariate_challenge, const size_t log_circuit_size)
Compute the evaluation of at the sumcheck challenge.
void update_evaluations(FF round_challenge, size_t round_idx)
Compute the evaluations of L^{(i)} at 0 and 1.
Contains the evaluations of multilinear polynomials at the challenge point . These are computed by S...
std::vector< FF > challenge
This structure is created to contain various polynomials and constants required by ZK Sumcheck.
static field random_element(numeric::RNG *engine=nullptr) noexcept
Minimal test flavors for sumcheck testing without UltraFlavor dependencies.