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::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: Create polynomials following the pattern from existing ZK tests
95 // Valid relations in first few rows, random values in last 4 rows (indices 4-7)
96
97 // Start with the same non-trivial circuit as existing tests
98 std::array<FF, multivariate_n> w_l = { 0, 1, 2, 0, 0, 0, 0, 0 };
99 std::array<FF, multivariate_n> w_r = { 0, 1, 2, 0, 0, 0, 0, 0 };
100 std::array<FF, multivariate_n> w_o = { 0, 2, 4, 0, 0, 0, 0, 0 };
101 std::array<FF, multivariate_n> w_4 = { 0, 0, 0, 0, 0, 0, 0, 0 };
102 std::array<FF, multivariate_n> q_m = { 0, 0, 1, 0, 0, 0, 0, 0 };
103 std::array<FF, multivariate_n> q_l = { 0, 1, 0, 0, 0, 0, 0, 0 };
104 std::array<FF, multivariate_n> q_r = { 0, 1, 0, 0, 0, 0, 0, 0 };
105 std::array<FF, multivariate_n> q_o = { 0, -1, -1, 0, 0, 0, 0, 0 };
106 std::array<FF, multivariate_n> q_c = { 0, 0, 0, 0, 0, 0, 0, 0 };
107 std::array<FF, multivariate_n> q_arith = { 0, 1, 1, 0, 0, 0, 0, 0 };
108
109 // Critical: Add RANDOM values to the last 4 rows (indices 4-7)
110 // These would normally break sumcheck, but RowDisablingPolynomial disable their contribution
111
112 for (size_t i = 4; i < multivariate_n; i++) {
113 w_l[i] = FF::random_element();
114 w_r[i] = FF::random_element();
115 w_o[i] = FF::random_element();
116 w_4[i] = FF::random_element();
117 q_m[i] = FF::random_element();
118 q_l[i] = FF::random_element();
119 q_r[i] = FF::random_element();
120 q_o[i] = FF::random_element();
121 q_c[i] = FF::random_element();
122 q_arith[i] = FF(1); // Enable arithmetic relation
123 }
124
125 // Setup z_perm, lookup_inverses with random values in disabled rows
126 // SumcheckTestFlavor doesn't need z_perm, lookup_inverses, lookup_read_counts
127 // Random values in witness polynomials are sufficient for testing row disabling
128
129 // Create zero polynomials for all entities
130 std::vector<bb::Polynomial<FF>> zero_polynomials(NUM_POLYNOMIALS);
131 for (auto& poly : zero_polynomials) {
132 poly = bb::Polynomial<FF>(multivariate_n);
133 }
134
135 // Assign to ProverPolynomials
136 ProverPolynomials full_polynomials;
137 for (auto [full_poly, zero_poly] : zip_view(full_polynomials.get_all(), zero_polynomials)) {
138 full_poly = zero_poly.share();
139 }
140
141 // Override with our constructed polynomials
142 full_polynomials.w_l = bb::Polynomial<FF>(w_l);
143 full_polynomials.w_r = bb::Polynomial<FF>(w_r);
144 full_polynomials.w_o = bb::Polynomial<FF>(w_o);
145 full_polynomials.w_4 = bb::Polynomial<FF>(w_4);
146 full_polynomials.q_m = bb::Polynomial<FF>(q_m);
147 full_polynomials.q_l = bb::Polynomial<FF>(q_l);
148 full_polynomials.q_r = bb::Polynomial<FF>(q_r);
149 full_polynomials.q_o = bb::Polynomial<FF>(q_o);
150 full_polynomials.q_c = bb::Polynomial<FF>(q_c);
151 full_polynomials.q_arith = bb::Polynomial<FF>(q_arith);
152
153 // SumcheckTestFlavor doesn't have z_perm, lookup_inverses, lookup_read_counts
154 // The row disabling mechanism will handle the random values in witness polynomials
155
156 // Set relation parameters (SumcheckTestFlavor doesn't need beta/gamma)
157 RelationParameters<FF> relation_parameters{};
158
159 // Prover: Run ZK Sumcheck with RowDisablingPolynomial
160 auto prover_transcript = Flavor::Transcript::prover_init_empty();
161 FF prover_alpha = prover_transcript->template get_challenge<FF>("Sumcheck:alpha");
162
163 std::vector<FF> prover_gate_challenges(virtual_log_n);
164 for (size_t idx = 0; idx < virtual_log_n; idx++) {
165 prover_gate_challenges[idx] =
166 prover_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
167 }
168
169 SumcheckProver<Flavor> sumcheck_prover(multivariate_n,
170 full_polynomials,
171 prover_transcript,
172 prover_alpha,
173 prover_gate_challenges,
174 relation_parameters,
175 virtual_log_n);
176
177 // ZK Sumcheck: this will use RowDisablingPolynomial to mask the last 4 rows
178 ZKData zk_sumcheck_data = ZKData(multivariate_d, prover_transcript);
179 SumcheckOutput<Flavor> prover_output = sumcheck_prover.prove(zk_sumcheck_data);
180
181 // Verifier: Verify with padding_indicator_array
182 auto verifier_transcript = Flavor::Transcript::verifier_init_empty(prover_transcript);
183
184 // Extract challenges from verifier transcript (must match prover)
185 FF verifier_alpha = verifier_transcript->template get_challenge<FF>("Sumcheck:alpha");
186 std::vector<FF> verifier_gate_challenges(virtual_log_n);
187 for (size_t idx = 0; idx < virtual_log_n; idx++) {
188 verifier_gate_challenges[idx] =
189 verifier_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
190 }
191
192 auto sumcheck_verifier = SumcheckVerifier<Flavor>(verifier_transcript, verifier_alpha, virtual_log_n);
193
194 // Construct padding indicator array (all 1s since we're not using padding rounds)
195 std::vector<FF> padding_indicator_array(virtual_log_n, FF(1));
196
197 auto verifier_output =
198 sumcheck_verifier.verify(relation_parameters, verifier_gate_challenges, padding_indicator_array);
199
200 // Verification: Despite random values in last 4 rows, sumcheck succeeds
201 EXPECT_TRUE(verifier_output.verified)
202 << "ZK Sumcheck should succeed with RowDisablingPolynomial masking random padding rows";
203
204 // Verify challenges match
205 EXPECT_EQ(prover_output.challenge.size(), verifier_output.challenge.size());
206 for (size_t i = 0; i < prover_output.challenge.size(); i++) {
207 EXPECT_EQ(prover_output.challenge[i], verifier_output.challenge[i]);
208 }
209}
210
216TEST(RowDisablingPolynomial, ComputeDisabledContribution)
217{
219 using TestFixture = RowDisablingPolynomialTest<Flavor>;
220 using FF = typename Flavor::FF;
222 using SumcheckRound = typename TestFixture::SumcheckRound;
223
224 const size_t multivariate_d = 4; // 16 rows
225 const size_t multivariate_n = 1 << multivariate_d;
226 const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES;
227 const size_t NUM_DISABLED_ROWS = 4;
228
229 // Create simple test polynomials with known values in the last 4 rows
230 std::vector<bb::Polynomial<FF>> test_polynomials(NUM_POLYNOMIALS);
231 for (auto& poly : test_polynomials) {
232 poly = bb::Polynomial<FF>(multivariate_n);
233 // Set specific values in the disabled rows (12-15) for easier verification
234 for (size_t i = multivariate_n - NUM_DISABLED_ROWS; i < multivariate_n; i++) {
235 poly.at(i) = FF(i + 1); // Non-zero, predictable values
236 }
237 }
238
239 ProverPolynomials full_polynomials;
240 for (auto [full_poly, test_poly] : zip_view(full_polynomials.get_all(), test_polynomials)) {
241 full_poly = test_poly.share();
242 }
243
244 // Use fixture to create standard sumcheck setup
245 auto setup = TestFixture::create_sumcheck_setup(multivariate_d);
246
247 // Initialize row disabling polynomial
248 RowDisablingPolynomial<FF> row_disabling_polynomial; // Default constructor
249
250 // Test Round 1: After partially evaluating with u_0
251 {
252 FF u_0 = FF::random_element();
253
254 // Create partially evaluated polynomials (simplified for test)
255 typename Flavor::PartiallyEvaluatedMultivariates partially_evaluated;
256 for (auto [pe_poly, full_poly] : zip_view(partially_evaluated.get_all(), full_polynomials.get_all())) {
257 // Simulate partial evaluation: P(u_0, X_1, ...)
258 pe_poly = bb::Polynomial<FF>(multivariate_n / 2);
259 for (size_t i = 0; i < multivariate_n / 2; i++) {
260 // P(u_0, i_1, i_2, ...) = P(0, i_1, ...) + u_0 * (P(1, i_1, ...) - P(0, i_1, ...))
261 pe_poly.at(i) = full_poly[2 * i] + (u_0 * (full_poly[(2 * i) + 1] - full_poly[2 * i]));
262 }
263 }
264
265 SumcheckRound round(multivariate_n / 2);
266
267 // Update row disabling polynomial after round 0 with challenge u_0
268 row_disabling_polynomial.update_evaluations(u_0, 0);
269 // At this point (round_idx=0), eval_at_0 and eval_at_1 should still be 1
270 EXPECT_EQ(row_disabling_polynomial.eval_at_0, FF(1));
271 EXPECT_EQ(row_disabling_polynomial.eval_at_1, FF(1));
272
273 // Now update for round 1
274 FF u_1 = FF::random_element();
275 row_disabling_polynomial.update_evaluations(u_1, 1);
276 // After round 1, eval_at_0 should be 0
277 EXPECT_EQ(row_disabling_polynomial.eval_at_0, FF(0));
278 EXPECT_EQ(row_disabling_polynomial.eval_at_1, FF(1));
279 }
280
281 // Test that row disabling factor equals X_2 × X_3 × ... × X_{d-1}
282 {
283 std::vector<FF> challenges(multivariate_d);
284 for (auto& c : challenges) {
285 c = FF::random_element();
286 }
287
288 // Compute using the optimized method
289 FF eval = RowDisablingPolynomial<FF>::evaluate_at_challenge(challenges, multivariate_d);
290
291 // Manually compute: should be (1 - X_0*X_1 - (1-X_0)*X_1 - X_0*(1-X_1) - (1-X_0)*(1-X_1)) * X_2 * ... * X_{d-1}
292 // Which simplifies to: (1 - X_1 - (1-X_1)) * X_2 * ... * X_{d-1} = 0 * X_2 * ... = 0?
293 // Wait, let me reconsider...
294
295 // L_{n-1} + L_{n-2} + L_{n-3} + L_{n-4} where:
296 // L_{n-1} = X_0 * X_1 * X_2 * ... * X_{d-1}
297 // L_{n-2} = (1-X_0) * X_1 * X_2 * ... * X_{d-1}
298 // L_{n-3} = X_0 * (1-X_1) * X_2 * ... * X_{d-1}
299 // L_{n-4} = (1-X_0) * (1-X_1) * X_2 * ... * X_{d-1}
300 // Sum = X_2 * ... * X_{d-1} * [X_0*X_1 + (1-X_0)*X_1 + X_0*(1-X_1) + (1-X_0)*(1-X_1)]
301 // = X_2 * ... * X_{d-1} * [X_1 + (1-X_1)] = X_2 * ... * X_{d-1}
302
303 FF expected_sum_lagranges = FF(1);
304 for (size_t i = 2; i < multivariate_d; i++) {
305 expected_sum_lagranges *= challenges[i];
306 }
307
308 FF expected_eval = FF(1) - expected_sum_lagranges;
309
310 EXPECT_EQ(eval, expected_eval) << "Row disabling polynomial should equal (1 - X_2 * X_3 * ... * X_{d-1})";
311
312 info("Verified: L_{n-1} + L_{n-2} + L_{n-3} + L_{n-4} = X_2 × X_3 × ... × X_{d-1}");
313 }
314}
315
320TEST(RowDisablingPolynomial, FailsWithoutRowDisabling)
321{
322 using Flavor = SumcheckTestFlavor; // Non-ZK flavor (no RowDisablingPolynomial)
323 using FF = typename Flavor::FF;
325
326 const size_t NUM_RANDOM_ROWS = 4;
327 const size_t multivariate_d = 4;
328 const size_t multivariate_n = 1 << multivariate_d;
329 const size_t virtual_log_n = multivariate_d;
330 const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES;
331 const size_t valid_rows = multivariate_n - NUM_RANDOM_ROWS;
332
333 // Create arrays for polynomial coefficients
334 std::vector<FF> w_l(multivariate_n);
335 std::vector<FF> w_r(multivariate_n);
336 std::vector<FF> w_o(multivariate_n);
337 std::vector<FF> w_4(multivariate_n);
338 std::vector<FF> q_m(multivariate_n);
339 std::vector<FF> q_l(multivariate_n);
340 std::vector<FF> q_r(multivariate_n);
341 std::vector<FF> q_o(multivariate_n);
342 std::vector<FF> q_c(multivariate_n);
343 std::vector<FF> q_arith(multivariate_n);
344
345 // Setup valid circuit in first rows
346 for (size_t i = 0; i < valid_rows; i++) {
347 w_l[i] = FF(i);
348 w_r[i] = FF(i + 1);
349 w_o[i] = -FF((2 * i) + 1);
350 w_4[i] = FF(0);
351 q_m[i] = FF(0);
352 q_l[i] = FF(1);
353 q_r[i] = FF(1);
354 q_o[i] = FF(1);
355 q_c[i] = FF(0);
356 q_arith[i] = FF(1);
357 }
358
359 // Add random values to last rows (this WILL break sumcheck for non-ZK flavor)
360 for (size_t i = valid_rows; i < multivariate_n; i++) {
361 w_l[i] = FF::random_element();
362 w_r[i] = FF::random_element();
363 w_o[i] = FF::random_element();
364 w_4[i] = FF::random_element();
365 q_m[i] = FF::random_element();
366 q_l[i] = FF::random_element();
367 q_r[i] = FF::random_element();
368 q_o[i] = FF::random_element();
369 q_c[i] = FF::random_element();
370 q_arith[i] = FF(1); // Keep enabled
371 }
372
373 // SumcheckTestFlavor doesn't need z_perm, lookup_inverses, lookup_read_counts
374
375 // Create random polynomials for remaining entities
376 std::vector<bb::Polynomial<FF>> random_polynomials(NUM_POLYNOMIALS);
377 for (auto& poly : random_polynomials) {
378 poly = bb::Polynomial<FF>(multivariate_n);
379 for (size_t i = 0; i < multivariate_n; i++) {
380 poly.at(i) = FF::random_element();
381 }
382 }
383
384 ProverPolynomials full_polynomials;
385 for (auto [full_poly, random_poly] : zip_view(full_polynomials.get_all(), random_polynomials)) {
386 full_poly = random_poly.share();
387 }
388
389 // Override with our constructed polynomials
390 full_polynomials.w_l = bb::Polynomial<FF>(w_l);
391 full_polynomials.w_r = bb::Polynomial<FF>(w_r);
392 full_polynomials.w_o = bb::Polynomial<FF>(w_o);
393 full_polynomials.w_4 = bb::Polynomial<FF>(w_4);
394 full_polynomials.q_m = bb::Polynomial<FF>(q_m);
395 full_polynomials.q_l = bb::Polynomial<FF>(q_l);
396 full_polynomials.q_r = bb::Polynomial<FF>(q_r);
397 full_polynomials.q_o = bb::Polynomial<FF>(q_o);
398 full_polynomials.q_c = bb::Polynomial<FF>(q_c);
399 full_polynomials.q_arith = bb::Polynomial<FF>(q_arith);
400
401 // SumcheckTestFlavor doesn't have z_perm, lookup_inverses, lookup_read_counts
402
403 // Set relation parameters (SumcheckTestFlavor doesn't need beta/gamma)
404 RelationParameters<FF> relation_parameters{};
405
406 auto prover_transcript = Flavor::Transcript::prover_init_empty();
407 FF prover_alpha = prover_transcript->template get_challenge<FF>("Sumcheck:alpha");
408
409 std::vector<FF> prover_gate_challenges(virtual_log_n);
410 for (size_t idx = 0; idx < virtual_log_n; idx++) {
411 prover_gate_challenges[idx] =
412 prover_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
413 }
414
415 SumcheckProver<Flavor> sumcheck_prover(multivariate_n,
416 full_polynomials,
417 prover_transcript,
418 prover_alpha,
419 prover_gate_challenges,
420 relation_parameters,
421 virtual_log_n);
422
423 // Non-ZK Sumcheck (no RowDisablingPolynomial)
424 SumcheckOutput<Flavor> prover_output = sumcheck_prover.prove();
425
426 auto verifier_transcript = Flavor::Transcript::verifier_init_empty(prover_transcript);
427
428 // Extract challenges from verifier transcript (must match prover)
429 FF verifier_alpha = verifier_transcript->template get_challenge<FF>("Sumcheck:alpha");
430 std::vector<FF> verifier_gate_challenges(virtual_log_n);
431 for (size_t idx = 0; idx < virtual_log_n; idx++) {
432 verifier_gate_challenges[idx] =
433 verifier_transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(idx));
434 }
435
436 auto sumcheck_verifier = SumcheckVerifier<Flavor>(verifier_transcript, verifier_alpha, virtual_log_n);
437
438 std::vector<FF> padding_indicator_array(virtual_log_n, FF(1));
439
440 auto verifier_output =
441 sumcheck_verifier.verify(relation_parameters, verifier_gate_challenges, padding_indicator_array);
442
443 // Without RowDisablingPolynomial, sumcheck should FAIL with random padding
444 EXPECT_FALSE(verifier_output.verified)
445 << "Non-ZK Sumcheck should FAIL when random values break relations (no RowDisablingPolynomial)";
446}
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 storing the partially evaluated multivariates produced by sumcheck.
A container for the prover polynomials.
typename Curve::ScalarField FF
static constexpr size_t NUM_SUBRELATIONS
static constexpr size_t NUM_ALL_ENTITIES
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:289
SumcheckOutput< Flavor > prove()
Non-ZK version: Compute round univariate, place it in transcript, compute challenge,...
Definition sumcheck.hpp:398
Imlementation of the Sumcheck prover round.
Implementation of the sumcheck Verifier for statements of the form for multilinear polynomials .
Definition sumcheck.hpp:786
void info(Args... args)
Definition log.hpp:75
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::vector< 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.