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(stdlib_plookup, 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
63 bool result = CircuitChecker::check(builder);
64
65 EXPECT_EQ(result, true);
66}
67
68TEST(stdlib_plookup, 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
132 bool result = CircuitChecker::check(builder);
133
134 EXPECT_EQ(result, true);
135}
136
137TEST(stdlib_plookup, 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
190 bool result = CircuitChecker::check(builder);
191
192 EXPECT_EQ(result, true);
193}
194
195TEST(stdlib_plookup, 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
248 bool result = CircuitChecker::check(builder);
249
250 EXPECT_EQ(result, true);
251}
252
253TEST(stdlib_plookup, 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
311 bool result = CircuitChecker::check(builder);
312
313 EXPECT_EQ(result, true);
314}
315
316TEST(stdlib_plookup, 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
353 bool result = CircuitChecker::check(builder);
354
355 EXPECT_EQ(result, true);
356}
357
358TEST(stdlib_plookup, 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::fq_ct x = curve::fq_ct::unsafe_construct_from_limbs(xlo.first, xlo.second, xhi.first, xhi.second);
402 curve::fq_ct y = curve::fq_ct::unsafe_construct_from_limbs(ylo.first, ylo.second, yhi.first, yhi.second);
403
404 const auto res = curve::g1_ct(x, y).get_value();
405 curve::fr scalar(i);
406 scalar = scalar + scalar;
407 scalar = scalar - 255;
409
410 EXPECT_EQ(res, expec);
411 }
412 curve::g1_ct accumulator;
413 {
414 const auto xlo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XLO, circuit_naf_values[0]);
415 const auto xhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XHI, circuit_naf_values[0]);
416 const auto ylo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YLO, circuit_naf_values[0]);
417 const auto yhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YHI, circuit_naf_values[0]);
418
419 curve::fq_ct x = curve::fq_ct::unsafe_construct_from_limbs(xlo.first, xlo.second, xhi.first, xhi.second);
420 curve::fq_ct y = curve::fq_ct::unsafe_construct_from_limbs(ylo.first, ylo.second, yhi.first, yhi.second);
421 accumulator = curve::g1_ct(x, y);
422 }
423 for (size_t i = 1; i < circuit_naf_values.size(); ++i) {
424 accumulator = accumulator.dbl();
425 accumulator = accumulator.dbl();
426 accumulator = accumulator.dbl();
427 accumulator = accumulator.dbl();
428 accumulator = accumulator.dbl();
429 accumulator = accumulator.dbl();
430 accumulator = accumulator.dbl();
431
432 const auto xlo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XLO, circuit_naf_values[i]);
433 const auto xhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XHI, circuit_naf_values[i]);
434 const auto ylo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YLO, circuit_naf_values[i]);
435 const auto yhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YHI, circuit_naf_values[i]);
436 curve::fq_ct x = curve::fq_ct::unsafe_construct_from_limbs(xlo.first, xlo.second, xhi.first, xhi.second);
437 curve::fq_ct y = curve::fq_ct::unsafe_construct_from_limbs(ylo.first, ylo.second, yhi.first, yhi.second);
438 accumulator = accumulator.dbl() + curve::g1_ct(x, y);
439 }
440
441 if (skew) {
442 accumulator = accumulator - curve::g1_ct::one(&builder);
443 }
444
445 curve::g1::affine_element result = accumulator.get_value();
446 curve::g1::affine_element expected(curve::g1::one * input_value);
447 EXPECT_EQ(result, expected);
448
449 bool proof_result = CircuitChecker::check(builder);
450 EXPECT_EQ(proof_result, true);
451}
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
static constexpr element one
Definition group.hpp:46
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:1167
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:828
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:70
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)
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:190
constexpr uint32_t rotate32(const uint32_t value, const uint32_t rotation)
Definition rotate.hpp:18
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:174
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
stdlib::field_t< Builder > field_ct
stdlib::witness_t< Builder > witness_ct
UltraCircuitBuilder Builder