Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
databus_lookup_relation_consistency.test.cpp
Go to the documentation of this file.
1
15#include <gtest/gtest.h>
16
17using namespace bb;
18
19using FF = fr;
20
26 // Wires used in read gates
27 FF w_l; // value being read
28 FF w_r; // index into bus column
29 FF databus_id; // id/index in the bus (for write term)
30
31 // Read gate selector
33
34 // Column selectors (determine which bus column is being read)
35 FF q_l; // calldata selector
36 FF q_r; // secondary_calldata selector
37 FF q_o; // return_data selector
38
39 // Calldata (bus_idx = 0)
44
45 // Secondary calldata (bus_idx = 1)
50
51 // Return data (bus_idx = 2)
56
81
82 // Create inputs representing a valid read gate for calldata
84 {
85 DatabusInputElements result{};
86
87 // Set up a read from calldata at index 5, value 42
88 result.w_l = FF(42); // value being read
89 result.w_r = FF(5); // index
90 result.databus_id = FF(5); // same index in the bus
91 result.calldata = FF(42); // value in bus matches
92
93 // Enable read gate for calldata
94 result.q_busread = FF(1);
95 result.q_l = FF(1); // calldata selector
96 result.q_r = FF(0);
97 result.q_o = FF(0);
98
99 // Read counts and tags
100 result.calldata_read_counts = FF(1);
101 result.calldata_read_tags = FF(1);
102
103 // Other columns inactive
104 result.secondary_calldata_read_counts = FF(0);
105 result.secondary_calldata_read_tags = FF(0);
106 result.return_data_read_counts = FF(0);
107 result.return_data_read_tags = FF(0);
108
109 return result;
110 }
111};
112
113class DatabusLookupRelationConsistency : public testing::Test {
114 public:
116 static constexpr size_t NUM_SUBRELATIONS = 9; // 3 subrelations per bus column, 3 columns
117
122 const DatabusInputElements& input_elements,
123 const RelationParameters<FF>& parameters)
124 {
126 Relation::accumulate(accumulator, input_elements, parameters, FF(1));
127 EXPECT_EQ(accumulator, expected_values);
128 }
129};
130
134static std::array<FF, 9> compute_expected_values(const DatabusInputElements& in, const RelationParameters<FF>& params)
135{
136 const auto& beta = params.beta;
137 const auto& gamma = params.gamma;
138
139 std::array<FF, 9> expected_values;
140 std::fill(expected_values.begin(), expected_values.end(), FF(0));
141
142 // Read term (same for all columns): value + index * beta + gamma
143 auto read_term = in.w_l + in.w_r * beta + gamma;
144
145 // Lambda to compute subrelations for a given bus column
146 auto compute_column_subrelations =
147 [&](size_t bus_idx, FF column_selector, FF bus_value, FF read_counts, FF read_tags, FF inverses) {
148 auto is_read_gate = in.q_busread * column_selector;
149 auto inverse_exists = is_read_gate + read_tags - is_read_gate * read_tags;
150 auto write_term = bus_value + in.databus_id * beta + gamma;
151
152 // Subrelation 1: Inverse correctness
153 expected_values[bus_idx * 3] = read_term * write_term * inverses - inverse_exists;
154
155 // Subrelation 2: Log-derivative lookup (no scaling factor since linearly dependent)
156 expected_values[bus_idx * 3 + 1] = (is_read_gate * write_term - read_counts * read_term) * inverses;
157
158 // Subrelation 3: Read tag boolean check
159 expected_values[bus_idx * 3 + 2] = read_tags * read_tags - read_tags;
160 };
161
162 // Bus column 0 (calldata)
163 compute_column_subrelations(
165
166 // Bus column 1 (secondary_calldata)
167 compute_column_subrelations(1,
168 in.q_r,
173
174 // Bus column 2 (return_data)
175 compute_column_subrelations(
177
178 return expected_values;
179}
180
186{
187 const auto run_test = [](bool random_inputs) {
190
191 const auto parameters = RelationParameters<FF>::get_random();
192 auto expected_values = compute_expected_values(in, parameters);
193
194 validate_relation_execution(expected_values, in, parameters);
195 };
196
197 run_test(/*random_inputs=*/false);
198 run_test(/*random_inputs=*/true);
199}
200
206{
207 const auto parameters = RelationParameters<FF>::get_random();
208
209 // Test with read_tag = 0
210 {
212 in.calldata_read_tags = FF(0);
215
217 Relation::accumulate(accumulator, in, parameters, FF(1));
218
219 // Boolean check subrelations should be 0
220 EXPECT_EQ(accumulator[2], FF(0));
221 EXPECT_EQ(accumulator[5], FF(0));
222 EXPECT_EQ(accumulator[8], FF(0));
223 }
224
225 // Test with read_tag = 1
226 {
228 in.calldata_read_tags = FF(1);
231
233 std::fill(accumulator.begin(), accumulator.end(), FF(0));
234 Relation::accumulate(accumulator, in, parameters, FF(1));
235
236 // Boolean check subrelations should be 0 (1*1 - 1 = 0)
237 EXPECT_EQ(accumulator[2], FF(0));
238 EXPECT_EQ(accumulator[5], FF(0));
239 EXPECT_EQ(accumulator[8], FF(0));
240 }
241}
242
248{
249 const auto parameters = RelationParameters<FF>::get_random();
250
252 // Set non-boolean read tags
253 in.calldata_read_tags = FF(2);
255 in.return_data_read_tags = FF(100);
256
258 std::fill(accumulator.begin(), accumulator.end(), FF(0));
259 Relation::accumulate(accumulator, in, parameters, FF(1));
260
261 // Boolean check subrelations should be non-zero
262 // For tag=2: 2*2 - 2 = 2 != 0
263 EXPECT_EQ(accumulator[2], FF(2));
264 // For tag=5: 5*5 - 5 = 20 != 0
265 EXPECT_EQ(accumulator[5], FF(20));
266 // For tag=100: 100*100 - 100 = 9900 != 0
267 EXPECT_EQ(accumulator[8], FF(9900));
268}
269
274{
275 const auto parameters = RelationParameters<FF>::get_random();
276
278 // All selectors and read tags are 0
279 in.q_busread = FF(0);
280 in.q_l = FF(0);
281 in.q_r = FF(0);
282 in.q_o = FF(0);
283 in.calldata_read_tags = FF(0);
286 in.calldata_read_counts = FF(0);
289
290 // Set other values to something non-zero to ensure they don't affect inactive gates
291 in.w_l = FF(42);
292 in.w_r = FF(5);
293 in.databus_id = FF(5);
294 in.calldata = FF(42);
295 in.calldata_inverses = FF(0); // inverse should be 0 when inactive
296
298 Relation::accumulate(accumulator, in, parameters, FF(1));
299
300 // When inactive (is_read_gate=0, read_tag=0), inverse_exists=0
301 // So inverse correctness becomes: 0 - 0 = 0
302 EXPECT_EQ(accumulator[0], FF(0));
303 EXPECT_EQ(accumulator[3], FF(0));
304 EXPECT_EQ(accumulator[6], FF(0));
305
306 // Lookup subrelation with inverses=0 should be 0
307 EXPECT_EQ(accumulator[1], FF(0));
308 EXPECT_EQ(accumulator[4], FF(0));
309 EXPECT_EQ(accumulator[7], FF(0));
310
311 // Boolean check with read_tag=0: 0*0 - 0 = 0
312 EXPECT_EQ(accumulator[2], FF(0));
313 EXPECT_EQ(accumulator[5], FF(0));
314 EXPECT_EQ(accumulator[8], FF(0));
315}
316
323{
324 const auto parameters = RelationParameters<FF>::get_random();
325 const auto& beta = parameters.beta;
326 const auto& gamma = parameters.gamma;
327
329
330 // Set up a read gate for calldata
331 in.q_busread = FF(1);
332 in.q_l = FF(1); // calldata selector
333 in.q_r = FF(0);
334 in.q_o = FF(0);
335
336 // Value and index
337 FF value = FF(42);
338 FF index = FF(5);
339 in.w_l = value; // value being read
340 in.w_r = index; // index
341 in.databus_id = index; // same index in the bus
342 in.calldata = value; // value in bus matches
343
344 // Compute the correct inverse
345 auto read_term = value + index * beta + gamma;
346 auto write_term = value + index * beta + gamma; // same since value and index match
347 auto inverse = (read_term * write_term).invert();
348 in.calldata_inverses = inverse;
349
350 // Read tag = 1 (this row is being read)
351 in.calldata_read_tags = FF(1);
352 in.calldata_read_counts = FF(1);
353
354 // Other columns inactive
360 in.return_data_inverses = FF(0);
361
363
364 Relation::accumulate(accumulator, in, parameters, FF(1));
365
366 // Inverse correctness subrelation should be 0 (satisfied)
367 // I * read_term * write_term - inverse_exists = 0
368 // inverse * read_term * write_term = 1
369 // inverse_exists = is_read_gate + read_tag - is_read_gate * read_tag = 1 + 1 - 1 = 1
370 EXPECT_EQ(accumulator[0], FF(0));
371
372 // Read tag boolean check should be 0 (tag = 1 is boolean)
373 EXPECT_EQ(accumulator[2], FF(0));
374
375 // Other columns should have 0 subrelations (inactive)
376 EXPECT_EQ(accumulator[3], FF(0)); // secondary_calldata inverse correctness
377 EXPECT_EQ(accumulator[5], FF(0)); // secondary_calldata boolean check
378 EXPECT_EQ(accumulator[6], FF(0)); // return_data inverse correctness
379 EXPECT_EQ(accumulator[8], FF(0)); // return_data boolean check
380}
381
386TEST_F(DatabusLookupRelationConsistency, MismatchedReadWriteTerms)
387{
388 const auto parameters = RelationParameters<FF>::get_random();
389 const auto& beta = parameters.beta;
390 const auto& gamma = parameters.gamma;
391
393
394 // Set up a read gate for calldata
395 in.q_busread = FF(1);
396 in.q_l = FF(1);
397 in.q_r = FF(0);
398 in.q_o = FF(0);
399
400 // Value being read differs from value in bus!
401 FF read_value = FF(42);
402 FF bus_value = FF(100); // Different!
403 FF index = FF(5);
404
405 in.w_l = read_value;
406 in.w_r = index;
407 in.databus_id = index;
408 in.calldata = bus_value;
409
410 // Compute inverse based on read_term (which uses w_l, w_r) and write_term (which uses calldata)
411 auto read_term = read_value + index * beta + gamma;
412 auto write_term = bus_value + index * beta + gamma;
413 // Even with "correct" inverse, the lookup will fail because terms don't match
414 auto inverse = (read_term * write_term).invert();
415 in.calldata_inverses = inverse;
416
417 in.calldata_read_tags = FF(1);
418 in.calldata_read_counts = FF(1);
424 in.return_data_inverses = FF(0);
425
427
428 Relation::accumulate(accumulator, in, parameters, FF(1));
429
430 // Inverse correctness subrelation should still be 0 (inverse is computed correctly for these terms)
431 EXPECT_EQ(accumulator[0], FF(0));
432
433 // But the lookup subrelation (index 1) will be non-zero because read_term != write_term
434 // This is where the soundness comes in - the sum across the trace won't be zero
435 // For a single row: (read_selector * write_term - read_count * read_term) * inverse
436 // = (1 * write_term - 1 * read_term) * inverse
437 // = (write_term - read_term) * inverse != 0 when read_term != write_term
438 FF expected_lookup = (write_term - read_term) * inverse;
439 EXPECT_EQ(accumulator[1], expected_lookup);
440 EXPECT_NE(accumulator[1], FF(0)); // Confirm it's non-zero
441}
static void validate_relation_execution(const std::array< FF, NUM_SUBRELATIONS > &expected_values, const DatabusInputElements &input_elements, const RelationParameters< FF > &parameters)
Validate that the relation's accumulate function produces expected values.
Log-derivative lookup argument relation for establishing DataBus reads.
static void accumulate(ContainerOverSubrelations &accumulator, const AllEntities &in, const Parameters &params, const FF &scaling_factor)
Accumulate the log derivative databus lookup argument subrelation contributions for each databus colu...
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:185
field< Bn254FrParams > fr
Definition fr.hpp:174
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Input elements for DatabusLookupRelation testing.
static DatabusInputElements get_valid_calldata_read()
Container for parameters used by the grand product (permutation, lookup) Honk relations.
static RelationParameters get_random()
static field random_element(numeric::RNG *engine=nullptr) noexcept