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 BB_ASSERT(val.on_curve());
137 return val;
138 } else {
139 // Array or Univariate
140 T val;
141 constexpr size_t SZ = calc_num_fields<typename T::value_type>();
142 size_t i = 0;
143 for (auto& x : val) {
144 x = deserialize_from_fields<typename T::value_type>(fr_vec.subspan(SZ * i, SZ));
145 ++i;
146 }
147 return val;
148 }
149 }
150
154 template <typename T> static std::vector<fr> serialize_to_fields(const T& val)
155 {
157 return { val };
158 } else if constexpr (IsAnyOf<T, fq>) {
161 using BaseField = typename T::Fq;
162 std::vector<bb::fr> fr_vec_x =
163 val.is_point_at_infinity() ? serialize_to_fields(BaseField::zero()) : serialize_to_fields(val.x);
164 std::vector<bb::fr> fr_vec_y =
165 val.is_point_at_infinity() ? serialize_to_fields(BaseField::zero()) : serialize_to_fields(val.y);
166 // Use move + append to avoid iterator-based insert that triggers GCC false positive
167 std::vector<bb::fr> fr_vec = std::move(fr_vec_x);
168 fr_vec.reserve(fr_vec.size() + fr_vec_y.size());
169 for (auto& e : fr_vec_y) {
170 fr_vec.push_back(std::move(e));
171 }
172 return fr_vec;
173 } else {
174 // Array or Univariate
175 std::vector<fr> out;
176 for (auto& x : val) {
177 auto tmp = serialize_to_fields(x);
178 for (auto& e : tmp) {
179 out.push_back(std::move(e));
180 }
181 }
182 return out;
183 }
184 }
185
195 {
196 static constexpr size_t TOTAL_BITS = bb::fr::modulus.get_msb() + 1; // 254
197 static constexpr size_t LO_BITS = TOTAL_BITS / 2; // 127
198 static constexpr size_t HI_BITS = TOTAL_BITS - LO_BITS; // 127
199
200 const uint256_t u = static_cast<uint256_t>(challenge);
201 const uint256_t lo = u.slice(0, LO_BITS);
202 const uint256_t hi = u.slice(LO_BITS, LO_BITS + HI_BITS);
203
204 return { bb::fr(lo), bb::fr(hi) };
205 }
206
210 template <typename T> static T convert_challenge(const bb::fr& challenge)
211 {
212 if constexpr (std::is_same_v<T, bb::fr>) {
213 return challenge;
214 } else if constexpr (std::is_same_v<T, fq>) {
215 BB_ASSERT_LT(static_cast<uint256_t>(challenge).get_msb(),
216 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION,
217 "field_conversion: convert challenge");
218 return fq(challenge);
219 }
220 }
221};
222
224 public:
226 using fr = bb::fr;
230
231 // Size calculators
232 template <typename T> static constexpr size_t calc_num_fields()
233 {
235 return 1;
237 // In contrast to bb::fr, bn254 points can be represented by only 2 uint256_t elements
238 return 2;
239 } else {
240 // Array or Univariate
241 return calc_num_fields<typename T::value_type>() * (std::tuple_size<T>::value);
242 }
243 }
244
245 // ---------------------------------------------------------------------
246 // Deserialize
247 // ---------------------------------------------------------------------
248 template <typename T> static T deserialize_from_fields(std::span<const uint256_t> vec)
249 {
250 BB_ASSERT_EQ(vec.size(), calc_num_fields<T>());
251 if constexpr (IsAnyOf<T, bool>) {
252 return static_cast<bool>(vec[0]);
254 return static_cast<T>(vec[0]);
256 using BaseField = typename T::Fq;
257 constexpr size_t N = calc_num_fields<BaseField>();
258 T val;
259 val.x = deserialize_from_fields<BaseField>(vec.subspan(0, N));
260 val.y = deserialize_from_fields<BaseField>(vec.subspan(N, N));
261 if (val.x == BaseField::zero() && val.y == BaseField::zero()) {
262 val.self_set_infinity();
263 }
264 BB_ASSERT(val.on_curve());
265 return val;
266 } else {
267 // Array or Univariate
268 T val;
269 constexpr size_t SZ = calc_num_fields<typename T::value_type>();
270 size_t i = 0;
271 for (auto& x : val) {
272 x = deserialize_from_fields<typename T::value_type>(vec.subspan(SZ * i, SZ));
273 ++i;
274 }
275 return val;
276 }
277 }
278
282 template <typename T> static std::vector<uint256_t> serialize_to_fields(const T& val)
283 {
285 return { val };
287 using BaseField = typename T::Fq;
288 std::vector<uint256_t> uint256_vec_x;
289 std::vector<uint256_t> uint256_vec_y;
290 // When encountering a point at infinity we pass a zero point in the proof to ensure that on the receiving
291 // size there are no inconsistencies whenre constructing and hashing.
292 if (val.is_point_at_infinity()) {
293 uint256_vec_x = serialize_to_fields(BaseField::zero());
294 uint256_vec_y = serialize_to_fields(BaseField::zero());
295 } else {
296 uint256_vec_x = serialize_to_fields<BaseField>(val.x);
297 uint256_vec_y = serialize_to_fields<BaseField>(val.y);
298 }
299 std::vector<uint256_t> uint256_vec(uint256_vec_x.begin(), uint256_vec_x.end());
300 uint256_vec.insert(uint256_vec.end(), uint256_vec_y.begin(), uint256_vec_y.end());
301 return uint256_vec;
302 } else {
303 // Array or Univariate
305 for (auto& e : val) {
306 auto tmp = serialize_to_fields(e);
307 out.insert(out.end(), tmp.begin(), tmp.end());
308 }
309 return out;
310 }
311 }
312
322 {
323 static constexpr size_t TOTAL_BITS = bb::fr::modulus.get_msb() + 1; // 254
324 static constexpr size_t LO_BITS = TOTAL_BITS / 2; // 127
325 static constexpr size_t HI_BITS = TOTAL_BITS - LO_BITS; // 127
326
327 const uint256_t u = static_cast<uint256_t>(challenge);
328 const uint256_t lo = u.slice(0, LO_BITS);
329 const uint256_t hi = u.slice(LO_BITS, LO_BITS + HI_BITS);
330
331 return { uint256_t(lo), uint256_t(hi) };
332 }
333
337 template <typename T> static T convert_challenge(const bb::fr& challenge)
338 {
339 if constexpr (std::is_same_v<T, bb::fr>) {
340 return challenge;
341 } else if constexpr (std::is_same_v<T, fq>) {
342 BB_ASSERT_LT(static_cast<uint256_t>(challenge).get_msb(),
343 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION,
344 "field_conversion: convert challenge");
345 return fq(challenge);
346 }
347 }
348};
349
350} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
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 T convert_challenge(const bb::fr &challenge)
Convert an fr challenge to a target type (fr or fq). Assumes challenge is "short".
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 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_challenge(const bb::fr &challenge)
Convert an fr challenge to a target type (fr or fq). Assumes challenge is "short".
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)
typename Group::affine_element AffineElement
Definition bn254.hpp:22
typename Group::affine_element AffineElement
Definition grumpkin.hpp:65
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:49
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
static constexpr uint256_t modulus