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 <IsUltraOrMegaBuilder Builder>
25 : raw_entries(table_entries)
26 , length(table_entries.size())
28{
29 index_initialized.resize(length, false);
30
31 // For consistency with the other constructor, we delegate the initialization of the table to the first read
32 // operation.
33
34 // Store tags
35 _tags.resize(length);
36 for (size_t i = 0; i < length; i++) {
37 _tags[i] = table_entries[i].get_origin_tag();
38 }
39}
40
48template <IsUltraOrMegaBuilder Builder>
50 : raw_entries(table_entries)
51 , length(raw_entries.size())
52{
53 for (const auto& entry : table_entries) {
54 if (entry.get_context() != nullptr) {
55 context = entry.get_context();
56 break;
57 }
58 }
59
60 index_initialized.resize(length, false);
61
62 // Do not initialize the table yet. The input entries might all be constant,
63 // if this is the case we might not have a valid pointer to a Builder
64 // We get around this, by initializing the table when `read` or `write` operator is called
65 // with a non-const field element.
66
67 // Store tags
68 _tags.resize(length);
69 for (size_t i = 0; i < length; i++) {
70 _tags[i] = table_entries[i].get_origin_tag();
71 }
72}
73
83template <IsUltraOrMegaBuilder Builder> void ram_table<Builder>::initialize_table() const
84{
85 if (ram_table_generated_in_builder) {
86 return;
87 }
88 // only call this when there is a context
89 BB_ASSERT(context != nullptr);
90 ram_id = context->create_RAM_array(length);
91
92 if (raw_entries.size() > 0) {
93 for (size_t i = 0; i < length; ++i) {
94 if (!index_initialized[i]) {
95 field_pt entry;
96 if (raw_entries[i].is_constant()) {
98 context->put_constant_variable(raw_entries[i].get_value()));
99 } else {
100 entry = raw_entries[i];
101 }
102 context->init_RAM_element(ram_id, i, entry.get_witness_index());
103 index_initialized[i] = true;
104 }
105 }
106 }
107
108 // Store the tags of the original entries
109 _tags.resize(length);
110 if (raw_entries.size() > 0) {
111 for (size_t i = 0; i < length; i++) {
112 _tags[i] = raw_entries[i].get_origin_tag();
113 }
114 }
115 ram_table_generated_in_builder = true;
116}
117// constructors and move operators
118template <IsUltraOrMegaBuilder Builder> ram_table<Builder>::ram_table(const ram_table& other) = default;
119template <IsUltraOrMegaBuilder Builder> ram_table<Builder>::ram_table(ram_table&& other) = default;
120template <IsUltraOrMegaBuilder Builder>
122template <IsUltraOrMegaBuilder Builder> ram_table<Builder>& ram_table<Builder>::operator=(ram_table&& other) = default;
123
131template <IsUltraOrMegaBuilder Builder> field_t<Builder> ram_table<Builder>::read(const field_pt& index) const
132{
133 if (context == nullptr) {
136 context,
137 nullptr,
138 "ram_table: Performing a read operation without providing a context. We cannot initialize the table.");
139 }
140
141 // When we perform the first read operation, we initialize the table
142 initialize_table();
143
144 const auto native_index = uint256_t(index.get_value());
145 if (native_index >= length) {
146 // set a failure when the index is out of bounds. another error will be thrown when we try to call
147 // `read_RAM_array`.
148 context->failure("ram_table: RAM array access out of bounds");
149 }
150
151 if (!check_indices_initialized()) {
152 context->failure("ram_table must have initialized every RAM entry before the table can be read");
153 }
154
155 field_pt index_wire = index;
156 if (index.is_constant()) {
157 index_wire = field_pt::from_witness_index(context, context->put_constant_variable(index.get_value()));
158 }
159
160 uint32_t output_idx = context->read_RAM_array(ram_id, index_wire.get_witness_index());
161 auto element = field_pt::from_witness_index(context, output_idx);
162
163 const size_t cast_index = static_cast<size_t>(static_cast<uint64_t>(native_index));
164 // If the index is legitimate, restore the tag
165 if (native_index < length) {
166 element.set_origin_tag(_tags[cast_index]);
167 }
168 return element;
169}
170
180template <IsUltraOrMegaBuilder Builder> void ram_table<Builder>::write(const field_pt& index, const field_pt& value)
181{
182 if (context == nullptr) {
183 context = index.get_context();
185 context,
186 nullptr,
187 "ram_table: Performing a write operation without providing a context. We cannot initialize the table.");
188 }
189
190 // When we perform the first read operation, we initialize the table
191 initialize_table();
192
193 if (uint256_t(index.get_value()) >= length) {
194 // set a failure when the index is out of bounds. an error will be thrown when we try to call either
195 // `init_RAM_element` or `write_RAM_array`.
196 context->failure("ram_table: RAM array access out of bounds");
197 }
198
199 field_pt index_wire = index;
200 const auto native_index = uint256_t(index.get_value());
201 if (index.is_constant()) {
202 // need to write/process every array element at constant indicies before doing reads/writes at prover-defined
203 // indices
205 } else {
206 if (!check_indices_initialized()) {
207 context->failure("ram_table must have initialized every RAM entry before a write can be performed");
208 }
209 }
210
211 field_pt value_wire = value;
212 auto native_value = value.get_value();
213 if (value.is_constant()) {
214 value_wire = field_pt::from_witness_index(context, context->put_constant_variable(native_value));
215 }
216
217 const size_t cast_index = static_cast<size_t>(static_cast<uint64_t>(native_index));
218 if (index.is_constant() && !index_initialized[cast_index]) {
219 context->init_RAM_element(ram_id, cast_index, value_wire.get_witness_index());
220
221 index_initialized[cast_index] = true;
222 } else {
223 context->write_RAM_array(ram_id, index_wire.get_witness_index(), value_wire.get_witness_index());
224 }
225 // Update the value of the stored tag, if index is legitimate
226
227 if (native_index < length) {
228 _tags[cast_index] = value.get_origin_tag();
229 }
230}
231
234} // namespace bb::stdlib
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
#define BB_ASSERT_NEQ(actual, expected,...)
Definition assert.hpp:92
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:62
Builder * get_context() const
Definition field.hpp:419
OriginTag get_origin_tag() const
Definition field.hpp:346
void convert_constant_to_fixed_witness(Builder *ctx)
Definition field.hpp:444
uint32_t get_witness_index() const
Get the witness index of the current field element.
Definition field.hpp:506
ram_table & operator=(const ram_table &other)
std::vector< bool > index_initialized
Definition ram_table.hpp:60
field_pt read(const field_pt &index) const
Read a field element from the RAM table at an index value.
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:83
AluTraceBuilder builder
Definition alu.test.cpp:124
StrictMock< MockContext > context
uint8_t const size_t length
Definition data_store.hpp:9
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...
Definition biggroup.hpp:995
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...