Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
rom_ram_logic.cpp
Go to the documentation of this file.
1#include "rom_ram_logic.hpp"
4#include <execution>
5
6namespace bb {
7
8template <typename ExecutionTrace> size_t RomRamLogic_<ExecutionTrace>::create_ROM_array(const size_t array_size)
9{
10 RomTranscript new_transcript;
11 for (size_t i = 0; i < array_size; ++i) {
12 new_transcript.state.emplace_back(
13 std::array<uint32_t, 2>{ UNINITIALIZED_MEMORY_RECORD, UNINITIALIZED_MEMORY_RECORD });
14 }
15 rom_arrays.emplace_back(new_transcript);
16 return rom_arrays.size() - 1;
17}
30template <typename ExecutionTrace>
32 const size_t rom_id,
33 const size_t index_value,
34 const uint32_t value_witness)
35{
36 BB_ASSERT_GT(rom_arrays.size(), rom_id);
37 RomTranscript& rom_array = rom_arrays[rom_id];
38 const uint32_t index_witness =
39 (index_value == 0) ? builder->zero_idx() : builder->put_constant_variable((uint64_t)index_value);
40 BB_ASSERT_GT(rom_array.state.size(), index_value);
41 BB_ASSERT_EQ(rom_array.state[index_value][0], UNINITIALIZED_MEMORY_RECORD);
42
43 RomRecord new_record{
44 .index_witness = index_witness,
45 .value_column1_witness = value_witness,
46 .value_column2_witness = builder->zero_idx(),
47 .index = static_cast<uint32_t>(index_value),
48 .record_witness = 0,
49 .gate_index = 0,
50 };
51 rom_array.state[index_value][0] = value_witness;
52 rom_array.state[index_value][1] = builder->zero_idx();
53 create_ROM_gate(builder, new_record);
54 rom_array.records.emplace_back(new_record);
55}
59template <typename ExecutionTrace>
61 const size_t rom_id,
62 const size_t index_value,
63 const std::array<uint32_t, 2>& value_witnesses)
64{
65 BB_ASSERT_GT(rom_arrays.size(), rom_id);
66 RomTranscript& rom_array = rom_arrays[rom_id];
67 const uint32_t index_witness = builder->put_constant_variable((uint64_t)index_value);
68 BB_ASSERT_GT(rom_array.state.size(), index_value);
69 BB_ASSERT_EQ(rom_array.state[index_value][0], UNINITIALIZED_MEMORY_RECORD);
70 RomRecord new_record{
71 .index_witness = index_witness,
72 .value_column1_witness = value_witnesses[0],
73 .value_column2_witness = value_witnesses[1],
74 .index = static_cast<uint32_t>(index_value),
75 .record_witness = 0,
76 .gate_index = 0,
77 };
78 rom_array.state[index_value][0] = value_witnesses[0];
79 rom_array.state[index_value][1] = value_witnesses[1];
80 // `create_ROM_gate` fills in the `gate_index` of the `RamRecord`.
81 create_ROM_gate(builder, new_record);
82 rom_array.records.emplace_back(new_record);
83}
84
85template <typename ExecutionTrace>
87 const size_t rom_id,
88 const uint32_t index_witness)
89{
90 BB_ASSERT_GT(rom_arrays.size(), rom_id);
91 RomTranscript& rom_array = rom_arrays[rom_id];
92 const uint32_t index = static_cast<uint32_t>(uint256_t(builder->get_variable(index_witness)));
93 BB_ASSERT_GT(rom_array.state.size(), index);
94 BB_ASSERT(rom_array.state[index][0] != UNINITIALIZED_MEMORY_RECORD);
95 const auto value = builder->get_variable(rom_array.state[index][0]);
96 const uint32_t value_witness = builder->add_variable(value);
97 RomRecord new_record{
98 .index_witness = index_witness,
99 .value_column1_witness = value_witness,
100 .value_column2_witness = builder->zero_idx(),
101 .index = index,
102 .record_witness = 0,
103 .gate_index = 0,
104 };
105 create_ROM_gate(builder, new_record);
106 rom_array.records.emplace_back(new_record);
107
108 return value_witness;
109}
110
111template <typename ExecutionTrace>
113 const size_t rom_id,
114 const uint32_t index_witness)
115{
116 std::array<uint32_t, 2> value_witnesses;
117
118 const uint32_t index = static_cast<uint32_t>(uint256_t(builder->get_variable(index_witness)));
119 BB_ASSERT_GT(rom_arrays.size(), rom_id);
120 RomTranscript& rom_array = rom_arrays[rom_id];
121 BB_ASSERT_GT(rom_array.state.size(), index);
122 BB_ASSERT(rom_array.state[index][0] != UNINITIALIZED_MEMORY_RECORD);
123 BB_ASSERT(rom_array.state[index][1] != UNINITIALIZED_MEMORY_RECORD);
124 const auto value1 = builder->get_variable(rom_array.state[index][0]);
125 const auto value2 = builder->get_variable(rom_array.state[index][1]);
126 value_witnesses[0] = builder->add_variable(value1);
127 value_witnesses[1] = builder->add_variable(value2);
128 RomRecord new_record{
129 .index_witness = index_witness,
130 .value_column1_witness = value_witnesses[0],
131 .value_column2_witness = value_witnesses[1],
132 .index = index,
133 .record_witness = 0,
134 .gate_index = 0,
135 };
136 create_ROM_gate(builder, new_record);
137 rom_array.records.emplace_back(new_record);
138
139 return value_witnesses;
140}
141
142// There is one important difference between `create_ROM_gate` and `create_sorted_ROM_gate`: we apply a different memory
143// selectors. We also only call `update_used_witnesses` for `record_witness` in the latter, but this is just for
144// Boomerang value detection.
145
146template <typename ExecutionTrace>
148{
149 // Record wire value can't yet be computed; it will be filled in later.
150 record.record_witness = builder->add_variable(FF(0));
151 builder->apply_memory_selectors(CircuitBuilder::MEMORY_SELECTORS::ROM_READ);
152 builder->blocks.memory.populate_wires(
154 // Note: record the index into the memory block that contains the RAM/ROM gates
155 record.gate_index = builder->blocks.memory.size() - 1;
156 builder->check_selector_length_consistency();
157 builder->increment_num_gates();
158}
159
160template <typename ExecutionTrace>
162{
163 record.record_witness = builder->add_variable(FF(0));
164 // record_witness is intentionally used only in a single gate
165 builder->update_used_witnesses(record.record_witness);
166 builder->apply_memory_selectors(CircuitBuilder::MEMORY_SELECTORS::ROM_CONSISTENCY_CHECK);
167 builder->blocks.memory.populate_wires(
169 // Note: record the index into the memory block that contains the RAM/ROM gates
170 record.gate_index = builder->blocks.memory.size() - 1;
171 builder->check_selector_length_consistency();
172 builder->increment_num_gates();
173}
174
175template <typename ExecutionTrace>
177{
178 auto& rom_array = rom_arrays[rom_id];
179 // when we process a given ROM array, we apply a "multiset equality check" between the records of the gates and then
180 // the records of the sorted gates. at the time of witness generation, the prover certainly knows the permutation;
181 // however, incarnating this with copy constraints would make the circuit (i.e., the VK) _witness dependent_.
182 const auto read_tag = builder->get_new_tag(); // current_tag + 1;
183 const auto sorted_list_tag = builder->get_new_tag(); // current_tag + 2;
184 builder->set_tau_transposition(read_tag, sorted_list_tag);
185
186 // Make sure that every cell has been initialized
187 for (size_t i = 0; i < rom_array.state.size(); ++i) {
188 if (rom_array.state[i][0] == UNINITIALIZED_MEMORY_RECORD) {
189 set_ROM_element_pair(
190 builder, rom_id, static_cast<uint32_t>(i), { builder->zero_idx(), builder->zero_idx() });
191 }
192 }
193
194#ifdef NO_PAR_ALGOS
195 std::sort(rom_array.records.begin(), rom_array.records.end());
196#else
197 std::sort(std::execution::par_unseq, rom_array.records.begin(), rom_array.records.end());
198#endif
199
200 for (const RomRecord& record : rom_array.records) {
201 const auto index = record.index;
202 const auto value1 = builder->get_variable(record.value_column1_witness);
203 const auto value2 = builder->get_variable(record.value_column2_witness);
204 const auto index_witness = builder->add_variable(FF((uint64_t)index));
205 builder->update_used_witnesses(index_witness);
206 const auto value1_witness = builder->add_variable(value1);
207 const auto value2_witness = builder->add_variable(value2);
208 // (the real values in) `sorted_record` will be identical to (those in) `record`, except with a different
209 // `gate_index` field, which will be filled out by `create_sorted_ROM_Gate`.
210 RomRecord sorted_record{
211 .index_witness = index_witness,
212 .value_column1_witness = value1_witness,
213 .value_column2_witness = value2_witness,
214 .index = index,
215 .record_witness = 0,
216 .gate_index = 0,
217 };
218 // the position of the sorted ROM gate in the execution trace depends on the witness data.
219 create_sorted_ROM_gate(builder, sorted_record);
220
221 builder->assign_tag(record.record_witness, read_tag);
222 builder->assign_tag(sorted_record.record_witness, sorted_list_tag);
223
224 // For ROM/RAM gates, the 'record' wire value (wire column 4) is a linear combination of the first 3 wire
225 // values. However, the record value uses the random challenge 'eta', generated after the first 3 wires are
226 // committed to. i.e., we can't compute the record witness here because we don't know what `eta` is! Take the
227 // gate indices of the two rom gates (original read gate + sorted gate) and store in `memory_records`. Once we
228 // generate the `eta` challenge, we'll use `memory_records` to figure out which gates need a record wire value
229 // to be computed.
230 //
231 // `record` (w4) = w3 * eta^3 + w2 * eta^2 + w1 * eta + read_write_flag (0 for reads, 1 for writes)
232 // Separate containers used to store gate indices of reads and writes. Need to differentiate because of
233 // `read_write_flag` (N.B. all ROM accesses are considered reads. Writes are for RAM operations)
234 builder->memory_read_records.push_back(static_cast<uint32_t>(sorted_record.gate_index));
235 builder->memory_read_records.push_back(static_cast<uint32_t>(record.gate_index));
236 }
237 // One of the checks we run on the sorted list is to validate the difference between the index field across two
238 // adjacent gates is either 0 or 1. To make this work with the last gate, we add a dummy gate at the end of the
239 // sorted list, where we set the first wire to equal `m + 1`, where `m` is the maximum allowed index in the sorted
240 // list. Moreover, as `m + 1` is a circuit constant, this ensures that the checks correctly constrain the sorted ROM
241 // gate chunks.
242 FF max_index_value((uint64_t)rom_array.state.size());
243 uint32_t max_index = builder->add_variable(max_index_value);
244
245 builder->create_unconstrained_gate(
246 builder->blocks.memory, max_index, builder->zero_idx(), builder->zero_idx(), builder->zero_idx());
247 builder->create_big_add_gate(
248 {
249 max_index,
250 builder->zero_idx(),
251 builder->zero_idx(),
252 builder->zero_idx(),
253 1,
254 0,
255 0,
256 0,
257 -max_index_value,
258 },
259 false);
260 // N.B. If the above check holds, we know the sorted list begins with an index value of 0,
261 // because the first cell is explicitly initialized using zero_idx as the index field.
262}
263
265{
266 for (size_t i = 0; i < rom_arrays.size(); ++i) {
267 process_ROM_array(builder, i);
268 }
269}
270
271template <typename ExecutionTrace> size_t RomRamLogic_<ExecutionTrace>::create_RAM_array(const size_t array_size)
272{
273 RamTranscript new_transcript;
274 for (size_t i = 0; i < array_size; ++i) {
275 new_transcript.state.emplace_back(UNINITIALIZED_MEMORY_RECORD);
276 }
277 ram_arrays.emplace_back(new_transcript);
278 return ram_arrays.size() - 1;
279}
295template <typename ExecutionTrace>
297 const size_t ram_id,
298 const size_t index_value,
299 const uint32_t value_witness)
300{
301 BB_ASSERT_GT(ram_arrays.size(), ram_id);
302 RamTranscript& ram_array = ram_arrays[ram_id];
303 const uint32_t index_witness =
304 (index_value == 0) ? builder->zero_idx() : builder->put_constant_variable((uint64_t)index_value);
305 BB_ASSERT_GT(ram_array.state.size(), index_value);
306 BB_ASSERT_EQ(ram_array.state[index_value], UNINITIALIZED_MEMORY_RECORD);
307 RamRecord new_record{ .index_witness = index_witness,
308 .timestamp_witness = builder->put_constant_variable((uint64_t)ram_array.access_count),
309 .value_witness = value_witness,
310 .index = static_cast<uint32_t>(index_value),
311 .timestamp = static_cast<uint32_t>(ram_array.access_count),
312 .access_type = RamRecord::AccessType::WRITE,
313 .record_witness = 0,
314 .gate_index = 0 };
315 ram_array.state[index_value] = value_witness;
316 ram_array.access_count++;
317 // mutates the gate_index
318 create_RAM_gate(builder, new_record);
319 ram_array.records.emplace_back(new_record);
320}
321
322template <typename ExecutionTrace>
324 const size_t ram_id,
325 const uint32_t index_witness)
326{
327 BB_ASSERT_GT(ram_arrays.size(), ram_id);
328 RamTranscript& ram_array = ram_arrays[ram_id];
329 const uint32_t index = static_cast<uint32_t>(uint256_t(builder->get_variable(index_witness)));
330 BB_ASSERT_GT(ram_array.state.size(), index);
331 BB_ASSERT(ram_array.state[index] != UNINITIALIZED_MEMORY_RECORD);
332 const auto value = builder->get_variable(ram_array.state[index]);
333 const uint32_t value_witness = builder->add_variable(value);
334
335 RamRecord new_record{ .index_witness = index_witness,
336 .timestamp_witness = builder->put_constant_variable((uint64_t)ram_array.access_count),
337 .value_witness = value_witness,
338 .index = index,
339 .timestamp = static_cast<uint32_t>(ram_array.access_count),
340 .access_type = RamRecord::AccessType::READ,
341 .record_witness = 0,
342 .gate_index = 0 };
343
344 // mutates `gate_index`
345 create_RAM_gate(builder, new_record);
346 ram_array.records.emplace_back(new_record);
347
348 // increment ram array's access count
349 ram_array.access_count++;
350
351 // return witness index of the value in the array
352 return value_witness;
353}
366template <typename ExecutionTrace>
368 const size_t ram_id,
369 const uint32_t index_witness,
370 const uint32_t value_witness)
371{
372 BB_ASSERT_GT(ram_arrays.size(), ram_id);
373 RamTranscript& ram_array = ram_arrays[ram_id];
374 const uint32_t index = static_cast<uint32_t>(uint256_t(builder->get_variable(index_witness)));
375 BB_ASSERT_GT(ram_array.state.size(), index);
376 BB_ASSERT(ram_array.state[index] != UNINITIALIZED_MEMORY_RECORD);
377
378 RamRecord new_record{ .index_witness = index_witness,
379 .timestamp_witness = builder->put_constant_variable((uint64_t)ram_array.access_count),
380 .value_witness = value_witness,
381 .index = index,
382 .timestamp = static_cast<uint32_t>(ram_array.access_count),
383 .access_type = RamRecord::AccessType::WRITE,
384 .record_witness = 0,
385 .gate_index = 0 };
386 // mutates `gate_index`
387 create_RAM_gate(builder, new_record);
388 ram_array.records.emplace_back(new_record);
389
390 // increment ram array's access count
391 ram_array.access_count++;
392
393 // update Composer's current state of RAM array
394 ram_array.state[index] = value_witness;
395}
396
397template <typename ExecutionTrace>
399{
400 // Record wire value can't yet be computed (uses randomness generated during proof construction).
401 // However it needs a distinct witness index,
402 // we will be applying copy constraints + set membership constraints.
403 // Later on during proof construction we will compute the record wire value + assign it
404 record.record_witness = builder->add_variable(FF(0));
405 builder->apply_memory_selectors(record.access_type == RamRecord::AccessType::READ
406 ? CircuitBuilder::MEMORY_SELECTORS::RAM_READ
407 : CircuitBuilder::MEMORY_SELECTORS::RAM_WRITE);
408 builder->blocks.memory.populate_wires(
409 record.index_witness, record.timestamp_witness, record.value_witness, record.record_witness);
410
411 // Note: record the index into the block that contains the RAM/ROM gates
412 record.gate_index = builder->blocks.memory.size() - 1;
413 builder->increment_num_gates();
414}
415
416template <typename ExecutionTrace>
418{
419 record.record_witness = builder->add_variable(FF(0));
420 builder->apply_memory_selectors(CircuitBuilder::MEMORY_SELECTORS::RAM_CONSISTENCY_CHECK);
421 builder->blocks.memory.populate_wires(
422 record.index_witness, record.timestamp_witness, record.value_witness, record.record_witness);
423 // Note: record the index into the memory block that contains the RAM/ROM gates
424 record.gate_index = builder->blocks.memory.size() - 1;
425 builder->check_selector_length_consistency();
426 builder->increment_num_gates();
427}
428
429template <typename ExecutionTrace>
431 RamRecord& record,
432 const size_t ram_array_size)
433{
434 record.record_witness = builder->add_variable(FF(0));
435 // Note: record the index into the block that contains the RAM/ROM gates
436 record.gate_index = builder->blocks.memory.size(); // no -1 since we _haven't_ added the gate yet
437
438 // Create a final gate with all selectors zero (hence unconstrained). In particular, the `MEMORY_SELECTORS` are not
439 // on. Wire values are accessed by the previous RAM gate via shifted wires.
440 builder->create_unconstrained_gate(builder->blocks.memory,
441 record.index_witness,
442 record.timestamp_witness,
443 record.value_witness,
444 record.record_witness);
445
446 // Create an add gate ensuring the final index is consistent with the size of the RAM array
447 builder->create_big_add_gate({
448 record.index_witness,
449 builder->zero_idx(),
450 builder->zero_idx(),
451 builder->zero_idx(),
452 1,
453 0,
454 0,
455 0,
456 -FF(static_cast<uint64_t>(ram_array_size) - 1),
457 });
458}
459
460template <typename ExecutionTrace>
462{
463 RamTranscript& ram_array = ram_arrays[ram_id];
464 const auto access_tag = builder->get_new_tag(); // current_tag + 1;
465 const auto sorted_list_tag = builder->get_new_tag(); // current_tag + 2;
466 // when we process a given RAM array, we apply a "multiset equality check" between the records of the gates and then
467 // the records of the sorted gates. at the time of witness generation, the prover certainly knows the permutation;
468 // however, incarnating this with copy constraints would make the circuit (i.e., the VK) _witness dependent_.
469 builder->set_tau_transposition(access_tag, sorted_list_tag);
470
471 // NOTE: we simply assert that all cells have been initialized. The circuit should initialize all RAM elements to
472 // prevent witness-dependent constraints. For example, if a RAM record is uninitialized but the index of that record
473 // is a function of witness data (e.g. public/private inputs), different public inputs will produce different
474 // circuit constraints, and in particular VKs will not be independent of witness generation.
475 for (size_t i = 0; i < ram_array.state.size(); ++i) {
476 BB_ASSERT_NEQ(ram_array.state[i], UNINITIALIZED_MEMORY_RECORD);
477 }
478
479#ifdef NO_PAR_ALGOS
480 std::sort(ram_array.records.begin(), ram_array.records.end());
481#else
482 std::sort(std::execution::par_unseq, ram_array.records.begin(), ram_array.records.end());
483#endif
484
485 std::vector<RamRecord> sorted_ram_records;
486
487 // Iterate over all but final RAM record. This is because one of the checks for the "interior" RAM gates is that the
488 // next gate is also a RAM gate. We therfore apply a simplified check for the last gate.
489 for (size_t i = 0; i < ram_array.records.size(); ++i) {
490 const RamRecord& record = ram_array.records[i];
491
492 const auto index = record.index;
493 const auto value = builder->get_variable(record.value_witness);
494 const auto index_witness = builder->add_variable(FF((uint64_t)index));
495 const auto timestamp_witess = builder->add_variable(FF(record.timestamp));
496 const auto value_witness = builder->add_variable(value);
497 // (the values in) `sorted_record` will be identical to (the values in) `record`, except with a different
498 // `gate_index` field, which will be fixed by `create_sorted_RAM_Gate` (resp. `created_final_sorted_RAM_Gate`).
499 RamRecord sorted_record{
500 .index_witness = index_witness,
501 .timestamp_witness = timestamp_witess,
502 .value_witness = value_witness,
503 .index = index,
504 .timestamp = record.timestamp,
505 .access_type = record.access_type,
506 .record_witness = 0,
507 .gate_index = 0,
508 };
509
510 // create a list of sorted ram records
511 sorted_ram_records.emplace_back(sorted_record);
512
513 // We don't apply the RAM consistency check gate to the final record,
514 // as this gate expects a RAM record to be present at the next gate
515 if (i < ram_array.records.size() - 1) {
516 create_sorted_RAM_gate(builder, sorted_record);
517 } else {
518 // For the final record in the sorted list, we do not apply the full consistency check gate.
519 // Only need to check the index value = RAM array size - 1.
520 create_final_sorted_RAM_gate(builder, sorted_record, ram_array.state.size());
521 }
522
523 // Assign record/sorted records to tags that we will perform set equivalence checks on
524 builder->assign_tag(record.record_witness, access_tag);
525 builder->assign_tag(sorted_record.record_witness, sorted_list_tag);
526
527 // For ROM/RAM gates, the 'record' wire value (wire column 4) is a linear combination of the first 3 wire
528 // values. However, the record value uses the random challenge 'eta', generated after the first 3 wires are
529 // committed to. i.e. we can't compute the record witness here because we don't know what `eta` is!
530 //
531 // Take the gate indices of the two rom gates (original read gate + sorted gate) and store in `memory_records`.
532 // Once we generate the `eta` challenge, we'll use `memory_records` to figure out which gates need a record wire
533 // value to be computed.
534
535 switch (record.access_type) {
537 builder->memory_read_records.push_back(static_cast<uint32_t>(sorted_record.gate_index));
538 builder->memory_read_records.push_back(static_cast<uint32_t>(record.gate_index));
539 break;
540 }
542 builder->memory_write_records.push_back(static_cast<uint32_t>(sorted_record.gate_index));
543 builder->memory_write_records.push_back(static_cast<uint32_t>(record.gate_index));
544 break;
545 }
546 default: {
547 throw_or_abort("Unexpected record.access_type."); // shouldn't get here!
548 }
549 }
550 }
551
552 // Step 2: Create gates that validate correctness of RAM timestamps
553
554 std::vector<uint32_t> timestamp_deltas;
555 // Guard against empty sorted_ram_records (e.g., RAM array of size 0)
556 if (sorted_ram_records.size() <= 1) {
557 return;
558 }
559 for (size_t i = 0; i < sorted_ram_records.size() - 1; ++i) {
560 const auto& current = sorted_ram_records[i];
561 const auto& next = sorted_ram_records[i + 1];
562
563 const bool share_index = current.index == next.index;
564
565 FF timestamp_delta = 0;
566 if (share_index) {
567 BB_ASSERT_GT(next.timestamp, current.timestamp);
568 timestamp_delta = FF(next.timestamp - current.timestamp);
569 }
570
571 uint32_t timestamp_delta_witness = builder->add_variable(timestamp_delta);
572 // note that the `index_witness` and `timestamp_witness` are taken from `current`. This means that there are
573 // copy constraints, which will mean that once we constrain the sorted gates to be in lexicographic order,
574 // these gates will _automatically_ be in lexicographic order.
575 builder->apply_memory_selectors(CircuitBuilder::MEMORY_SELECTORS::RAM_TIMESTAMP_CHECK);
576 builder->blocks.memory.populate_wires(
577 current.index_witness, current.timestamp_witness, timestamp_delta_witness, builder->zero_idx());
578
579 builder->increment_num_gates();
580
581 // store timestamp offsets for later. Need to apply range checks to them, but calling
582 // `create_new_range_constraint` can add gates, which could ruin the structure of our sorted timestamp list.
583 timestamp_deltas.push_back(timestamp_delta_witness);
584 }
585
586 // add the index/timestamp values of the last sorted record in an empty add gate.
587 // (the previous gate will access the wires on this gate and requires them to be those of the last record)
588 const auto& last = sorted_ram_records[ram_array.records.size() - 1];
589 builder->create_unconstrained_gate(
590 builder->blocks.memory, last.index_witness, last.timestamp_witness, builder->zero_idx(), builder->zero_idx());
591
592 // Step 3: validate that the timestamp_deltas (successive difference of timestamps for the same index) are
593 // monotonically increasing. i.e. are <= maximum timestamp. NOTE: we do _not_ check that every possible
594 // timestamp between 0 and `max_timestamp` occurs at least once (which corresponds to an "honest trace," e.g.,
595 // one generated by the code in this file). However, our check nonetheless suffices for correct memory accesses.
596 const size_t max_timestamp = ram_array.access_count - 1;
597 for (auto& w : timestamp_deltas) {
598 builder->create_new_range_constraint(w, max_timestamp);
599 }
600}
601
603{
604 for (size_t i = 0; i < ram_arrays.size(); ++i) {
605 process_RAM_array(builder, i);
606 }
607}
608
609// Template instantiations
612
613} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:107
#define BB_ASSERT_NEQ(actual, expected,...)
Definition assert.hpp:92
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:77
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:22
ROM/RAM logic handler for UltraCircuitBuilder.
size_t create_ROM_array(const size_t array_size)
Create a new read-only memory region.
uint32_t read_ROM_array(CircuitBuilder *builder, const size_t rom_id, const uint32_t index_witness)
Read a single element from ROM.
void process_ROM_array(CircuitBuilder *builder, const size_t rom_id)
Compute additional gates required to validate ROM reads. Called when generating the proving key.
void create_sorted_RAM_gate(CircuitBuilder *builder, RamRecord &record)
Gate that performs consistency checks to validate that a claimed RAM read/write value is correct.
void process_ROM_arrays(CircuitBuilder *builder)
Process all of the ROM arrays.
std::array< uint32_t, 2 > read_ROM_array_pair(CircuitBuilder *builder, const size_t rom_id, const uint32_t index_witness)
Read a pair of elements from ROM.
void set_ROM_element(CircuitBuilder *builder, const size_t rom_id, const size_t index_value, const uint32_t value_witness)
Initialize a rom cell to equal value_witness
void create_final_sorted_RAM_gate(CircuitBuilder *builder, RamRecord &record, const size_t ram_array_size)
Performs consistency checks to validate that a claimed RAM read/write value is correct....
void process_RAM_arrays(CircuitBuilder *builder)
void init_RAM_element(CircuitBuilder *builder, const size_t ram_id, const size_t index_value, const uint32_t value_witness)
Initialize a RAM cell to equal value_witness
void create_sorted_ROM_gate(CircuitBuilder *builder, RomRecord &record)
Gate that performs consistency checks to validate that a claimed ROM read value is correct.
void write_RAM_array(CircuitBuilder *builder, const size_t ram_id, const uint32_t index_witness, const uint32_t value_witness)
Write a cell in a RAM array.
void set_ROM_element_pair(CircuitBuilder *builder, const size_t rom_id, const size_t index_value, const std::array< uint32_t, 2 > &value_witnesses)
Initialize a ROM array element with a pair of witness values.
void create_ROM_gate(CircuitBuilder *builder, RomRecord &record)
Gate that'reads' from a ROM table, i.e., the table index is a witness not precomputed.
uint32_t read_RAM_array(CircuitBuilder *builder, const size_t ram_id, const uint32_t index_witness)
typename ExecutionTrace::FF FF
void create_RAM_gate(CircuitBuilder *builder, RamRecord &record)
Gate that performs a read/write operation into a RAM table, i.e. table index is a witness not precomp...
void process_RAM_array(CircuitBuilder *builder, const size_t ram_id)
Compute additional gates required to validate RAM read/writes. Called when generating the proving key...
size_t create_RAM_array(const size_t array_size)
Create a new updatable memory region.
AluTraceBuilder builder
Definition alu.test.cpp:124
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
A RAM memory record that can be ordered, first by index, then by timestamp.
uint32_t index_witness
uint32_t value_witness
AccessType access_type
uint32_t record_witness
uint32_t timestamp_witness
RamTranscript contains the RamRecords for a particular RAM table (recording READ and WRITE operations...
std::vector< RamRecord > records
std::vector< uint32_t > state
A ROM memory record that can be ordered, where the ordering is given by the index (a....
uint32_t value_column1_witness
uint32_t index_witness
uint32_t record_witness
uint32_t value_column2_witness
RomTranscript contains the RomRecords for a particular ROM table as well as the vector whose ith entr...
std::vector< std::array< uint32_t, 2 > > state
std::vector< RomRecord > records
void throw_or_abort(std::string const &err)