Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ram_table.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#include "ram_table.hpp"
8
9#include "../circuit_builders/circuit_builders.hpp"
13#include <vector>
14
15namespace bb::stdlib {
16
23template <typename Builder> ram_table<Builder>::ram_table(Builder* builder, const size_t table_size)
25 static_assert(HasPlookup<Builder>);
26 _context = builder;
27 _length = table_size;
28 _index_initialized.resize(table_size);
29 for (size_t i = 0; i < _index_initialized.size(); ++i) {
30 _index_initialized[i] = false;
31 }
33 // do not initialize the table yet. The input entries might all be constant,
34 // if this is the case we might not have a valid pointer to a Builder
35 // We get around this, by initializing the table when `read` or `write` operator is called
36 // with a non-const field element.
37}
38
45template <typename Builder> ram_table<Builder>::ram_table(const std::vector<field_pt>& table_entries)
46{
47 static_assert(HasPlookup<Builder>);
48 // get the builder _context
49 for (const auto& entry : table_entries) {
50 if (entry.get_context() != nullptr) {
51 _context = entry.get_context();
52 break;
53 }
54 }
55 _raw_entries = table_entries;
56 _length = _raw_entries.size();
57 _index_initialized.resize(_length);
58 for (size_t i = 0; i < _index_initialized.size(); ++i) {
59 _index_initialized[i] = false;
60 }
61 // do not initialize the table yet. The input entries might all be constant,
62 // if this is the case we might not have a valid pointer to a Builder
63 // We get around this, by initializing the table when `read` or `write` operator is called
64 // with a non-const field element.
65
66 // Store tags
67 _tags.resize(_length);
68 for (size_t i = 0; i < _length; i++) {
69 _tags[i] = table_entries[i].get_origin_tag();
70 }
71}
72
82template <typename Builder> void ram_table<Builder>::initialize_table() const
83{
84 if (_ram_table_generated_in_builder) {
85 return;
86 }
87 ASSERT(_context != nullptr);
88 _ram_id = _context->create_RAM_array(_length);
89
90 if (_raw_entries.size() > 0) {
91 for (size_t i = 0; i < _length; ++i) {
92 if (!_index_initialized[i]) {
93 field_pt entry;
94 if (_raw_entries[i].is_constant()) {
95 entry = field_pt::from_witness_index(_context,
96 _context->put_constant_variable(_raw_entries[i].get_value()));
97 } else {
98 entry = _raw_entries[i].normalize();
99 }
100 _context->init_RAM_element(_ram_id, i, entry.get_witness_index());
101 _index_initialized[i] = true;
102 }
103 }
104 }
105
106 // Store the tags of the original entries
107 _tags.resize(_length);
108 if (_raw_entries.size() > 0) {
109 for (size_t i = 0; i < _length; i++) {
110 _tags[i] = _raw_entries[i].get_origin_tag();
111 }
112 }
113 _ram_table_generated_in_builder = true;
114}
115
122template <typename Builder>
124 : _raw_entries(other._raw_entries)
125 , _tags(other._tags)
126 , _index_initialized(other._index_initialized)
127 , _length(other._length)
128 , _ram_id(other._ram_id)
129 , _ram_table_generated_in_builder(other._ram_table_generated_in_builder)
130 , _all_entries_written_to_with_constant_index(other._all_entries_written_to_with_constant_index)
131 , _context(other._context)
132{}
133
140template <typename Builder>
142 : _raw_entries(other._raw_entries)
143 , _tags(other._tags)
144 , _index_initialized(other._index_initialized)
145 , _length(other._length)
146 , _ram_id(other._ram_id)
147 , _ram_table_generated_in_builder(other._ram_table_generated_in_builder)
148 , _all_entries_written_to_with_constant_index(other._all_entries_written_to_with_constant_index)
149 , _context(other._context)
150{}
151
159template <typename Builder> ram_table<Builder>& ram_table<Builder>::operator=(const ram_table& other)
160{
161 _raw_entries = other._raw_entries;
162 _tags = other._tags;
163 _length = other._length;
164 _ram_id = other._ram_id;
165 _index_initialized = other._index_initialized;
166 _ram_table_generated_in_builder = other._ram_table_generated_in_builder;
167 _all_entries_written_to_with_constant_index = other._all_entries_written_to_with_constant_index;
168
169 _context = other._context;
170 return *this;
171}
172
180template <typename Builder> ram_table<Builder>& ram_table<Builder>::operator=(ram_table&& other)
181{
182 _raw_entries = other._raw_entries;
183 _length = other._length;
184 _ram_id = other._ram_id;
185 _index_initialized = other._index_initialized;
186 _ram_table_generated_in_builder = other._ram_table_generated_in_builder;
187 _all_entries_written_to_with_constant_index = other._all_entries_written_to_with_constant_index;
188 _context = other._context;
189 _tags = other._tags;
190 return *this;
191}
192
200template <typename Builder> field_t<Builder> ram_table<Builder>::read(const field_pt& index) const
201{
202 if (_context == nullptr) {
203 _context = index.get_context();
204 }
205 const auto native_index = uint256_t(index.get_value());
206 if (native_index >= _length) {
207 // TODO: what's best practise here? We are assuming that this action will generate failing constraints,
208 // and we set failure message here so that it better describes the point of failure.
209 // However, we are not *ensuring* that failing constraints are generated at the point that `failure()` is
210 // called. Is this ok?
211 _context->failure("ram_table: RAM array access out of bounds");
212 }
213
214 initialize_table();
215
216 if (!check_indices_initialized()) {
217 _context->failure("ram_table: must write to every RAM entry at least once (with constant index value) before "
218 "table can be read");
219 }
220
221 field_pt index_wire = index;
222 if (index.is_constant()) {
223 index_wire = field_pt::from_witness_index(_context, _context->put_constant_variable(index.get_value()));
224 }
225
226 uint32_t output_idx = _context->read_RAM_array(_ram_id, index_wire.get_normalized_witness_index());
227 auto element = field_pt::from_witness_index(_context, output_idx);
228
229 const size_t cast_index = static_cast<size_t>(static_cast<uint64_t>(native_index));
230 // If the index is legitimate, restore the tag
231 if (native_index < _length) {
232 element.set_origin_tag(_tags[cast_index]);
233 }
234 return element;
235}
236
244template <typename Builder> void ram_table<Builder>::write(const field_pt& index, const field_pt& value)
245{
246 if (_context == nullptr) {
247 _context = index.get_context();
248 }
249
250 if (uint256_t(index.get_value()) >= _length) {
251 // TODO: what's best practise here? We are assuming that this action will generate failing constraints,
252 // and we set failure message here so that it better describes the point of failure.
253 // However, we are not *ensuring* that failing constraints are generated at the point that `failure()` is
254 // called. Is this ok?
255 _context->failure("ram_table: RAM array access out of bounds");
256 }
257
258 initialize_table();
259 field_pt index_wire = index;
260 const auto native_index = uint256_t(index.get_value());
261 if (index.is_constant()) {
262 // need to write every array element at a constant index before doing reads/writes at prover-defined indices
263 index_wire = field_pt::from_witness_index(_context, _context->put_constant_variable(native_index));
264 } else {
265 if (!check_indices_initialized()) {
266 _context->failure("ram_table: must write to every RAM entry at least once (with constant index value) "
267 "before table can be written to at an unknown index");
268 }
269 }
270
271 field_pt value_wire = value;
272 auto native_value = value.get_value();
273 if (value.is_constant()) {
274 value_wire = field_pt::from_witness_index(_context, _context->put_constant_variable(native_value));
275 }
276
277 const size_t cast_index = static_cast<size_t>(static_cast<uint64_t>(native_index));
278 if (index.is_constant() && _index_initialized[cast_index] == false) {
279 _context->init_RAM_element(_ram_id, cast_index, value_wire.get_witness_index());
280
281 _index_initialized[cast_index] = true;
282 } else {
283 _context->write_RAM_array(
284 _ram_id, index_wire.get_normalized_witness_index(), value_wire.get_normalized_witness_index());
285 }
286 // Update the value of the stored tag, if index is legitimate
287
288 if (native_index < _length) {
289 _tags[cast_index] = value.get_origin_tag();
290 }
291}
292
295} // namespace bb::stdlib
#define ASSERT(expression,...)
Definition assert.hpp:49
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:59
Builder * get_context() const
Definition field.hpp:389
OriginTag get_origin_tag() const
Definition field.hpp:333
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:827
field_t normalize() const
Return a new element, where the in-circuit witness contains the actual represented value (multiplicat...
Definition field.cpp:635
bool is_constant() const
Definition field.hpp:399
uint32_t get_normalized_witness_index() const
Get the index of a normalized version of this element.
Definition field.hpp:471
uint32_t get_witness_index() const
Get the witness index of the current field element.
Definition field.hpp:461
ram_table & operator=(const ram_table &other)
Copy assignment operator.
std::vector< bool > _index_initialized
Definition ram_table.hpp:60
bool _ram_table_generated_in_builder
Definition ram_table.hpp:63
field_pt read(const field_pt &index) const
Read a field element from the RAM table at an index value.
std::vector< field_pt > _raw_entries
Definition ram_table.hpp:57
bool _all_entries_written_to_with_constant_index
Definition ram_table.hpp:64
std::vector< OriginTag > _tags
Definition ram_table.hpp:59
void write(const field_pt &index, const field_pt &value)
Write a field element from the RAM table at an index value.
void initialize_table() const
internal method, is used to call Builder methods that will generate RAM table.
Definition ram_table.cpp:82
Contains all the headers required to adequately compile the types defined in circuit_builders_fwd....
AluTraceBuilder builder
Definition alu.test.cpp:123
std::conditional_t< IsGoblinBigGroup< C, Fq, Fr, G >, element_goblin::goblin_element< C, goblin_field< C >, Fr, G >, element_default::element< C, Fq, Fr, G > > element
element wraps either element_default::element or element_goblin::goblin_element depending on parametr...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...