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: Complete, auditors: [Sherlock], commit: e6694849223 }
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 = 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 BB_ASSERT_LT(ram_array.access_count, UINT32_MAX, "RAM access count overflow");
323 ram_array.access_count++;
324 // mutates the gate_index
325 create_RAM_gate(builder, new_record);
326 ram_array.records.emplace_back(new_record);
327}
328
329template <typename ExecutionTrace>
331 const size_t ram_id,
332 const uint32_t index_witness)
333{
334 BB_ASSERT_GT(ram_arrays.size(), ram_id);
335 RamTranscript& ram_array = ram_arrays[ram_id];
336 const uint32_t index = static_cast<uint32_t>(uint256_t(builder->get_variable(index_witness)));
337 BB_ASSERT_GT(ram_array.state.size(), index);
338 BB_ASSERT(ram_array.state[index] != UNINITIALIZED_MEMORY_RECORD);
339 const auto value = builder->get_variable(ram_array.state[index]);
340 const uint32_t value_witness = builder->add_variable(value);
341
342 RamRecord new_record{ .index_witness = index_witness,
343 .timestamp_witness = builder->put_constant_variable((uint64_t)ram_array.access_count),
344 .value_witness = value_witness,
345 .index = index,
346 .timestamp = ram_array.access_count,
347 .access_type = RamRecord::AccessType::READ,
348 .record_witness = 0,
349 .gate_index = 0 };
350
351 // mutates `gate_index`
352 create_RAM_gate(builder, new_record);
353 ram_array.records.emplace_back(new_record);
354
355 // increment ram array's access count
356 BB_ASSERT_LT(ram_array.access_count, UINT32_MAX, "RAM access count overflow");
357 ram_array.access_count++;
358
359 // return witness index of the value in the array
360 return value_witness;
361}
374template <typename ExecutionTrace>
376 const size_t ram_id,
377 const uint32_t index_witness,
378 const uint32_t value_witness)
379{
380 BB_ASSERT_GT(ram_arrays.size(), ram_id);
381 RamTranscript& ram_array = ram_arrays[ram_id];
382 const uint32_t index = static_cast<uint32_t>(uint256_t(builder->get_variable(index_witness)));
383 BB_ASSERT_GT(ram_array.state.size(), index);
384 BB_ASSERT(ram_array.state[index] != UNINITIALIZED_MEMORY_RECORD);
385
386 RamRecord new_record{ .index_witness = index_witness,
387 .timestamp_witness = builder->put_constant_variable((uint64_t)ram_array.access_count),
388 .value_witness = value_witness,
389 .index = index,
390 .timestamp = ram_array.access_count,
391 .access_type = RamRecord::AccessType::WRITE,
392 .record_witness = 0,
393 .gate_index = 0 };
394 // mutates `gate_index`
395 create_RAM_gate(builder, new_record);
396 ram_array.records.emplace_back(new_record);
397
398 // increment ram array's access count
399 BB_ASSERT_LT(ram_array.access_count, UINT32_MAX, "RAM access count overflow");
400 ram_array.access_count++;
401
402 // update Composer's current state of RAM array
403 ram_array.state[index] = value_witness;
404}
405
406template <typename ExecutionTrace>
408{
409 // Record wire value can't yet be computed (uses randomness generated during proof construction).
410 // However it needs a distinct witness index,
411 // we will be applying copy constraints + set membership constraints.
412 // Later on during proof construction we will compute the record wire value + assign it
413 record.record_witness = builder->add_variable(FF(0));
414 builder->apply_memory_selectors(record.access_type == RamRecord::AccessType::READ
415 ? CircuitBuilder::MEMORY_SELECTORS::RAM_READ
416 : CircuitBuilder::MEMORY_SELECTORS::RAM_WRITE);
417 builder->blocks.memory.populate_wires(
418 record.index_witness, record.timestamp_witness, record.value_witness, record.record_witness);
419
420 // Note: record the index into the block that contains the RAM/ROM gates
421 record.gate_index = builder->blocks.memory.size() - 1;
422 builder->increment_num_gates();
423}
424
425template <typename ExecutionTrace>
427{
428 record.record_witness = builder->add_variable(FF(0));
429 builder->apply_memory_selectors(CircuitBuilder::MEMORY_SELECTORS::RAM_CONSISTENCY_CHECK);
430 builder->blocks.memory.populate_wires(
431 record.index_witness, record.timestamp_witness, record.value_witness, record.record_witness);
432 // Note: record the index into the memory block that contains the RAM/ROM gates
433 record.gate_index = builder->blocks.memory.size() - 1;
434 builder->check_selector_length_consistency();
435 builder->increment_num_gates();
436}
437
438template <typename ExecutionTrace>
440 RamRecord& record,
441 const size_t ram_array_size)
442{
443 record.record_witness = builder->add_variable(FF(0));
444 // Note: record the index into the block that contains the RAM/ROM gates
445 record.gate_index = builder->blocks.memory.size(); // no -1 since we _haven't_ added the gate yet
446
447 // Create a final gate with all selectors zero (hence unconstrained). In particular, the `MEMORY_SELECTORS` are not
448 // on. Wire values are accessed by the previous RAM gate via shifted wires.
449 builder->create_unconstrained_gate(builder->blocks.memory,
450 record.index_witness,
451 record.timestamp_witness,
452 record.value_witness,
453 record.record_witness);
454
455 // Create an add gate ensuring the final index is consistent with the size of the RAM array
456 builder->create_big_add_gate({
457 record.index_witness,
458 builder->zero_idx(),
459 builder->zero_idx(),
460 builder->zero_idx(),
461 1,
462 0,
463 0,
464 0,
465 -FF(static_cast<uint64_t>(ram_array_size) - 1),
466 });
467}
468
469// Gate cost of RAM interactions:
470// Currently, a circuit consisting predominantly of RAM interactions with 1 RAM array costs ~3.25 gates per
471// interaction:
472// 1. The memory gate itself (create_RAM_gate)
473// 2. Fixing the witness for the timestamp (put_constant_variable -> fix_witness, 1 gate per unique timestamp)
474// 3. Sorted memory gate (create_sorted_RAM_gate)
475// 4. 0.25 for the range constraint on the timestamp delta in the sorted memory gate
476//
477// Potential optimization: If we delay the creation of memory gates until circuit finalisation, we can eliminate step 2.
478// We would add the relation `timestamp_omega - timestamp - 1 == 0` into the RAM memory gate and fix the first timestamp
479// to 0 or 1. This would reduce the cost to 2.25 gates per interaction + 1 fix_witness + 1 waste gate after the memory
480// gates.
481
482template <typename ExecutionTrace>
484{
485 RamTranscript& ram_array = ram_arrays[ram_id];
486 const auto access_tag = builder->get_new_tag(); // current_tag + 1;
487 const auto sorted_list_tag = builder->get_new_tag(); // current_tag + 2;
488 // when we process a given RAM array, we apply a "multiset equality check" between the records of the gates and then
489 // the records of the sorted gates. at the time of witness generation, the prover certainly knows the permutation;
490 // however, incarnating this with copy constraints would make the circuit (i.e., the VK) _witness dependent_.
491 builder->set_tau_transposition(access_tag, sorted_list_tag);
492
493 // NOTE: we simply assert that all cells have been initialized. The circuit should initialize all RAM elements to
494 // prevent witness-dependent constraints. For example, if a RAM record is uninitialized but the index of that record
495 // is a function of witness data (e.g. public/private inputs), different public inputs will produce different
496 // circuit constraints, and in particular VKs will not be independent of witness generation.
497 for (size_t i = 0; i < ram_array.state.size(); ++i) {
498 BB_ASSERT_NEQ(ram_array.state[i], UNINITIALIZED_MEMORY_RECORD);
499 }
500
501#ifdef NO_PAR_ALGOS
502 std::sort(ram_array.records.begin(), ram_array.records.end());
503#else
504 std::sort(std::execution::par_unseq, ram_array.records.begin(), ram_array.records.end());
505#endif
506
507 std::vector<RamRecord> sorted_ram_records;
508
509 // Iterate over all but final RAM record. This is because one of the checks for the "interior" RAM gates is that the
510 // next gate is also a RAM gate. We therfore apply a simplified check for the last gate.
511 for (size_t i = 0; i < ram_array.records.size(); ++i) {
512 const RamRecord& record = ram_array.records[i];
513
514 const auto index = record.index;
515 const auto value = builder->get_variable(record.value_witness);
516 const auto index_witness = builder->add_variable(FF((uint64_t)index));
517 const auto timestamp_witess = builder->add_variable(FF(record.timestamp));
518 const auto value_witness = builder->add_variable(value);
519 // (the values in) `sorted_record` will be identical to (the values in) `record`, except with a different
520 // `gate_index` field, which will be fixed by `create_sorted_RAM_Gate` (resp. `created_final_sorted_RAM_Gate`).
521 RamRecord sorted_record{
522 .index_witness = index_witness,
523 .timestamp_witness = timestamp_witess,
524 .value_witness = value_witness,
525 .index = index,
526 .timestamp = record.timestamp,
527 .access_type = record.access_type,
528 .record_witness = 0,
529 .gate_index = 0,
530 };
531
532 // create a list of sorted ram records
533 sorted_ram_records.emplace_back(sorted_record);
534
535 // We don't apply the RAM consistency check gate to the final record,
536 // as this gate expects a RAM record to be present at the next gate
537 if (i < ram_array.records.size() - 1) {
538 create_sorted_RAM_gate(builder, sorted_record);
539 } else {
540 // For the final record in the sorted list, we do not apply the full consistency check gate.
541 // Only need to check the index value = RAM array size - 1.
542 create_final_sorted_RAM_gate(builder, sorted_record, ram_array.state.size());
543 }
544
545 // Assign record/sorted records to tags that we will perform set equivalence checks on
546 builder->assign_tag(record.record_witness, access_tag);
547 builder->assign_tag(sorted_record.record_witness, sorted_list_tag);
548
549 // For ROM/RAM gates, the 'record' wire value (wire column 4) is a linear combination of the first 3 wire
550 // values. However, the record value uses the random challenge 'eta', generated after the first 3 wires are
551 // committed to. i.e. we can't compute the record witness here because we don't know what `eta` is!
552 //
553 // Take the gate indices of the two rom gates (original read gate + sorted gate) and store in `memory_records`.
554 // Once we generate the `eta` challenge, we'll use `memory_records` to figure out which gates need a record wire
555 // value to be computed.
556
557 switch (record.access_type) {
559 builder->memory_read_records.push_back(static_cast<uint32_t>(sorted_record.gate_index));
560 builder->memory_read_records.push_back(static_cast<uint32_t>(record.gate_index));
561 break;
562 }
564 builder->memory_write_records.push_back(static_cast<uint32_t>(sorted_record.gate_index));
565 builder->memory_write_records.push_back(static_cast<uint32_t>(record.gate_index));
566 break;
567 }
568 default: {
569 throw_or_abort("Unexpected record.access_type."); // shouldn't get here!
570 }
571 }
572 }
573
574 // Step 2: Create gates that validate correctness of RAM timestamps
575
576 std::vector<uint32_t> timestamp_deltas;
577 // Guard against empty sorted_ram_records (e.g., RAM array of size 0)
578 if (sorted_ram_records.size() <= 1) {
579 return;
580 }
581 for (size_t i = 0; i < sorted_ram_records.size() - 1; ++i) {
582 const auto& current = sorted_ram_records[i];
583 const auto& next = sorted_ram_records[i + 1];
584
585 const bool share_index = current.index == next.index;
586
587 FF timestamp_delta = 0;
588 if (share_index) {
589 BB_ASSERT_GT(next.timestamp, current.timestamp);
590 timestamp_delta = FF(next.timestamp - current.timestamp);
591 }
592
593 uint32_t timestamp_delta_witness = builder->add_variable(timestamp_delta);
594 // note that the `index_witness` and `timestamp_witness` are taken from `current`. This means that there are
595 // copy constraints, which will mean that once we constrain the sorted gates to be in lexicographic order,
596 // these gates will _automatically_ be in lexicographic order.
597 builder->apply_memory_selectors(CircuitBuilder::MEMORY_SELECTORS::RAM_TIMESTAMP_CHECK);
598 builder->blocks.memory.populate_wires(
599 current.index_witness, current.timestamp_witness, timestamp_delta_witness, builder->zero_idx());
600
601 builder->increment_num_gates();
602
603 // store timestamp offsets for later. Need to apply range checks to them, but calling
604 // `create_new_range_constraint` can add gates, which could ruin the structure of our sorted timestamp list.
605 timestamp_deltas.push_back(timestamp_delta_witness);
606 }
607
608 // add the index/timestamp values of the last sorted record in an empty add gate.
609 // (the previous gate will access the wires on this gate and requires them to be those of the last record)
610 const auto& last = sorted_ram_records[ram_array.records.size() - 1];
611 builder->create_unconstrained_gate(
612 builder->blocks.memory, last.index_witness, last.timestamp_witness, builder->zero_idx(), builder->zero_idx());
613
614 // Step 3: validate that the timestamp_deltas (successive difference of timestamps for the same index) are
615 // monotonically increasing. i.e. are <= maximum timestamp. NOTE: we do _not_ check that every possible
616 // timestamp between 0 and `max_timestamp` occurs at least once (which corresponds to an "honest trace," e.g.,
617 // one generated by the code in this file). However, our check nonetheless suffices for correct memory accesses.
618 const uint32_t max_timestamp = ram_array.access_count - 1;
619 for (auto& w : timestamp_deltas) {
620 builder->create_small_range_constraint(w, max_timestamp);
621 }
622}
623
625{
626 for (size_t i = 0; i < ram_arrays.size(); ++i) {
627 process_RAM_array(builder, i);
628 }
629}
630
631// Template instantiations
634
635} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:113
#define BB_ASSERT_NEQ(actual, expected,...)
Definition assert.hpp:98
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
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)