Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
byte_array.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 "byte_array.hpp"
8
9#include "../circuit_builders/circuit_builders.hpp"
11
12using namespace bb;
13
14namespace bb::stdlib {
15
16template <typename Builder>
18 : context(parent_context)
19{}
20
21template <typename Builder>
22byte_array<Builder>::byte_array(Builder* parent_context, const size_t n)
23 : context(parent_context)
24 , values(std::vector<field_t<Builder>>(n))
25{}
26
33template <typename Builder>
34byte_array<Builder>::byte_array(Builder* parent_context, std::vector<uint8_t> const& input)
35 : context(parent_context)
36 , values(input.size())
39 for (size_t i = 0; i < input.size(); ++i) {
40 uint8_t c = input[i];
42 value.create_range_constraint(8, "byte_array: vector entry larger than 1 byte.");
43 values[i] = value;
44 }
45 set_free_witness_tag();
53template <typename Builder>
54byte_array<Builder>::byte_array(Builder* parent_context, const std::string& input)
55 : byte_array(parent_context, std::vector<uint8_t>(input.begin(), input.end()))
56{}
57
103template <typename Builder>
105 const size_t num_bytes,
107{
109
110 static constexpr size_t max_num_bytes = 32;
111 static constexpr size_t midpoint = max_num_bytes / 2;
112 constexpr uint256_t one(1);
113
114 BB_ASSERT_LTE(num_bytes, max_num_bytes);
115 // If a test 256-bit is injected, use it to create a byte decomposition.
116 const uint256_t value = test_val.has_value() ? *test_val : static_cast<uint256_t>(input.get_value());
117
118 values.resize(num_bytes);
119
120 // To optimize the computation of the reconstructed_hi and reconstructed_lo, we use the `field_t::accumulate`
121 // method.
122 std::vector<field_t> accumulator_lo;
123 std::vector<field_t> accumulator_hi;
124
125 context = input.get_context();
126
127 for (size_t i = 0; i < num_bytes; ++i) {
128
129 size_t bit_start = (num_bytes - i - 1) * 8;
130 size_t bit_end = bit_start + 8;
131 const field_t scaling_factor = one << bit_start;
132
133 // Extract the current byte
134 const uint256_t byte_val = value.slice(bit_start, bit_end);
135 // Ensure that current `byte_array` element is a witness iff input is a witness and that it is range
136 // constrained.
137 field_t byte = input.is_constant() ? field_t(byte_val) : witness_t(context, byte_val);
138 byte.create_range_constraint(8, "byte_array: byte extraction failed.");
139 values[i] = byte;
140
141 if (i < midpoint) {
142 accumulator_hi.push_back(scaling_factor * byte);
143 } else {
144 accumulator_lo.push_back(scaling_factor * byte);
145 }
146 }
147
148 // Reconstruct the high and low limbs of input from the byte decomposition
149 const field_t reconstructed_lo = field_t::accumulate(accumulator_lo);
150 const field_t reconstructed_hi = field_t::accumulate(accumulator_hi);
151 const field_t reconstructed = reconstructed_hi + reconstructed_lo;
152
153 // Ensure that the reconstruction succeeded
154 input.assert_equal(reconstructed);
155
156 // Handle the case when the decomposition is not unique
157 if (num_bytes == 32) {
158 // For a modulus `r`, split `r - 1` into limbs
159 constexpr uint256_t modulus_minus_one = fr::modulus - 1;
160 constexpr uint256_t s_lo = modulus_minus_one.slice(0, 128);
161 constexpr uint256_t s_hi = modulus_minus_one.slice(128, 256);
162 const uint256_t shift = uint256_t(1) << 128;
163
164 // Ensure that `(r - 1).lo + 2 ^ 128 - reconstructed_lo` is a 129 bit integer by slicing it into a 128- and 1-
165 // bit chunks.
166 const field_t diff_lo = -reconstructed_lo + s_lo + shift;
167 const uint256_t diff_lo_value = diff_lo.get_value();
168
169 // Extract the "borrow" bit
170 const uint256_t diff_lo_hi_value = (diff_lo_value >> 128);
171 const field_t diff_lo_hi =
172 input.is_constant() ? field_t(diff_lo_hi_value) : witness_t<Builder>(context, diff_lo_hi_value);
173 diff_lo_hi.create_range_constraint(1, "byte_array: y_overlap is not a bit");
174
175 // Extract first 128 bits of `diff_lo`
176 const uint256_t lo_mask = shift - 1;
177 const uint256_t lo = diff_lo_value & lo_mask;
178 const field_t diff_lo_lo = input.is_constant() ? field_t(lo) : witness_t(context, lo);
179 diff_lo_lo.create_range_constraint(128, "byte_array: y_remainder doesn't fit in 128 bits.");
180
181 // Both chunks were computed out-of-circuit - need to constrain. The range constraints above ensure that
182 // they are not overlapping.
183 diff_lo.assert_equal(diff_lo_lo + diff_lo_hi * shift);
184
185 const field_t overlap = -diff_lo_hi + 1;
186 // Ensure that (r - 1).hi - reconstructed_hi/shift - overlap is positive.
187 const field_t diff_hi = (-reconstructed_hi / shift).add_two(s_hi, -overlap);
188 diff_hi.create_range_constraint(128, "byte_array: y_hi doesn't fit in 128 bits.");
189 }
190
191 set_origin_tag(input.tag);
192}
193
194template <typename Builder>
195byte_array<Builder>::byte_array(Builder* parent_context, bytes_t const& input)
196 : context(parent_context)
197 , values(input)
198{}
199
200template <typename Builder>
202 : context(parent_context)
203 , values(input)
204{}
205
206template <typename Builder>
208 : context(other.context)
209{
210 std::copy(other.values.begin(), other.values.end(), std::back_inserter(values));
211}
212
213template <typename Builder>
215 : context(other.context)
216 , values(std::move(other.values))
217{}
218
219template <typename Builder> byte_array<Builder>& byte_array<Builder>::operator=(const byte_array& other)
220{
221 context = other.context;
223 std::copy(other.values.begin(), other.values.end(), std::back_inserter(values));
224 return *this;
225}
226
228{
229 context = other.context;
230 values = std::move(other.values);
231 return *this;
232}
233
239template <typename Builder> byte_array<Builder>::operator field_t<Builder>() const
240{
241 const size_t bytes = values.size();
242 ASSERT(bytes < 32);
243 static constexpr uint256_t one(1);
244 std::vector<field_t<Builder>> scaled_values;
245
246 for (size_t i = 0; i < bytes; ++i) {
247 const field_t<Builder> scaling_factor(one << (8 * (bytes - i - 1)));
248 scaled_values.push_back(values[i] * scaling_factor);
249 }
250
251 return field_t<Builder>::accumulate(scaled_values);
252}
256template <typename Builder> byte_array<Builder>& byte_array<Builder>::write(byte_array const& other)
257{
258 values.insert(values.end(), other.bytes().begin(), other.bytes().end());
259 return *this;
260}
261
266template <typename Builder> byte_array<Builder>& byte_array<Builder>::write_at(byte_array const& other, size_t index)
267{
268 BB_ASSERT_LTE(index + other.values.size(), values.size());
269 for (size_t i = 0; i < other.values.size(); i++) {
270 values[i + index] = other.values[i];
271 }
272 return *this;
273}
274
278template <typename Builder> byte_array<Builder> byte_array<Builder>::slice(size_t offset) const
279{
280 BB_ASSERT_LT(offset, values.size());
281 return byte_array(context, bytes_t(values.begin() + static_cast<ptrdiff_t>(offset), values.end()));
282}
283
288template <typename Builder> byte_array<Builder> byte_array<Builder>::slice(size_t offset, size_t length) const
289{
290 BB_ASSERT_LT(offset, values.size());
291 // it's <= cause vector constructor doesn't include end point
292 BB_ASSERT_LTE(length, values.size() - offset);
293 auto start = values.begin() + static_cast<ptrdiff_t>(offset);
294 auto end = values.begin() + static_cast<ptrdiff_t>((offset + length));
295 return byte_array(context, bytes_t(start, end));
296}
297
301template <typename Builder> byte_array<Builder> byte_array<Builder>::reverse() const
302{
303 bytes_t bytes(values.size());
304 size_t offset = bytes.size() - 1;
305 for (size_t i = 0; i < bytes.size(); i += 1, offset -= 1) {
306 bytes[offset] = values[i];
307 }
308 return byte_array(context, bytes);
309}
310
315template <typename Builder> std::vector<uint8_t> byte_array<Builder>::get_value() const
316{
317 const size_t length = values.size();
318 std::vector<uint8_t> bytes(length, 0);
319 for (size_t i = 0; i < length; ++i) {
320 bytes[i] = static_cast<uint8_t>(values[i].get_value());
321 }
322 return bytes;
323}
324
329template <typename Builder> std::string byte_array<Builder>::get_string() const
330{
331 auto v = get_value();
332 return std::string(v.begin(), v.end());
333}
334
337
338} // namespace bb::stdlib
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:129
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:115
#define ASSERT(expression,...)
Definition assert.hpp:49
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Represents a dynamic array of bytes in-circuit.
byte_array slice(size_t offset) const
Slice bytes from the byte array starting at offset. Does not add any constraints.
byte_array(Builder *parent_context=nullptr)
byte_array reverse() const
Reverse the bytes in the byte array.
byte_array & write_at(byte_array const &other, size_t index)
Overwrites this byte_array starting at index with the contents of other. Asserts that the write does ...
byte_array & write(byte_array const &other)
Appends the contents of another byte_array (other) to the end of this one.
byte_array & operator=(const byte_array &other)
std::vector< uint8_t > get_value() const
A helper converting a byte_array into the vector of its uint8_t values.
bytes_t const & bytes() const
std::string get_string() const
Given a byte_array, compute a vector containing the values of its entries and convert it to a string.
typename std::vector< field_t< Builder > > bytes_t
void assert_equal(const field_t &rhs, std::string const &msg="field_t::assert_equal") const
Copy constraint: constrain that *this field is equal to rhs element.
Definition field.cpp:929
static field_t accumulate(const std::vector< field_t > &input)
Efficiently compute the sum of vector entries. Using big_add_gate we reduce the number of gates neede...
Definition field.cpp:1147
void create_range_constraint(size_t num_bits, std::string const &msg="field_t::range_constraint") const
Let x = *this.normalize(), constrain x.v < 2^{num_bits}.
Definition field.cpp:908
Builder * get_context() const
Definition field.hpp:389
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:827
bool is_constant() const
Definition field.hpp:399
StrictMock< MockContext > context
uint8_t const size_t length
Definition data_store.hpp:9
ssize_t offset
Definition engine.cpp:36
Entry point for Barretenberg command-line interface.
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr uint256_t modulus