Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
plookup.test.cpp
Go to the documentation of this file.
1#include "plookup.hpp"
10#include <gtest/gtest.h>
11
12using namespace bb;
13using namespace bb::plookup;
14
15// Defining ultra-specific types for local testing.
20namespace {
22}
23
24TEST(PlookupTests, uint32_xor)
25{
27
28 const size_t num_lookups = (32 + 5) / 6;
29
30 uint256_t left_value = (engine.get_random_uint256() & 0xffffffffULL);
31 uint256_t right_value = (engine.get_random_uint256() & 0xffffffffULL);
32
33 field_ct left = witness_ct(&builder, bb::fr(left_value));
34 field_ct right = witness_ct(&builder, bb::fr(right_value));
35
36 const auto lookup = plookup_read::get_lookup_accumulators(MultiTableId::UINT32_XOR, left, right, true);
37
38 const auto left_slices = numeric::slice_input(left_value, 1 << 6, num_lookups);
39 const auto right_slices = numeric::slice_input(right_value, 1 << 6, num_lookups);
40
41 std::vector<uint256_t> out_expected(num_lookups);
42 std::vector<uint256_t> left_expected(num_lookups);
43 std::vector<uint256_t> right_expected(num_lookups);
44
45 for (size_t i = 0; i < left_slices.size(); ++i) {
46 out_expected[i] = left_slices[i] ^ right_slices[i];
47 left_expected[i] = left_slices[i];
48 right_expected[i] = right_slices[i];
49 }
50
51 for (size_t i = num_lookups - 2; i < num_lookups; --i) {
52 out_expected[i] += out_expected[i + 1] * (1 << 6);
53 left_expected[i] += left_expected[i + 1] * (1 << 6);
54 right_expected[i] += right_expected[i + 1] * (1 << 6);
55 }
56
57 for (size_t i = 0; i < num_lookups; ++i) {
58 EXPECT_EQ(lookup[ColumnIdx::C1][i].get_value(), bb::fr(left_expected[i]));
59 EXPECT_EQ(lookup[ColumnIdx::C2][i].get_value(), bb::fr(right_expected[i]));
60 EXPECT_EQ(lookup[ColumnIdx::C3][i].get_value(), bb::fr(out_expected[i]));
61 }
62
64
65 EXPECT_EQ(result, true);
66}
67
68TEST(PlookupTests, blake2s_xor_rotate_16)
69{
71
72 const size_t num_lookups = 6;
73
74 uint256_t left_value = (engine.get_random_uint256() & 0xffffffffULL);
75 uint256_t right_value = (engine.get_random_uint256() & 0xffffffffULL);
76
77 field_ct left = witness_ct(&builder, bb::fr(left_value));
78 field_ct right = witness_ct(&builder, bb::fr(right_value));
79
80 const auto lookup = plookup_read::get_lookup_accumulators(MultiTableId::BLAKE_XOR_ROTATE_16, left, right, true);
81
82 const auto left_slices = numeric::slice_input(left_value, 1 << 6, num_lookups);
83 const auto right_slices = numeric::slice_input(right_value, 1 << 6, num_lookups);
84
85 std::vector<fr> out_expected(num_lookups);
86 std::vector<fr> left_expected(num_lookups);
87 std::vector<fr> right_expected(num_lookups);
88
89 for (size_t i = 0; i < left_slices.size(); ++i) {
90 if (i == 2) {
91 uint32_t a = static_cast<uint32_t>(left_slices[i]);
92 uint32_t b = static_cast<uint32_t>(right_slices[i]);
93 uint32_t c = numeric::rotate32(a ^ b, 4);
94 out_expected[i] = uint256_t(c);
95 } else {
96 out_expected[i] = uint256_t(left_slices[i]) ^ uint256_t(right_slices[i]);
97 }
98 left_expected[i] = left_slices[i];
99 right_expected[i] = right_slices[i];
100 }
101
102 /*
103 * The following out coefficients are the ones multiplied for computing the cumulative intermediate terms
104 * in the expected output. If the column_3_coefficients for this table are (a0, a1, ..., a5), then the
105 * out_coefficients must be (a5/a4, a4/a3, a3/a2, a2/a1, a1/a0). Note that these are stored in reverse orde
106 * for simplicity.
107 */
108 std::vector<fr> out_coefficients{ (1 << 6), (bb::fr(1) / bb::fr(1 << 22)), (1 << 2), (1 << 6), (1 << 6) };
109
110 for (size_t i = num_lookups - 2; i < num_lookups; --i) {
111 out_expected[i] += out_expected[i + 1] * out_coefficients[i];
112 left_expected[i] += left_expected[i + 1] * (1 << 6);
113 right_expected[i] += right_expected[i + 1] * (1 << 6);
114 }
115
116 for (size_t i = 0; i < num_lookups; ++i) {
117 EXPECT_EQ(lookup[ColumnIdx::C1][i].get_value(), left_expected[i]);
118 EXPECT_EQ(lookup[ColumnIdx::C2][i].get_value(), right_expected[i]);
119 EXPECT_EQ(lookup[ColumnIdx::C3][i].get_value(), out_expected[i]);
120 }
121
122 /*
123 * Note that we multiply the output of the lookup table (lookup[Column::Idx}0]) by 2^{16} because
124 * while defining the table we had set the coefficient of s0 to 1, so to correct that, we need to multiply by a
125 * constant.
126 */
127 auto mul_constant = fr(1 << 16);
128 fr lookup_output = lookup[ColumnIdx::C3][0].get_value() * mul_constant;
129 uint32_t xor_rotate_output = numeric::rotate32(uint32_t(left_value) ^ uint32_t(right_value), 16);
130 EXPECT_EQ(fr(uint256_t(xor_rotate_output)), lookup_output);
131
133
134 EXPECT_EQ(result, true);
135}
136
137TEST(PlookupTests, blake2s_xor_rotate_8)
138{
140
141 const size_t num_lookups = 6;
142
143 uint256_t left_value = (engine.get_random_uint256() & 0xffffffffULL);
144 uint256_t right_value = (engine.get_random_uint256() & 0xffffffffULL);
145
146 field_ct left = witness_ct(&builder, bb::fr(left_value));
147 field_ct right = witness_ct(&builder, bb::fr(right_value));
148
149 const auto lookup = plookup_read::get_lookup_accumulators(MultiTableId::BLAKE_XOR_ROTATE_8, left, right, true);
150
151 const auto left_slices = numeric::slice_input(left_value, 1 << 6, num_lookups);
152 const auto right_slices = numeric::slice_input(right_value, 1 << 6, num_lookups);
153
154 std::vector<fr> out_expected(num_lookups);
155 std::vector<fr> left_expected(num_lookups);
156 std::vector<fr> right_expected(num_lookups);
157
158 for (size_t i = 0; i < left_slices.size(); ++i) {
159 if (i == 1) {
160 uint32_t a = static_cast<uint32_t>(left_slices[i]);
161 uint32_t b = static_cast<uint32_t>(right_slices[i]);
162 uint32_t c = numeric::rotate32(a ^ b, 2);
163 out_expected[i] = uint256_t(c);
164 } else {
165 out_expected[i] = uint256_t(left_slices[i]) ^ uint256_t(right_slices[i]);
166 }
167 left_expected[i] = left_slices[i];
168 right_expected[i] = right_slices[i];
169 }
170
171 auto mul_constant = fr(1 << 24);
172 std::vector<fr> out_coefficients{ (bb::fr(1) / mul_constant), (1 << 4), (1 << 6), (1 << 6), (1 << 6) };
173
174 for (size_t i = num_lookups - 2; i < num_lookups; --i) {
175 out_expected[i] += out_expected[i + 1] * out_coefficients[i];
176 left_expected[i] += left_expected[i + 1] * (1 << 6);
177 right_expected[i] += right_expected[i + 1] * (1 << 6);
178 }
179
180 for (size_t i = 0; i < num_lookups; ++i) {
181 EXPECT_EQ(lookup[ColumnIdx::C1][i].get_value(), left_expected[i]);
182 EXPECT_EQ(lookup[ColumnIdx::C2][i].get_value(), right_expected[i]);
183 EXPECT_EQ(lookup[ColumnIdx::C3][i].get_value(), out_expected[i]);
184 }
185
186 fr lookup_output = lookup[ColumnIdx::C3][0].get_value() * mul_constant;
187 uint32_t xor_rotate_output = numeric::rotate32(uint32_t(left_value) ^ uint32_t(right_value), 8);
188 EXPECT_EQ(fr(uint256_t(xor_rotate_output)), lookup_output);
189
191
192 EXPECT_EQ(result, true);
193}
194
195TEST(PlookupTests, blake2s_xor_rotate_7)
196{
198
199 const size_t num_lookups = 6;
200
201 uint256_t left_value = (engine.get_random_uint256() & 0xffffffffULL);
202 uint256_t right_value = (engine.get_random_uint256() & 0xffffffffULL);
203
204 field_ct left = witness_ct(&builder, bb::fr(left_value));
205 field_ct right = witness_ct(&builder, bb::fr(right_value));
206
207 const auto lookup = plookup_read::get_lookup_accumulators(MultiTableId::BLAKE_XOR_ROTATE_7, left, right, true);
208
209 const auto left_slices = numeric::slice_input(left_value, 1 << 6, num_lookups);
210 const auto right_slices = numeric::slice_input(right_value, 1 << 6, num_lookups);
211
212 std::vector<fr> out_expected(num_lookups);
213 std::vector<fr> left_expected(num_lookups);
214 std::vector<fr> right_expected(num_lookups);
215
216 for (size_t i = 0; i < left_slices.size(); ++i) {
217 if (i == 1) {
218 uint32_t a = static_cast<uint32_t>(left_slices[i]);
219 uint32_t b = static_cast<uint32_t>(right_slices[i]);
220 uint32_t c = numeric::rotate32(a ^ b, 1);
221 out_expected[i] = uint256_t(c);
222 } else {
223 out_expected[i] = uint256_t(left_slices[i]) ^ uint256_t(right_slices[i]);
224 }
225 left_expected[i] = left_slices[i];
226 right_expected[i] = right_slices[i];
227 }
228
229 auto mul_constant = fr(1 << 25);
230 std::vector<fr> out_coefficients{ (bb::fr(1) / mul_constant), (1 << 5), (1 << 6), (1 << 6), (1 << 6) };
231
232 for (size_t i = num_lookups - 2; i < num_lookups; --i) {
233 out_expected[i] += out_expected[i + 1] * out_coefficients[i];
234 left_expected[i] += left_expected[i + 1] * (1 << 6);
235 right_expected[i] += right_expected[i + 1] * (1 << 6);
236 }
237
238 for (size_t i = 0; i < num_lookups; ++i) {
239 EXPECT_EQ(lookup[ColumnIdx::C1][i].get_value(), left_expected[i]);
240 EXPECT_EQ(lookup[ColumnIdx::C2][i].get_value(), right_expected[i]);
241 EXPECT_EQ(lookup[ColumnIdx::C3][i].get_value(), out_expected[i]);
242 }
243
244 fr lookup_output = lookup[ColumnIdx::C3][0].get_value() * mul_constant;
245 uint32_t xor_rotate_output = numeric::rotate32(uint32_t(left_value) ^ uint32_t(right_value), 7);
246 EXPECT_EQ(fr(uint256_t(xor_rotate_output)), lookup_output);
247
249
250 EXPECT_EQ(result, true);
251}
252
253TEST(PlookupTests, blake2s_xor)
254{
256
257 const size_t num_lookups = 6;
258
259 uint256_t left_value = (engine.get_random_uint256() & 0xffffffffULL);
260 uint256_t right_value = (engine.get_random_uint256() & 0xffffffffULL);
261
262 field_ct left = witness_ct(&builder, bb::fr(left_value));
263 field_ct right = witness_ct(&builder, bb::fr(right_value));
264
265 const auto lookup = plookup_read::get_lookup_accumulators(MultiTableId::BLAKE_XOR, left, right, true);
266
267 const auto left_slices = numeric::slice_input(left_value, 1 << 6, num_lookups);
268 const auto right_slices = numeric::slice_input(right_value, 1 << 6, num_lookups);
269
270 std::vector<uint256_t> out_expected(num_lookups);
271 std::vector<uint256_t> left_expected(num_lookups);
272 std::vector<uint256_t> right_expected(num_lookups);
273
274 for (size_t i = 0; i < left_slices.size(); ++i) {
275 out_expected[i] = left_slices[i] ^ right_slices[i];
276 left_expected[i] = left_slices[i];
277 right_expected[i] = right_slices[i];
278 }
279
280 // Compute ror(a ^ b, 12) from lookup table.
281 // t0 = 2^30 a5 + 2^24 a4 + 2^18 a3 + 2^12 a2 + 2^6 a1 + a0
282 // t1 = 2^24 a5 + 2^18 a4 + 2^12 a3 + 2^6 a2 + a1
283 // t2 = 2^18 a5 + 2^12 a4 + 2^6 a3 + a2
284 // t3 = 2^12 a5 + 2^6 a4 + a3
285 // t4 = 2^6 a5 + a4
286 // t5 = a5
287 //
288 // output = (t0 - 2^12 t2) * 2^{32 - 12} + t2
289 fr lookup_output = lookup[ColumnIdx::C3][2].get_value();
290 fr t2_term = fr(1 << 12) * lookup[ColumnIdx::C3][2].get_value();
291 lookup_output += fr(1 << 20) * (lookup[ColumnIdx::C3][0].get_value() - t2_term);
292
293 for (size_t i = num_lookups - 2; i < num_lookups; --i) {
294 out_expected[i] += out_expected[i + 1] * (1 << 6);
295 left_expected[i] += left_expected[i + 1] * (1 << 6);
296 right_expected[i] += right_expected[i + 1] * (1 << 6);
297 }
298
299 //
300 // The following checks if the xor output rotated by 12 can be computed correctly from basic blake2s_xor.
301 //
302 auto xor_rotate_output = numeric::rotate32(uint32_t(left_value) ^ uint32_t(right_value), 12);
303 EXPECT_EQ(fr(uint256_t(xor_rotate_output)), lookup_output);
304
305 for (size_t i = 0; i < num_lookups; ++i) {
306 EXPECT_EQ(lookup[ColumnIdx::C1][i].get_value(), bb::fr(left_expected[i]));
307 EXPECT_EQ(lookup[ColumnIdx::C2][i].get_value(), bb::fr(right_expected[i]));
308 EXPECT_EQ(lookup[ColumnIdx::C3][i].get_value(), bb::fr(out_expected[i]));
309 }
310
312
313 EXPECT_EQ(result, true);
314}
315
316TEST(PlookupTests, uint32_and)
317{
319
320 const size_t num_lookups = (32 + 5) / 6;
321
322 uint256_t left_value = (engine.get_random_uint256() & 0xffffffffULL);
323 uint256_t right_value = (engine.get_random_uint256() & 0xffffffffULL);
324
325 field_ct left = witness_ct(&builder, bb::fr(left_value));
326 field_ct right = witness_ct(&builder, bb::fr(right_value));
327
328 const auto lookup = plookup_read::get_lookup_accumulators(MultiTableId::UINT32_AND, left, right, true);
329 const auto left_slices = numeric::slice_input(left_value, 1 << 6, num_lookups);
330 const auto right_slices = numeric::slice_input(right_value, 1 << 6, num_lookups);
331 std::vector<uint256_t> out_expected(num_lookups);
332 std::vector<uint256_t> left_expected(num_lookups);
333 std::vector<uint256_t> right_expected(num_lookups);
334
335 for (size_t i = 0; i < left_slices.size(); ++i) {
336 out_expected[i] = left_slices[i] & right_slices[i];
337 left_expected[i] = left_slices[i];
338 right_expected[i] = right_slices[i];
339 }
340
341 for (size_t i = num_lookups - 2; i < num_lookups; --i) {
342 out_expected[i] += out_expected[i + 1] * (1 << 6);
343 left_expected[i] += left_expected[i + 1] * (1 << 6);
344 right_expected[i] += right_expected[i + 1] * (1 << 6);
345 }
346
347 for (size_t i = 0; i < num_lookups; ++i) {
348 EXPECT_EQ(lookup[ColumnIdx::C1][i].get_value(), bb::fr(left_expected[i]));
349 EXPECT_EQ(lookup[ColumnIdx::C2][i].get_value(), bb::fr(right_expected[i]));
350 EXPECT_EQ(lookup[ColumnIdx::C3][i].get_value(), bb::fr(out_expected[i]));
351 }
352
354
355 EXPECT_EQ(result, true);
356}
357
358TEST(PlookupTests, secp256k1_generator)
359{
360 using curve = stdlib::secp256k1<Builder>;
362
363 uint256_t input_value = (engine.get_random_uint256() >> 128);
364
365 uint64_t wnaf_entries[18] = { 0 };
366 bool skew = false;
367 wnaf::fixed_wnaf<129, 1, 8>(&input_value.data[0], &wnaf_entries[0], skew, 0);
368
369 std::vector<uint64_t> naf_values;
370 for (size_t i = 0; i < 17; ++i) {
371 bool predicate = bool((wnaf_entries[i] >> 31U) & 1U);
372 uint64_t offset_entry;
373 if (predicate) {
374 offset_entry = (127 - (wnaf_entries[i] & 0xffffff));
375 } else {
376 offset_entry = (128 + (wnaf_entries[i] & 0xffffff));
377 }
378 naf_values.emplace_back(offset_entry);
379 }
380
381 std::vector<field_ct> circuit_naf_values;
382 for (size_t i = 0; i < naf_values.size(); ++i) {
383 circuit_naf_values.emplace_back(witness_ct(&builder, naf_values[i]));
384 }
385
386 std::vector<field_ct> accumulators;
387 for (size_t i = 0; i < naf_values.size(); ++i) {
388 field_ct t1 = (circuit_naf_values[naf_values.size() - 1 - i]) * field_ct(uint256_t(1) << (i * 8 + 1));
389 field_ct t2 = field_ct(255) * field_ct(uint256_t(1) << (i * 8));
390 accumulators.emplace_back(t1 - t2);
391 }
392 field_ct accumulator_field = field_ct::accumulate(accumulators);
393 EXPECT_EQ(accumulator_field.get_value(), bb::fr(input_value) + bb::fr(skew));
394
395 for (size_t i = 0; i < 256; ++i) {
397 const auto xlo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XLO, index);
398 const auto xhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XHI, index);
399 const auto ylo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YLO, index);
400 const auto yhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YHI, index);
401 curve::BaseField x =
402 curve::BaseField::unsafe_construct_from_limbs(xlo.first, xlo.second, xhi.first, xhi.second);
403 curve::BaseField y =
404 curve::BaseField::unsafe_construct_from_limbs(ylo.first, ylo.second, yhi.first, yhi.second);
405
406 const auto res = curve::Group(x, y).get_value();
407 curve::ScalarFieldNative scalar(i);
408 scalar = scalar + scalar;
409 scalar = scalar - 255;
410 curve::GroupNative::affine_element expec(curve::GroupNative::one * scalar);
411
412 EXPECT_EQ(res, expec);
413 }
414 curve::Group accumulator;
415 {
416 const auto xlo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XLO, circuit_naf_values[0]);
417 const auto xhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XHI, circuit_naf_values[0]);
418 const auto ylo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YLO, circuit_naf_values[0]);
419 const auto yhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YHI, circuit_naf_values[0]);
420
421 curve::BaseField x =
422 curve::BaseField::unsafe_construct_from_limbs(xlo.first, xlo.second, xhi.first, xhi.second);
423 curve::BaseField y =
424 curve::BaseField::unsafe_construct_from_limbs(ylo.first, ylo.second, yhi.first, yhi.second);
425 accumulator = curve::Group(x, y);
426 }
427 for (size_t i = 1; i < circuit_naf_values.size(); ++i) {
428 accumulator = accumulator.dbl();
429 accumulator = accumulator.dbl();
430 accumulator = accumulator.dbl();
431 accumulator = accumulator.dbl();
432 accumulator = accumulator.dbl();
433 accumulator = accumulator.dbl();
434 accumulator = accumulator.dbl();
435
436 const auto xlo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XLO, circuit_naf_values[i]);
437 const auto xhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XHI, circuit_naf_values[i]);
438 const auto ylo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YLO, circuit_naf_values[i]);
439 const auto yhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YHI, circuit_naf_values[i]);
440 curve::BaseField x =
441 curve::BaseField::unsafe_construct_from_limbs(xlo.first, xlo.second, xhi.first, xhi.second);
442 curve::BaseField y =
443 curve::BaseField::unsafe_construct_from_limbs(ylo.first, ylo.second, yhi.first, yhi.second);
444 accumulator = accumulator.dbl() + curve::Group(x, y);
445 }
446
447 if (skew) {
448 accumulator = accumulator - curve::Group::one(&builder);
449 }
450
451 curve::GroupNative::affine_element result = accumulator.get_value();
452 curve::GroupNative::affine_element expected(curve::GroupNative::one * input_value);
453 EXPECT_EQ(result, expected);
454
455 bool proof_result = CircuitChecker::check(builder);
456 EXPECT_EQ(proof_result, true);
457}
458
459// Constant vs variable path tests
460TEST(PlookupTests, ConstantInputsConstantOutputs)
461{
463
464 // Use constant field elements (not witnesses)
465 field_ct left(&builder, bb::fr(0x12345678));
466 field_ct right(&builder, bb::fr(0xDEADBEEF));
467
468 ASSERT_TRUE(left.is_constant());
469 ASSERT_TRUE(right.is_constant());
470
471 const auto lookup = plookup_read::get_lookup_accumulators(MultiTableId::UINT32_XOR, left, right, true);
472
473 // Result should be constant
474 EXPECT_TRUE(lookup[ColumnIdx::C3][0].is_constant());
475
476 // Result should still be correct
477 uint32_t expected = 0x12345678 ^ 0xDEADBEEF;
478 EXPECT_EQ(lookup[ColumnIdx::C3][0].get_value(), bb::fr(expected));
479}
480
481TEST(PlookupTests, VariableInputsVariableOutputs)
482{
484
485 // Use witness field elements
486 field_ct left = witness_ct(&builder, bb::fr(0x12345678));
487 field_ct right = witness_ct(&builder, bb::fr(0xDEADBEEF));
488
489 ASSERT_FALSE(left.is_constant());
490 ASSERT_FALSE(right.is_constant());
491
492 const auto lookup = plookup_read::get_lookup_accumulators(MultiTableId::UINT32_XOR, left, right, true);
493
494 // Result should NOT be constant
495 EXPECT_FALSE(lookup[ColumnIdx::C3][0].is_constant());
496
497 // Result should still be correct
498 uint32_t expected = 0x12345678 ^ 0xDEADBEEF;
499 EXPECT_EQ(lookup[ColumnIdx::C3][0].get_value(), bb::fr(expected));
500
501 EXPECT_TRUE(CircuitChecker::check(builder));
502}
503
504TEST(PlookupTests, MixedConstantVariableInputs)
505{
507
508 // One constant, one variable
509 field_ct left(&builder, bb::fr(0x12345678));
510 field_ct right = witness_ct(&builder, bb::fr(0xDEADBEEF));
511
512 ASSERT_TRUE(left.is_constant());
513 ASSERT_FALSE(right.is_constant());
514
515 const auto lookup = plookup_read::get_lookup_accumulators(MultiTableId::UINT32_XOR, left, right, true);
516
517 // Result should NOT be constant (one input is variable)
518 EXPECT_FALSE(lookup[ColumnIdx::C3][0].is_constant());
519
520 // Result should still be correct
521 uint32_t expected = 0x12345678 ^ 0xDEADBEEF;
522 EXPECT_EQ(lookup[ColumnIdx::C3][0].get_value(), bb::fr(expected));
523
524 EXPECT_TRUE(CircuitChecker::check(builder));
525}
526
527// Regression: the eight SECP256K1 generator MultiTables previously declared slice_sizes = 512
528// while the basic tables only have 256 rows, so a key in [256, 511] would slip past the
529// slice bound in slice_input_using_variable_bases and OOB-index generator_*_table.
530TEST(PlookupTests, Secp256k1GeneratorSliceSizeBound)
531{
533 MultiTableId::SECP256K1_XLO, MultiTableId::SECP256K1_XHI, MultiTableId::SECP256K1_YLO,
534 MultiTableId::SECP256K1_YHI, MultiTableId::SECP256K1_XYPRIME, MultiTableId::SECP256K1_XLO_ENDO,
535 MultiTableId::SECP256K1_XHI_ENDO, MultiTableId::SECP256K1_XYPRIME_ENDO,
536 };
537 for (const auto id : ids) {
538 // Last valid key.
539 EXPECT_NO_THROW(plookup::get_lookup_accumulators(id, bb::fr(255), bb::fr(0), false));
540 // First out-of-range key — used to silently OOB-read.
541 EXPECT_THROW(plookup::get_lookup_accumulators(id, bb::fr(256), bb::fr(0), false), std::runtime_error);
542 // Mid-range OOB witness from the auditor's PoC.
543 EXPECT_THROW(plookup::get_lookup_accumulators(id, bb::fr(300), bb::fr(0), false), std::runtime_error);
544 }
545}
546
547// Checking the eight SECP256R1_FIXED_BASE multitables: (1) the slicer rejects keys
548// past the 2^136 / 2^120 bit budget, and (2) each slot's basic-table size equals its declared
549// slice_size. Widening either bound lets a prover witness a slice past the tail's basic-table range.
550TEST(PlookupTests, Secp256r1FixedBaseSliceSizeBound)
551{
554 // Bits per half = (NUM_WINDOWS − 1) full 7-bit windows + tail: 136 (lo) and 120 (hi).
555 constexpr size_t LO_BIT_BUDGET = ((Params::NUM_WINDOWS_LO - 1) * Params::WINDOW_BITS) + Params::WINDOW_BITS_LO_TAIL;
556 constexpr size_t HI_BIT_BUDGET = ((Params::NUM_WINDOWS_HI - 1) * Params::WINDOW_BITS) + Params::WINDOW_BITS_HI_TAIL;
557
558 const std::array<MultiTableId, 4> lo_ids{
559 MultiTableId::SECP256R1_FIXED_BASE_XLO_LO,
560 MultiTableId::SECP256R1_FIXED_BASE_XHI_LO,
561 MultiTableId::SECP256R1_FIXED_BASE_YLO_LO,
562 MultiTableId::SECP256R1_FIXED_BASE_YHI_LO,
563 };
564 const std::array<MultiTableId, 4> hi_ids{
565 MultiTableId::SECP256R1_FIXED_BASE_XLO_HI,
566 MultiTableId::SECP256R1_FIXED_BASE_XHI_HI,
567 MultiTableId::SECP256R1_FIXED_BASE_YLO_HI,
568 MultiTableId::SECP256R1_FIXED_BASE_YHI_HI,
569 };
570
571 // (1) Slicer rejects keys at the bit budget; accepts the immediately-preceding key.
572 const uint256_t lo_oob_key = uint256_t(1) << LO_BIT_BUDGET;
573 const uint256_t hi_oob_key = uint256_t(1) << HI_BIT_BUDGET;
574 for (const auto id : lo_ids) {
575 EXPECT_NO_THROW(plookup::get_lookup_accumulators(id, bb::fr(lo_oob_key - 1), bb::fr(0), false));
576 EXPECT_THROW(plookup::get_lookup_accumulators(id, bb::fr(lo_oob_key), bb::fr(0), false), std::runtime_error);
577 }
578 for (const auto id : hi_ids) {
579 EXPECT_NO_THROW(plookup::get_lookup_accumulators(id, bb::fr(hi_oob_key - 1), bb::fr(0), false));
580 EXPECT_THROW(plookup::get_lookup_accumulators(id, bb::fr(hi_oob_key), bb::fr(0), false), std::runtime_error);
581 }
582
583 // (2) Each slot's slice_size and basic-table size both equal Params::TABLE_SIZE_BIG, except the tail.
584 auto check_layout = [](MultiTableId mt_id, size_t num_windows, size_t tail_rows) {
585 SCOPED_TRACE("MultiTable id=" + std::to_string(static_cast<size_t>(mt_id)));
586 const auto& mt = get_multitable(mt_id);
587 ASSERT_EQ(mt.basic_table_ids.size(), num_windows);
588 ASSERT_EQ(mt.slice_sizes.size(), num_windows);
589 for (size_t slot = 0; slot < num_windows; ++slot) {
590 SCOPED_TRACE("slot=" + std::to_string(slot));
591 const size_t expected = (slot == num_windows - 1) ? tail_rows : Params::TABLE_SIZE_BIG;
592 EXPECT_EQ(mt.slice_sizes[slot], expected);
593 EXPECT_EQ(create_basic_table(mt.basic_table_ids[slot], 1).size(), expected);
594 }
595 };
596 for (const auto id : lo_ids) {
597 check_layout(id, Params::NUM_WINDOWS_LO, Params::TABLE_SIZE_LO_TAIL);
598 }
599 for (const auto id : hi_ids) {
600 check_layout(id, Params::NUM_WINDOWS_HI, Params::TABLE_SIZE_HI_TAIL);
601 }
602}
603
613TEST(PlookupTests, Sha256InputMultiTablesMatchBasicTableSizes)
614{
615 const std::array<MultiTableId, 3> sha256_input_tables = {
616 MultiTableId::SHA256_MAJ_INPUT,
617 MultiTableId::SHA256_CH_INPUT,
618 MultiTableId::SHA256_WITNESS_INPUT,
619 };
620
621 for (auto mt_id : sha256_input_tables) {
622 const auto& mt = get_multitable(mt_id);
623 ASSERT_EQ(mt.basic_table_ids.size(), mt.slice_sizes.size())
624 << "MultiTable id=" << static_cast<size_t>(mt_id)
625 << ": basic_table_ids and slice_sizes have different sizes";
626
627 for (size_t slot = 0; slot < mt.slice_sizes.size(); ++slot) {
628 const auto basic = create_basic_table(mt.basic_table_ids[slot], 1);
629 EXPECT_EQ(basic.size(), mt.slice_sizes[slot])
630 << "MultiTable id=" << static_cast<size_t>(mt_id) << " slot=" << slot << ": basic-table size "
631 << basic.size() << " != declared slice_size " << mt.slice_sizes[slot];
632 }
633 }
634}
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
virtual uint256_t get_random_uint256()=0
static field_t accumulate(const std::vector< field_t > &input)
Efficiently compute the sum of vector entries. Using big_add_gate we reduce the number of gates neede...
Definition field.cpp:1180
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:838
bool is_constant() const
Definition field.hpp:455
static plookup::ReadData< field_pt > get_lookup_accumulators(const plookup::MultiTableId id, const field_pt &key_a, const field_pt &key_b=0, const bool is_2_to_1_lookup=false)
Definition plookup.cpp:19
static std::pair< field_pt, field_pt > read_pair_from_table(const plookup::MultiTableId id, const field_pt &key)
Definition plookup.cpp:74
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
numeric::RNG & engine
std::vector< uint64_t > slice_input(const uint256_t &input, const uint64_t base, const size_t num_slices)
Decompose a uint256_t into digits in the given base (least-significant digit first)....
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
constexpr uint32_t rotate32(const uint32_t value, const uint32_t rotation)
Definition rotate.hpp:18
ReadData< bb::fr > get_lookup_accumulators(const MultiTableId id, const fr &key_a, const fr &key_b, const bool is_2_to_1_lookup)
Given a table ID and the key(s) for a key-value lookup, return the lookup accumulators.
BasicTable create_basic_table(const BasicTableId id, const size_t index)
const MultiTable & get_multitable(const MultiTableId id)
Return the multitable with the provided ID; construct all MultiTables if not constructed already.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:155
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
stdlib::field_t< Builder > field_ct
stdlib::witness_t< Builder > witness_ct
UltraCircuitBuilder Builder
size_t size() const
Definition types.hpp:352
Layout parameters for the secp256r1 fixed-base plookup decomposition.
VectorField result