Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
field_conversion.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Planned, auditors: [Raju], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
8
15#include "barretenberg/stdlib/primitives/bigfield/constants.hpp" // NUM_LIMB_BITS_IN_FIELD_SIMULATION
16
17namespace bb {
18
19class FrCodec {
20 public:
22 using fr = bb::fr;
26
27 // Size calculators
28 template <typename T> static constexpr size_t calc_num_fields()
29 {
31 return 1;
32 } else if constexpr (IsAnyOf<T, bb::fr, fq>) {
33 return T::Params::NUM_BN254_SCALARS;
35 return 2 * calc_num_fields<typename T::Fq>();
36 } else {
37 // Array or Univariate
38 return calc_num_fields<typename T::value_type>() * (std::tuple_size<T>::value);
39 }
40 }
41
49 template <typename T> static bool check_point_at_infinity(std::span<const bb::fr> fr_vec)
50 {
51 // Check if all limbs are zero - this is the only canonical representation of infinity
52 for (const auto& limb : fr_vec) {
53 if (!limb.is_zero()) {
54 return false;
55 }
56 }
57 return true;
58 }
59
66 {
67 // expects 2 fr limbs; caller already asserts size
68 constexpr uint64_t NUM_LIMB_BITS = stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION; // 68
69 constexpr uint64_t TOTAL_BITS = 254;
70
71 BB_ASSERT_LT(uint256_t(fr_vec[0]),
72 (uint256_t(1) << (NUM_LIMB_BITS * 2)),
73 "Conversion error here usually implies some bad proof serde or parsing");
74 BB_ASSERT_LT(uint256_t(fr_vec[1]),
75 (uint256_t(1) << (TOTAL_BITS - NUM_LIMB_BITS * 2)),
76 "Conversion error here usually implies some bad proof serde or parsing");
77
78 const uint256_t value = uint256_t(fr_vec[0]) + (uint256_t(fr_vec[1]) << (NUM_LIMB_BITS * 2));
79
80 // Reject aliased values to ensure canonical representation.
81 // This matches the circuit behavior in StdlibCodec where assert_is_in_field is called.
82 BB_ASSERT_LT(value, fq::modulus, "Non-canonical field element: value >= fq::modulus");
83
84 return fq(value);
85 }
86
90 static std::vector<bb::fr> convert_grumpkin_fr_to_bn254_frs(const fq& val)
91 {
92 constexpr uint64_t NUM_LIMB_BITS = stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION; // 68
93 constexpr uint64_t TOTAL_BITS = 254;
94
95 constexpr uint64_t LOWER_BITS = 2 * NUM_LIMB_BITS; // 136
96 constexpr uint256_t LOWER_MASK = (uint256_t(1) << LOWER_BITS) - 1;
97
98 const uint256_t value = uint256_t(val);
99 BB_ASSERT_LT(value, (uint256_t(1) << TOTAL_BITS));
100
101 std::vector<bb::fr> out(2);
102 out[0] = static_cast<uint256_t>(value & LOWER_MASK);
103 out[1] = static_cast<uint256_t>(value >> LOWER_BITS);
104
105 BB_ASSERT_LT(static_cast<uint256_t>(out[1]), (uint256_t(1) << (TOTAL_BITS - LOWER_BITS)));
106 return out;
107 }
108
109 // ---------------------------------------------------------------------
110 // Deserialize
111 // ---------------------------------------------------------------------
112 template <typename T> static T deserialize_from_fields(std::span<const fr> fr_vec)
113 {
114 BB_ASSERT_EQ(fr_vec.size(), calc_num_fields<T>());
115 if constexpr (IsAnyOf<T, bool>) {
116 return static_cast<bool>(fr_vec[0]);
117 } else if constexpr (IsAnyOf<T, uint32_t, uint64_t, bb::fr>) {
118 return static_cast<T>(fr_vec[0]);
119 } else if constexpr (IsAnyOf<T, fq>) {
122 using BaseField = typename T::Fq;
123 constexpr size_t BASE = calc_num_fields<BaseField>();
124
125 // Check for point at infinity BEFORE deserializing to avoid alias issues.
126 // Only canonical (0,0) with all-zero limbs is accepted as infinity.
127 // This matches circuit behavior in StdlibCodec::check_point_at_infinity.
128 if (check_point_at_infinity<T>(fr_vec)) {
129 return T::infinity();
130 }
131
132 // Deserialize coordinates (this will reject non-canonical values via BB_ASSERT)
133 T val;
134 val.x = deserialize_from_fields<BaseField>(fr_vec.subspan(0, BASE));
135 val.y = deserialize_from_fields<BaseField>(fr_vec.subspan(BASE, BASE));
136 if (!val.on_curve()) {
137 throw_or_abort("Deserialized point is not on the curve");
138 }
139 return val;
140 } else {
141 // Array or Univariate
142 T val;
143 constexpr size_t SZ = calc_num_fields<typename T::value_type>();
144 size_t i = 0;
145 for (auto& x : val) {
146 x = deserialize_from_fields<typename T::value_type>(fr_vec.subspan(SZ * i, SZ));
147 ++i;
148 }
149 return val;
150 }
151 }
152
156 template <typename T> static std::vector<fr> serialize_to_fields(const T& val)
157 {
159 return { val };
160 } else if constexpr (IsAnyOf<T, fq>) {
163 using BaseField = typename T::Fq;
164 std::vector<bb::fr> fr_vec_x =
165 val.is_point_at_infinity() ? serialize_to_fields(BaseField::zero()) : serialize_to_fields(val.x);
166 std::vector<bb::fr> fr_vec_y =
167 val.is_point_at_infinity() ? serialize_to_fields(BaseField::zero()) : serialize_to_fields(val.y);
168 // Use move + append to avoid iterator-based insert that triggers GCC false positive
169 std::vector<bb::fr> fr_vec = std::move(fr_vec_x);
170 fr_vec.reserve(fr_vec.size() + fr_vec_y.size());
171 for (auto& e : fr_vec_y) {
172 fr_vec.push_back(std::move(e));
173 }
174 return fr_vec;
175 } else {
176 // Array or Univariate
177 std::vector<fr> out;
178 for (auto& x : val) {
179 auto tmp = serialize_to_fields(x);
180 for (auto& e : tmp) {
181 out.push_back(std::move(e));
182 }
183 }
184 return out;
185 }
186 }
187
197 {
198 static constexpr size_t TOTAL_BITS = bb::fr::modulus.get_msb() + 1; // 254
199 static constexpr size_t LO_BITS = TOTAL_BITS / 2; // 127
200 static constexpr size_t HI_BITS = TOTAL_BITS - LO_BITS; // 127
201
202 const uint256_t u = static_cast<uint256_t>(challenge);
203 const uint256_t lo = u.slice(0, LO_BITS);
204 const uint256_t hi = u.slice(LO_BITS, LO_BITS + HI_BITS);
205
206 return { bb::fr(lo), bb::fr(hi) };
207 }
208
214 template <typename T> static T convert_short_challenge(const bb::fr& challenge)
215 {
216 if constexpr (std::is_same_v<T, bb::fr>) {
217 return challenge;
218 } else if constexpr (std::is_same_v<T, fq>) {
219 BB_ASSERT_LT(static_cast<uint256_t>(challenge).get_msb(),
220 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION,
221 "field_conversion: convert challenge");
222 return fq(challenge);
223 }
224 }
225
233 template <typename T> static T convert_full_challenge(const bb::fr& challenge)
234 {
235 if constexpr (std::is_same_v<T, bb::fr>) {
236 return challenge;
237 } else if constexpr (std::is_same_v<T, fq>) {
238 return fq(static_cast<uint256_t>(challenge));
239 }
240 }
241};
242
244 public:
246 using fr = bb::fr;
250
251 // Size calculators
252 template <typename T> static constexpr size_t calc_num_fields()
253 {
255 return 1;
257 // In contrast to bb::fr, bn254 points can be represented by only 2 uint256_t elements
258 return 2;
259 } else {
260 // Array or Univariate
261 return calc_num_fields<typename T::value_type>() * (std::tuple_size<T>::value);
262 }
263 }
264
265 // ---------------------------------------------------------------------
266 // Deserialize
267 // ---------------------------------------------------------------------
268 template <typename T> static T deserialize_from_fields(std::span<const uint256_t> vec)
269 {
270 BB_ASSERT_EQ(vec.size(), calc_num_fields<T>());
271 if constexpr (IsAnyOf<T, bool>) {
272 return static_cast<bool>(vec[0]);
273 } else if constexpr (IsAnyOf<T, bb::fr>) {
275 vec[0], uint256_t(bb::fr::modulus), "Non-canonical scalar field element: value >= fr::modulus");
276 return static_cast<T>(vec[0]);
277 } else if constexpr (IsAnyOf<T, fq>) {
278 BB_ASSERT_LT(vec[0], uint256_t(fq::modulus), "Non-canonical base field element: value >= fq::modulus");
279 return static_cast<T>(vec[0]);
280 } else if constexpr (IsAnyOf<T, uint32_t, uint64_t, uint256_t>) {
281 return static_cast<T>(vec[0]);
283 using BaseField = typename T::Fq;
284 constexpr size_t N = calc_num_fields<BaseField>();
285 T val;
286 val.x = deserialize_from_fields<BaseField>(vec.subspan(0, N));
287 val.y = deserialize_from_fields<BaseField>(vec.subspan(N, N));
288 if (val.x == BaseField::zero() && val.y == BaseField::zero()) {
289 val.self_set_infinity();
290 }
291 if (!val.on_curve()) {
292 throw_or_abort("Deserialized point is not on the curve");
293 }
294 return val;
295 } else {
296 // Array or Univariate
297 T val;
298 constexpr size_t SZ = calc_num_fields<typename T::value_type>();
299 size_t i = 0;
300 for (auto& x : val) {
301 x = deserialize_from_fields<typename T::value_type>(vec.subspan(SZ * i, SZ));
302 ++i;
303 }
304 return val;
305 }
306 }
307
311 template <typename T> static std::vector<uint256_t> serialize_to_fields(const T& val)
312 {
314 return { val };
316 using BaseField = typename T::Fq;
317 std::vector<uint256_t> uint256_vec_x;
318 std::vector<uint256_t> uint256_vec_y;
319 // When encountering a point at infinity we pass a zero point in the proof to ensure that on the receiving
320 // size there are no inconsistencies whenre constructing and hashing.
321 if (val.is_point_at_infinity()) {
322 uint256_vec_x = serialize_to_fields(BaseField::zero());
323 uint256_vec_y = serialize_to_fields(BaseField::zero());
324 } else {
325 uint256_vec_x = serialize_to_fields<BaseField>(val.x);
326 uint256_vec_y = serialize_to_fields<BaseField>(val.y);
327 }
328 std::vector<uint256_t> uint256_vec(uint256_vec_x.begin(), uint256_vec_x.end());
329 uint256_vec.insert(uint256_vec.end(), uint256_vec_y.begin(), uint256_vec_y.end());
330 return uint256_vec;
331 } else {
332 // Array or Univariate
334 for (auto& e : val) {
335 auto tmp = serialize_to_fields(e);
336 out.insert(out.end(), tmp.begin(), tmp.end());
337 }
338 return out;
339 }
340 }
341
351 {
352 static constexpr size_t TOTAL_BITS = bb::fr::modulus.get_msb() + 1; // 254
353 static constexpr size_t LO_BITS = TOTAL_BITS / 2; // 127
354 static constexpr size_t HI_BITS = TOTAL_BITS - LO_BITS; // 127
355
356 const uint256_t u = static_cast<uint256_t>(challenge);
357 const uint256_t lo = u.slice(0, LO_BITS);
358 const uint256_t hi = u.slice(LO_BITS, LO_BITS + HI_BITS);
359
360 return { uint256_t(lo), uint256_t(hi) };
361 }
362
366 template <typename T> static T convert_short_challenge(const bb::fr& challenge)
367 {
368 if constexpr (std::is_same_v<T, bb::fr>) {
369 return challenge;
370 } else if constexpr (std::is_same_v<T, fq>) {
371 BB_ASSERT_LT(static_cast<uint256_t>(challenge).get_msb(),
372 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION,
373 "field_conversion: convert challenge");
374 return fq(challenge);
375 }
376 }
377
381 template <typename T> static T convert_full_challenge(const bb::fr& challenge)
382 {
383 if constexpr (std::is_same_v<T, bb::fr>) {
384 return challenge;
385 } else if constexpr (std::is_same_v<T, fq>) {
386 return fq(static_cast<uint256_t>(challenge));
387 }
388 }
389};
390
391} // namespace bb
constexpr size_t N
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
static T convert_short_challenge(const bb::fr &challenge)
Convert a short (≤127-bit limb) challenge to a target type (fr or fq).
curve::BN254::AffineElement bn254_commitment
grumpkin::fr fq
static std::vector< fr > serialize_to_fields(const T &val)
Conversion from transcript values to bb::frs.
static std::array< bb::fr, 2 > split_challenge(const bb::fr &challenge)
Split a challenge field element into two equal-width challenges.
static bool check_point_at_infinity(std::span< const bb::fr > fr_vec)
Check whether raw limbs represent the point at infinity (all limbs zero).
static fq convert_grumpkin_fr_from_bn254_frs(std::span< const bb::fr > fr_vec)
Converts 2 bb::fr elements to fq.
static std::vector< bb::fr > convert_grumpkin_fr_to_bn254_frs(const fq &val)
Converts fq to 2 bb::fr elements (inverse of the above).
static T convert_full_challenge(const bb::fr &challenge)
Convert a full-width challenge to a target type (fr or fq).
static T deserialize_from_fields(std::span< const fr > fr_vec)
curve::Grumpkin::AffineElement grumpkin_commitment
static constexpr size_t calc_num_fields()
curve::BN254::AffineElement bn254_commitment
curve::Grumpkin::AffineElement grumpkin_commitment
static constexpr size_t calc_num_fields()
static std::array< uint256_t, 2 > split_challenge(const uint256_t &challenge)
Split a challenge field element into two equal-width challenges.
static T convert_short_challenge(const bb::fr &challenge)
Convert a short (≤127-bit limb) challenge to a target type (fr or fq).
static std::vector< uint256_t > serialize_to_fields(const T &val)
Conversion from transcript values to uint256_ts.
static T deserialize_from_fields(std::span< const uint256_t > vec)
static T convert_full_challenge(const bb::fr &challenge)
Convert a full-width challenge to a target type (fr or fq); exact since r < q.
typename Group::affine_element AffineElement
Definition bn254.hpp:22
typename Group::affine_element AffineElement
Definition grumpkin.hpp:64
constexpr uint256_t slice(uint64_t start, uint64_t end) const
constexpr uint64_t get_msb() const
constexpr T get_msb(const T in)
Definition get_msb.hpp:50
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:155
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
bb::VectorAffineElementPushSpan< BaseParams > out
static constexpr uint256_t modulus
void throw_or_abort(std::string const &err)