Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ultra_circuit_checker.cpp
Go to the documentation of this file.
7#include <unordered_set>
8
9namespace bb {
10
11template <> auto UltraCircuitChecker::init_empty_values<UltraCircuitBuilder_<UltraExecutionTraceBlocks>>()
12{
14}
15
16template <> auto UltraCircuitChecker::init_empty_values<MegaCircuitBuilder_<bb::fr>>()
17{
18 return MegaFlavor::AllValues{};
19}
20
21template <>
24{
25 // Create a copy of the input circuit
27 if (!builder.circuit_finalized) { // avoid warnings about finalizing an already finalized circuit
29 }
30
31 return builder;
32}
33
34template <>
35MegaCircuitBuilder_<bb::fr> UltraCircuitChecker::prepare_circuit<MegaCircuitBuilder_<bb::fr>>(
36 const MegaCircuitBuilder_<bb::fr>& builder_in)
37{
38 // Create a copy of the input circuit
39 MegaCircuitBuilder_<bb::fr> builder{ builder_in };
40
41 // Deepcopy the opqueue to avoid modifying the original one
42 builder.op_queue = std::make_shared<ECCOpQueue>(*builder.op_queue);
43
44 if (!builder.circuit_finalized) { // avoid warnings about finalizing an already finalized circuit
45 builder.finalize_circuit();
46 }
47
48 return builder;
49}
50
51template <typename Builder> bool UltraCircuitChecker::check(const Builder& builder_in)
52{
53 if (builder_in.failed()) {
54 info("CircuitChecker: circuit contains invalid witnesses: ", builder_in.err());
55 }
56
58
59 // Construct a hash table for lookup table entries to efficiently determine if a lookup gate is valid
60 LookupHashTable lookup_hash_table;
61 for (const auto& table : builder.get_lookup_tables()) {
62 const FF table_index(table.table_index);
63 for (size_t i = 0; i < table.size(); ++i) {
64 lookup_hash_table.insert({ table.column_1[i], table.column_2[i], table.column_3[i], table_index });
65 }
66 }
67
68 // Instantiate structs used for checking tag and memory record correctness
69 TagCheckData tag_data;
70 MemoryCheckData memory_data{ builder };
71
72 bool result = true;
73 size_t block_idx = 0;
74 for (auto& block : builder.blocks.get()) {
75 result = result && check_block(builder, block, tag_data, memory_data, lookup_hash_table);
76 if (!result) {
77#ifndef FUZZING_DISABLE_WARNINGS
78 info("Failed at block idx = ", block_idx);
79#else
80 (void)block_idx;
81#endif
82 return false;
83 }
84 block_idx++;
85 }
86
87#ifdef ULTRA_FUZZ
88 result = result & relaxed_check_delta_range_relation(builder);
89 if (!result) {
90 return false;
91 }
92 result = result & relaxed_check_memory_relation(builder);
93 if (!result) {
94 return false;
95 }
96#endif
97#ifndef ULTRA_FUZZ
98 // Tag check is only expected to pass after entire execution trace (all blocks) have been processed
99 result = result && check_tag_data(tag_data);
100 if (!result) {
101 info("Failed tag check.");
102 return false;
103 }
104 // ROM-LogUp sum identity: the signed multiplicities and inverses summed across the trace must vanish.
105 // Subrelation 7 of MemoryRelation produces a per-row contribution that has been accumulated into
106 // memory_data.rom_logup_sum throughout the block iteration.
107 if (memory_data.rom_logup_sum != 0) {
108 info("Failed ROM-LogUp sum identity.");
109 return false;
110 }
111#endif
112
113 return result;
114};
115
116template <typename Builder>
118 auto& block,
119 TagCheckData& tag_data,
120 MemoryCheckData& memory_data,
121 LookupHashTable& lookup_hash_table)
122{
123 // Initialize empty AllValues of the correct Flavor based on Builder type; for input to Relation::accumulate
124 auto values = init_empty_values<Builder>();
125 Params params;
126 params.eta = memory_data.eta; // used in Memory relation for RAM/ROM consistency
127 params.eta_two = memory_data.eta_two;
128 params.eta_three = memory_data.eta_three;
129 params.rom_logup_gamma = memory_data.rom_logup_gamma;
130
131 auto report_fail = [&](const char* message, size_t row_idx) {
132#ifndef FUZZING_DISABLE_WARNINGS
133 info(message, row_idx);
134#else
135 (void)message;
136 (void)row_idx;
137#endif
138#ifdef CHECK_CIRCUIT_STACKTRACES
139 block.stack_traces.print(row_idx);
140#endif
141 return false;
142 };
143
144 // Perform checks on each gate defined in the builder
145 bool result = true;
146 for (size_t idx = 0; idx < block.size(); ++idx) {
147
148 populate_values(builder, block, values, tag_data, memory_data, idx);
149
150 result = result && check_relation<Arithmetic>(values, params);
151 if (!result) {
152 return report_fail("Failed Arithmetic relation at row idx = ", idx);
153 }
154 if constexpr (IsMegaBuilder<Builder>) {
155 result = result && check_relation<BilinearBatchedEq>(values, params);
156 if (!result) {
157 return report_fail("Failed BilinearBatchedEq relation at row idx = ", idx);
158 }
159 }
160 result = result && check_relation<Elliptic>(values, params);
161 if (!result) {
162 return report_fail("Failed Elliptic relation at row idx = ", idx);
163 }
164#ifndef ULTRA_FUZZ
165 result = result && check_memory_relation_with_logup<Memory>(values, params, memory_data);
166 if (!result) {
167 return report_fail("Failed Memory relation at row idx = ", idx);
168 }
169 result = result && check_relation<NonNativeField>(values, params);
170 if (!result) {
171 return report_fail("Failed NonNativeField relation at row idx = ", idx);
172 }
173 result = result && check_relation<DeltaRangeConstraint>(values, params);
174 if (!result) {
175 return report_fail("Failed DeltaRangeConstraint relation at row idx = ", idx);
176 }
177#else
178 // Bigfield related nnf gates
179 if (values.q_nnf() == 1) {
180 bool f0 = values.q_o() == 1 && (values.q_4() == 1 || values.q_m() == 1);
181 bool f1 = values.q_r() == 1 && (values.q_o() == 1 || values.q_4() == 1 || values.q_m() == 1);
182 if (f0 && f1) {
183 result = result && check_relation<NonNativeField>(values, params);
184 if (!result) {
185 return report_fail("Failed NonNativeField relation at row idx = ", idx);
186 }
187 }
188 }
189#endif
190 result = result && check_lookup(values, lookup_hash_table);
191 if (!result) {
192 return report_fail("Failed Lookup check relation at row idx = ", idx);
193 }
194 result = result && check_relation<PoseidonExternal>(values, params);
195 if (!result) {
196 return report_fail("Failed PoseidonExternal relation at row idx = ", idx);
197 }
198
199 if constexpr (!IsMegaBuilder<Builder>) {
200 result = result && check_relation<PoseidonInternal>(values, params);
201 if (!result) {
202 return report_fail("Failed PoseidonInternal relation at row idx = ", idx);
203 }
204 }
205 if constexpr (IsMegaBuilder<Builder>) {
206 result = result && check_relation<PoseidonInitialExternal>(values, params);
207 if (!result) {
208 return report_fail("Failed PoseidonInitialExternal relation at row idx = ", idx);
209 }
210 result = result && check_relation<PoseidonQuadInternal>(values, params);
211 if (!result) {
212 return report_fail("Failed PoseidonQuadInternal relation at row idx = ", idx);
213 }
214 result = result && check_relation<PoseidonQuadInternalTerminal>(values, params);
215 if (!result) {
216 return report_fail("Failed PoseidonQuadInternalTerminal relation at row idx = ", idx);
217 }
218 result = result && check_relation<PoseidonTransitionEntry>(values, params);
219 if (!result) {
220 return report_fail("Failed PoseidonTransitionEntry relation at row idx = ", idx);
221 }
223 if (!result) {
224 return report_fail("Failed databus read at row idx = ", idx);
225 }
226 // Note: EccOpQueueRelation is not checked here because it simply establishes that the ecc_op_wire
227 // polynomials contain copies of the conventional wire data in the ecc_op region (and are zero elsewhere) so
228 // there is nothing to check at the level of the builder.
229 }
230 if (!result) {
231 return report_fail("Failed at row idx = ", idx);
232 }
233 }
234
235 return result;
236};
237
238template <typename Relation, typename Builder, typename Block>
240{
241 auto values = init_empty_values<Builder>();
242 TagCheckData tag_data;
243 MemoryCheckData memory_data(builder);
244 populate_values(builder, block, values, tag_data, memory_data, row_idx);
245
246 Params params;
247 params.eta = memory_data.eta;
248 params.eta_two = memory_data.eta_two;
249 params.eta_three = memory_data.eta_three;
250 return check_relation<Relation>(values, params);
251}
252
253template <typename Relation> bool UltraCircuitChecker::check_relation(auto& values, auto& params)
254{
255 // Define zero initialized array to store the evaluation of each sub-relation
256 using SubrelationEvaluations = typename Relation::SumcheckArrayOfValuesOverSubrelations;
257 SubrelationEvaluations subrelation_evaluations;
258 for (auto& eval : subrelation_evaluations) {
259 eval = 0;
260 }
261
262 // Evaluate each subrelation in the relation
263 Relation::accumulate(subrelation_evaluations, values, params, /*scaling_factor=*/1);
264
265 // Ensure each linearly-independent subrelation evaluates to zero per-row. Linearly-dependent subrelations
266 // are summed across the trace by the caller (see check_memory_relation_with_logup) and must not be
267 // checked per-row, since individual row contributions need not vanish.
268 bool result = true;
269 constexpr_for<0, std::tuple_size_v<SubrelationEvaluations>, 1>([&]<size_t I>() {
270 if constexpr (subrelation_is_linearly_independent<Relation, I>()) {
271 if (std::get<I>(subrelation_evaluations) != 0) {
272 result = false;
273 }
274 }
275 });
276 return result;
277}
278
279// Memory relation has a linearly-dependent subrelation (#7, ROM-LogUp sum identity) whose contributions must
280// be summed across rows. This variant of `check_relation` runs Memory, checks the per-row linearly-independent
281// subrelations, and adds subrelation 7's per-row value to the supplied accumulator. The accumulator is checked
282// against zero at the end of `check_circuit`.
283template <typename Memory>
284bool UltraCircuitChecker::check_memory_relation_with_logup(auto& values, auto& params, MemoryCheckData& memory_data)
285{
286 using SubrelationEvaluations = typename Memory::SumcheckArrayOfValuesOverSubrelations;
287 SubrelationEvaluations subrelation_evaluations;
288 for (auto& eval : subrelation_evaluations) {
289 eval = 0;
290 }
291 Memory::accumulate(subrelation_evaluations, values, params, /*scaling_factor=*/1);
292
293 bool result = true;
294 constexpr_for<0, std::tuple_size_v<SubrelationEvaluations>, 1>([&]<size_t I>() {
295 if constexpr (subrelation_is_linearly_independent<Memory, I>()) {
296 if (std::get<I>(subrelation_evaluations) != 0) {
297 result = false;
298 }
299 } else {
300 // Linearly-dependent: contribute to the cross-row accumulator. Currently only subrelation 7
301 // (ROM-LogUp sum identity) is linearly-dependent in MemoryRelation.
302 memory_data.rom_logup_sum += std::get<I>(subrelation_evaluations);
303 }
304 });
305 return result;
306}
307
308bool UltraCircuitChecker::check_lookup(auto& values, auto& lookup_hash_table)
309{
310 // If this is a lookup gate, check the inputs are in the hash table containing all table entries
311 if (!values.q_lookup().is_zero()) {
312 return lookup_hash_table.contains({ values.w_l() + values.q_r() * values.w_l_shift(),
313 values.w_r() + values.q_m() * values.w_r_shift(),
314 values.w_o() + values.q_c() * values.w_o_shift(),
315 values.q_o() });
316 }
317 return true;
318};
319
320template <typename Builder> bool UltraCircuitChecker::check_databus_read(auto& values, Builder& builder)
321{
322 if (!values.q_busread().is_zero()) {
323 // Extract the {index, value} pair from the read gate inputs
324 auto raw_read_idx = static_cast<size_t>(uint256_t(values.w_r()));
325 auto value = values.w_l();
326
327 // Locate the bus column being read (exactly one selector should be active on a busread row) and look up the
328 // expected value from the builder's bus vector.
329 auto bus_selectors = values.get_databus_selectors();
330 FF bus_value{};
331 bool read_matched = false;
332 for (size_t bus_idx = 0; bus_idx < bus_selectors.size(); ++bus_idx) {
333 if (bus_selectors[bus_idx] == 1) {
334 const auto& bus_vec = builder.get_bus_vector(bus_idx);
335 bus_value = builder.get_variable(bus_vec[raw_read_idx]);
336 read_matched = true;
337 }
338 }
339 BB_ASSERT(read_matched);
340 return (value == bus_value);
341 }
342 return true;
343};
344
346{
347 return tag_data.left_product == tag_data.right_product;
348};
349
350template <typename Builder>
352 Builder& builder, auto& block, auto& values, TagCheckData& tag_data, MemoryCheckData& memory_data, size_t idx)
353{
354 // Function to quickly update tag products and encountered variable set by index and value
355 auto update_tag_check_data = [&](const size_t variable_index, const FF& value) {
356 size_t real_index = builder.real_variable_index[variable_index];
357 // Check to ensure that we are not including a variable twice
358 if (tag_data.encountered_variables.contains(real_index)) {
359 return;
360 }
361 uint32_t tag_in = builder.real_variable_tags[real_index];
362 if (tag_in != DEFAULT_TAG) {
363 uint32_t tag_out = builder.tau().at(tag_in);
364 tag_data.left_product *= value + tag_data.gamma * FF(tag_in);
365 tag_data.right_product *= value + tag_data.gamma * FF(tag_out);
366 tag_data.encountered_variables.insert(real_index);
367 }
368 };
369
370 // A lambda function for computing a memory record term of the form w3 * eta_three + w2 * eta_two + w1 * eta
371 auto compute_memory_record_term =
372 [](const FF& w_1, const FF& w_2, const FF& w_3, const FF& eta, const FF& eta_two, FF& eta_three) {
373 return (w_3 * eta_three + w_2 * eta_two + w_1 * eta);
374 };
375
376 // The ROM-LogUp inverse helper at a row with index `w_1`, value `w_2`, and array id `q_c`.
377 // Mirrors `add_rom_logup_inverses_to_wire_4` in oink_prover.cpp; kept consistent so check_circuit can
378 // validate the memory relation without invoking the prover.
379 auto compute_rom_logup_inverse =
380 [](const FF& w_1, const FF& w_2, const FF& q_c, const FF& eta, const FF& eta_two, const FF& rom_logup_gamma)
381 -> FF {
382 const FF denom = rom_logup_gamma + w_1 + eta * w_2 + eta_two * q_c;
383 return denom.invert();
384 };
385
386 // Set wire values. Wire 4 is treated specially since it may contain memory records
387 values.w_l() = builder.get_variable(block.w_l()[idx]);
388 values.w_r() = builder.get_variable(block.w_r()[idx]);
389 values.w_o() = builder.get_variable(block.w_o()[idx]);
390 // Note: memory_data contains indices into the block to which RAM/ROM gates were added so we need to check that
391 // we are indexing into the correct block before updating the w_4 value.
392 const bool is_ram_rom_block = (&block == &builder.blocks.memory);
393 if (is_ram_rom_block && memory_data.read_record_gates.contains(idx)) {
394 values.w_4() = compute_memory_record_term(
395 values.w_l(), values.w_r(), values.w_o(), memory_data.eta, memory_data.eta_two, memory_data.eta_three);
396 } else if (is_ram_rom_block && memory_data.write_record_gates.contains(idx)) {
397 values.w_4() =
398 compute_memory_record_term(
399 values.w_l(), values.w_r(), values.w_o(), memory_data.eta, memory_data.eta_two, memory_data.eta_three) +
400 FF::one();
401 } else if (is_ram_rom_block && memory_data.rom_logup_gates.contains(idx)) {
402 values.w_4() =
403 compute_rom_logup_inverse(values.w_l(),
404 values.w_r(),
405 block.q_c()[idx], // q_c is a selector value (the array id), not a witness
406 memory_data.eta,
407 memory_data.eta_two,
408 memory_data.rom_logup_gamma);
409 } else {
410 values.w_4() = builder.get_variable(block.w_4()[idx]);
411 }
412
413 // Set shifted wire values. Again, wire 4 is treated specially. On final row, set shift values to zero
414 if (idx < block.size() - 1) {
415 values.w_l_shift() = builder.get_variable(block.w_l()[idx + 1]);
416 values.w_r_shift() = builder.get_variable(block.w_r()[idx + 1]);
417 values.w_o_shift() = builder.get_variable(block.w_o()[idx + 1]);
418 if (is_ram_rom_block && memory_data.read_record_gates.contains(idx + 1)) {
419 values.w_4_shift() = compute_memory_record_term(values.w_l_shift(),
420 values.w_r_shift(),
421 values.w_o_shift(),
422 memory_data.eta,
423 memory_data.eta_two,
424 memory_data.eta_three);
425 } else if (is_ram_rom_block && memory_data.write_record_gates.contains(idx + 1)) {
426 values.w_4_shift() = compute_memory_record_term(values.w_l_shift(),
427 values.w_r_shift(),
428 values.w_o_shift(),
429 memory_data.eta,
430 memory_data.eta_two,
431 memory_data.eta_three) +
432 FF::one();
433 } else if (is_ram_rom_block && memory_data.rom_logup_gates.contains(idx + 1)) {
434 values.w_4_shift() = compute_rom_logup_inverse(values.w_l_shift(),
435 values.w_r_shift(),
436 block.q_c()[idx + 1],
437 memory_data.eta,
438 memory_data.eta_two,
439 memory_data.rom_logup_gamma);
440 } else {
441 values.w_4_shift() = builder.get_variable(block.w_4()[idx + 1]);
442 }
443 } else {
444 values.w_l_shift() = 0;
445 values.w_r_shift() = 0;
446 values.w_o_shift() = 0;
447 values.w_4_shift() = 0;
448 }
449
450 // Update tag check data
451 update_tag_check_data(block.w_l()[idx], values.w_l());
452 update_tag_check_data(block.w_r()[idx], values.w_r());
453 update_tag_check_data(block.w_o()[idx], values.w_o());
454 update_tag_check_data(block.w_4()[idx], values.w_4());
455
456 // Set selector values
457 values.q_m() = block.q_m()[idx];
458 values.q_c() = block.q_c()[idx];
459 values.q_l() = block.q_1()[idx];
460 values.q_r() = block.q_2()[idx];
461 values.q_o() = block.q_3()[idx];
462 values.q_4() = block.q_4()[idx];
463 values.q_arith() = read_gate_selector(block, GateKind::Arith, idx);
464 values.q_delta_range() = read_gate_selector(block, GateKind::DeltaRange, idx);
465 values.q_elliptic() = read_gate_selector(block, GateKind::Elliptic, idx);
466 values.q_memory() = read_gate_selector(block, GateKind::Memory, idx);
467 values.q_nnf() = read_gate_selector(block, GateKind::Nnf, idx);
468 values.q_lookup() = read_gate_selector(block, GateKind::Lookup, idx);
469 values.q_poseidon2_external() = read_gate_selector(block, GateKind::Poseidon2Ext, idx);
470 if constexpr (IsMegaBuilder<Builder>) {
471 values.q_5() = block.q_5()[idx];
472 values.q_bilinear_batched_eq() = read_gate_selector(block, GateKind::BilinearBatchedEq, idx);
473 values.q_busread() = read_gate_selector(block, GateKind::BusRead, idx);
474 values.q_poseidon2_external_initial() = read_gate_selector(block, GateKind::Poseidon2ExtInitial, idx);
475 values.q_poseidon2_quad_internal() = read_gate_selector(block, GateKind::Poseidon2QuadInt, idx);
476 values.q_poseidon2_quad_internal_terminal() =
478 values.q_poseidon2_transition_entry() = read_gate_selector(block, GateKind::Poseidon2TransitionEntry, idx);
479 } else {
480 values.q_poseidon2_internal() = read_gate_selector(block, GateKind::Poseidon2Int, idx);
481 }
482}
483
484#ifdef ULTRA_FUZZ
485
497template <typename Builder> bool UltraCircuitChecker::relaxed_check_delta_range_relation(Builder& builder)
498{
499 std::unordered_map<uint32_t, uint64_t> range_tags;
500 for (const auto& list : builder.range_lists) {
501 range_tags[list.second.range_tag] = list.first;
502 }
503
504 // Unprocessed blocks check
505 for (uint32_t i = 0; i < builder.real_variable_tags.size(); i++) {
506 uint32_t tag = builder.real_variable_tags[i];
507 if (tag != 0 && range_tags.contains(tag)) {
508 uint256_t range = static_cast<uint256_t>(range_tags[tag]);
509 uint256_t value = static_cast<uint256_t>(builder.get_variable(i));
510 if (value > range) {
511#ifndef FUZZING_DISABLE_WARNINGS
512 info("Failed range constraint on variable with index = ", i, ": ", value, " > ", range);
513#endif
514 return false;
515 }
516 }
517 }
518
519 // Processed blocks check
520 auto block = builder.blocks.delta_range;
521 for (size_t idx = 0; idx < block.size(); idx++) {
522 if (block.gate_selector_for(GateKind::DeltaRange)[idx] == 0) {
523 continue;
524 }
525 bb::fr w1 = builder.get_variable(block.w_l()[idx]);
526 bb::fr w2 = builder.get_variable(block.w_r()[idx]);
527 bb::fr w3 = builder.get_variable(block.w_o()[idx]);
528 bb::fr w4 = builder.get_variable(block.w_4()[idx]);
529 bb::fr w5 = idx == block.size() - 1 ? builder.get_variable(0) : builder.get_variable(block.w_l()[idx + 1]);
530
531 uint256_t delta = static_cast<uint256_t>(w2 - w1);
532 if (delta > 3) {
533#ifndef FUZZING_DISABLE_WARNINGS
534 info("Failed sort constraint relation at row idx = ", idx, " with delta1 = ", delta);
535 info(w1 - w2);
536#endif
537 return false;
538 }
539 delta = static_cast<uint256_t>(w3 - w2);
540 if (delta > 3) {
541#ifndef FUZZING_DISABLE_WARNINGS
542 info("Failed sort constraint relation at row idx = ", idx, " with delta2 = ", delta);
543#endif
544 return false;
545 }
546 delta = static_cast<uint256_t>(w4 - w3);
547 if (delta > 3) {
548#ifndef FUZZING_DISABLE_WARNINGS
549 info("Failed sort constraint at row idx = ", idx, " with delta3 = ", delta);
550#endif
551 return false;
552 }
553 delta = static_cast<uint256_t>(w5 - w4);
554 if (delta > 3) {
555#ifndef FUZZING_DISABLE_WARNINGS
556 info("Failed sort constraint at row idx = ", idx, " with delta4 = ", delta);
557#endif
558 return false;
559 }
560 }
561 return true;
562}
563
577template <typename Builder> bool UltraCircuitChecker::relaxed_check_memory_relation(Builder& builder)
578{
579 for (size_t i = 0; i < builder.rom_ram_logic.rom_arrays.size(); i++) {
580 auto rom_array = builder.rom_ram_logic.rom_arrays[i];
581
582 // check set and read ROM records
583 for (auto& rr : rom_array.records) {
584 uint32_t value_witness_1 = rr.value_column1_witness;
585 uint32_t value_witness_2 = rr.value_column2_witness;
586 uint32_t index = static_cast<uint32_t>(builder.get_variable(rr.index_witness));
587
588 uint32_t table_witness_1 = rom_array.state[index][0];
589 uint32_t table_witness_2 = rom_array.state[index][1];
590
591 if (builder.get_variable(value_witness_1) != builder.get_variable(table_witness_1)) {
592#ifndef FUZZING_DISABLE_WARNINGS
593 info("Failed SET/Read ROM[0] in table = ", i, " at idx = ", index);
594#endif
595 return false;
596 }
597 if (builder.get_variable(value_witness_2) != builder.get_variable(table_witness_2)) {
598#ifndef FUZZING_DISABLE_WARNINGS
599 info("Failed SET/Read ROM[1] in table = ", i, " at idx = ", index);
600#endif
601 return false;
602 }
603 }
604 }
605
606 for (size_t i = 0; i < builder.rom_ram_logic.ram_arrays.size(); i++) {
607 auto ram_array = builder.rom_ram_logic.ram_arrays[i];
608
609 std::vector<uint32_t> tmp_state(ram_array.state.size());
610
611 // Simulate the memory call trace
612 for (auto& rr : ram_array.records) {
613 uint32_t index = static_cast<uint32_t>(builder.get_variable(rr.index_witness));
614 uint32_t value_witness = rr.value_witness;
615 auto access_type = rr.access_type;
616
617 uint32_t table_witness = tmp_state[index];
618
619 switch (access_type) {
621 if (builder.get_variable(value_witness) != builder.get_variable(table_witness)) {
622#ifndef FUZZING_DISABLE_WARNINGS
623 info("Failed RAM read in table = ", i, " at idx = ", index);
624#endif
625 return false;
626 }
627 break;
629 tmp_state[index] = value_witness;
630 break;
631 default:
632 return false;
633 }
634 }
635
636 if (tmp_state != ram_array.state) {
637#ifndef FUZZING_DISABLE_WARNINGS
638 info("Failed RAM final state check at table = ", i);
639#endif
640 return false;
641 }
642 }
643 return true;
644}
645#endif
646
647// Template method instantiations for each check method
648template bool UltraCircuitChecker::check<UltraCircuitBuilder_<UltraExecutionTraceBlocks>>(
649 const UltraCircuitBuilder_<UltraExecutionTraceBlocks>& builder_in);
650template bool UltraCircuitChecker::check<MegaCircuitBuilder_<bb::fr>>(const MegaCircuitBuilder_<bb::fr>& builder_in);
651
652// Instantiations of check_relation_at_row for the Mega Poseidon2 boundary relations exercised by the compressed
653// internal-round soundness tests.
654template bool UltraCircuitChecker::check_relation_at_row<Poseidon2ExternalRelation<bb::fr>>(
656template bool UltraCircuitChecker::check_relation_at_row<Poseidon2TransitionEntryRelation<bb::fr>>(
658template bool UltraCircuitChecker::check_relation_at_row<Poseidon2QuadInternalRelation<bb::fr>>(
660template bool UltraCircuitChecker::check_relation_at_row<Poseidon2QuadInternalTerminalRelation<bb::fr>>(
662} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
ArrayOfValues< FF, RelationImpl::SUBRELATION_PARTIAL_LENGTHS > SumcheckArrayOfValuesOverSubrelations
std::unordered_set< Key, HashFunction > LookupHashTable
static bool check_relation_at_row(Builder &builder, Block &block, size_t row_idx)
Evaluate a single Relation at block's row row_idx in isolation, returning true iff every subrelation ...
static bool check_memory_relation_with_logup(auto &values, auto &params, MemoryCheckData &memory_data)
static bool check_databus_read(auto &values, Builder &builder)
Check that the {index, value} pair contained in a databus read gate reflects the actual value present...
static bool check_relation(auto &values, auto &params)
Check that a given relation is satisfied for the provided inputs corresponding to a single row.
static bool check_tag_data(const TagCheckData &tag_data)
Check whether the left and right running tag products are equal.
static bool check_lookup(auto &values, auto &lookup_hash_table)
Check whether the values in a lookup gate are contained within a corresponding hash table.
static void populate_values(Builder &builder, auto &block, auto &values, TagCheckData &tag_data, MemoryCheckData &memory_data, size_t idx)
Populate the values required to check the correctness of a single "row" of the circuit.
static bool check(const Builder &builder_in)
Check the correctness of a circuit witness.
static Builder prepare_circuit(const Builder &builder_in)
Copy the builder and finalize it before checking its validity.
static bool check_block(Builder &builder, auto &block, TagCheckData &tag_data, MemoryCheckData &memory_data, LookupHashTable &lookup_hash_table)
Checks that the provided witness satisfies all gates contained in a single execution trace block.
A field element for each entity of the flavor. These entities represent the prover polynomials evalua...
#define info(...)
Definition log.hpp:93
AluTraceBuilder builder
Definition alu.test.cpp:124
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
FF read_gate_selector(const ExecutionTraceBlock< FF, NUM_WIRES > &block, GateKind kind, size_t idx)
Gate-selector value at (block, idx) for kind, returning zero if the block does not own this kind or t...
@ Poseidon2QuadIntTerminal
@ Poseidon2TransitionEntry
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Struct for managing memory record data for ensuring RAM/ROM correctness.
Struct for managing the running tag product data for ensuring tag correctness.
std::unordered_set< size_t > encountered_variables
static constexpr field one()
constexpr field invert() const noexcept
VectorField result