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