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: Complete, auditors: [Sergei], commit: 777717f6af324188ecd6bb68c3c86ee7befef94d}
3// external_1: { status: Complete, auditors: [@ed25519 (Spearbit)], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
14#pragma once
15
24
25namespace bb::stdlib {
26
27template <typename Field> class StdlibCodec {
28 public:
29 using DataType = Field;
30 using Builder = typename Field::Builder;
35
43 template <typename T> static T convert_short_challenge(const fr& challenge)
44 {
45 if constexpr (std::is_same_v<T, fr>) {
46 return challenge;
47 } else if constexpr (std::is_same_v<T, fq>) {
48 // Sanity check that the input challenge fits into the first 2 bigfield limbs.
49 BB_ASSERT_LT(static_cast<uint256_t>(challenge.get_value()).get_msb(),
50 T::NUM_LIMB_BITS * 2,
51 "field_conversion: convert_challenge");
52 Builder* builder = challenge.get_context();
53 // All challenges must be circuit witnesses.
55 BB_ASSERT(!challenge.is_constant());
56 auto high_limb = fr::from_witness_index(builder, builder->zero_idx());
57 high_limb.set_origin_tag(challenge.get_origin_tag());
58 return T(challenge, high_limb);
59 }
60 }
61
70 template <typename T> static T convert_full_challenge(const fr& challenge)
71 {
72 if constexpr (std::is_same_v<T, fr>) {
73 return challenge;
74 } else if constexpr (std::is_same_v<T, fq>) {
75 // Decompose at the bigfield's lower-two-limb boundary; split_unique binds the halves to the challenge.
76 static constexpr size_t LOW_BITS = 2 * T::NUM_LIMB_BITS;
77 const auto [low_bits, high_bits] = split_unique(challenge, LOW_BITS);
78 return T(low_bits, high_bits);
79 }
80 }
81
82 static std::vector<fr> convert_goblin_fr_to_bn254_frs(const goblin_field<Builder>& input)
83 {
84 return { input.limbs[0], input.limbs[1] };
85 }
86
87 static std::vector<fr> convert_grumpkin_fr_to_bn254_frs(const fq& input)
88 {
89 static constexpr uint64_t NUM_LIMB_BITS = fq::NUM_LIMB_BITS;
90
91 static constexpr bb::fr shift(static_cast<uint256_t>(1) << NUM_LIMB_BITS);
92 std::vector<fr> result(2);
93 result[0] = input.get_limb(0).element + (input.get_limb(1).element * shift);
94 result[1] = input.get_limb(2).element + (input.get_limb(3).element * shift);
95 return result;
96 }
97
101 template <typename T> static constexpr size_t calc_num_fields()
102 {
103 if constexpr (IsAnyOf<T, fr>) {
105 } else if constexpr (IsAnyOf<T, fq, goblin_field<Builder>>) {
108 return 2 * calc_num_fields<typename T::BaseField>();
109 } else {
110 // Array or Univariate
111 return calc_num_fields<typename T::value_type>() * (std::tuple_size<T>::value);
112 }
113 }
114
151 template <typename T> static T deserialize_from_fields(std::span<const fr> fr_vec)
152 {
153 using field_ct = fr;
154 using bigfield_ct = fq;
155
156 constexpr size_t expected_size = calc_num_fields<T>();
157 BB_ASSERT_EQ(fr_vec.size(), expected_size);
158
159 BB_ASSERT(validate_context<Builder>(fr_vec));
160
161 if constexpr (IsAnyOf<T, field_ct>) {
162 // Case 1: input type matches the output type
163 return fr_vec[0];
164 } else if constexpr (IsAnyOf<T, bigfield_ct>) {
165 // Case 2: bigfield is reconstructed from low and high limbs with in-field validation.
166 // This ensures aliased values (>= Fq::modulus) are rejected.
167 T result(fr_vec[0], fr_vec[1]);
168 result.assert_is_in_field();
169 return result;
170 } else if constexpr (IsAnyOf<T, goblin_field<Builder>>) {
171 // Case 3: goblin_field stores limbs as-is; range validation is deferred to Translator circuit.
172 return T(fr_vec[0], fr_vec[1]);
174 // Case 4 and 5: Convert a vector of frs to a group element
175 using Basefield = typename T::BaseField;
176
177 constexpr size_t base_field_frs = expected_size / 2;
178
179 Basefield x = deserialize_from_fields<Basefield>(fr_vec.subspan(0, base_field_frs));
180 Basefield y = deserialize_from_fields<Basefield>(fr_vec.subspan(base_field_frs, base_field_frs));
181
182 // Construct the group element (validates the point is on curve).
183 // The 2-arg constructor auto-detects infinity from (x == 0 && y == 0).
184 // For goblin_element (bn254 with Mega arithmetization), the on-curve check is delegated to ECCVM, see
185 // `on_curve_check` in `ECCVMTranscriptRelationImpl`.
186 return T(x, y, /*assert_on_curve=*/true);
187 } else {
188 // Case 6: Array or Univariate
189 T val;
190 using element_type = typename T::value_type;
191 const size_t scalar_frs = calc_num_fields<element_type>();
192
193 size_t i = 0;
194 for (auto& x : val) {
195 x = deserialize_from_fields<element_type>(fr_vec.subspan(scalar_frs * i, scalar_frs));
196 ++i;
197 }
198 return val;
199 }
200 }
201
252 template <typename T> static std::vector<fr> serialize_to_fields(const T& val)
253 {
254 using field_ct = fr;
255 using bigfield_ct = fq;
256
257 if constexpr (IsAnyOf<T, field_ct>) {
258 return std::vector<T>{ val };
259 } else if constexpr (IsAnyOf<T, bigfield_ct>) {
261 } else if constexpr (IsAnyOf<T, goblin_field<Builder>>) {
263 } else if constexpr (IsAnyOf<T, grumpkin_commitment>) {
264 // Canonicalize: output (0,0) for infinity points, critical for the IPA accumulation flow.
265 auto is_inf = val.is_point_at_infinity();
266 fr canon_x = fr::conditional_assign(is_inf, fr(0), val.x());
267 fr canon_y = fr::conditional_assign(is_inf, fr(0), val.y());
268 return { canon_x, canon_y };
269 } else if constexpr (IsAnyOf<T, bn254_commitment>) {
270 // bn254_commitment serialization does not standardize infinity (unlike grumpkin_commitment above).
271 // All existing code paths produce canonical (0,0) infinity, so just assert that invariant.
272 if (val.get_value().is_point_at_infinity()) {
273 BB_ASSERT(val.x().get_value() == 0 && val.y().get_value() == 0,
274 "serialize_to_fields: bn254_commitment point at infinity must be canonical (0,0)");
275 }
276 using BaseField = typename T::BaseField;
277
278 std::vector<field_ct> fr_vec_x = serialize_to_fields<BaseField>(val.x());
279 std::vector<field_ct> fr_vec_y = serialize_to_fields<BaseField>(val.y());
280 std::vector<field_ct> fr_vec = std::move(fr_vec_x);
281 fr_vec.insert(fr_vec.end(), fr_vec_y.begin(), fr_vec_y.end());
282 return fr_vec;
283 } else {
284 // Array or Univariate
286 for (auto& x : val) {
287 auto tmp_vec = serialize_to_fields<typename T::value_type>(x);
288 fr_vec.insert(fr_vec.end(), tmp_vec.begin(), tmp_vec.end());
289 }
290 return fr_vec;
291 }
292 }
301 static std::array<fr, 2> split_challenge(const fr& challenge)
302 {
303 constexpr size_t TOTAL_BITS = fr::native::modulus.get_msb() + 1; // 254
304 constexpr size_t lo_bits = TOTAL_BITS / 2; // 127
305 // Construct a unique lo/hi decomposition of the challenge (hi_bits will be 127)
306 const auto [lo, hi] = split_unique(challenge, lo_bits);
307 return std::array<fr, 2>{ lo, hi };
308 }
309
320 template <typename TargetType> static TargetType deserialize_from_frs(std::span<fr> elements, size_t& num_frs_read)
321 {
322 constexpr size_t num_frs = calc_num_fields<TargetType>();
323 BB_ASSERT_GTE(elements.size(), num_frs_read + num_frs);
324 TargetType result = deserialize_from_fields<TargetType>(elements.subspan(num_frs_read, num_frs));
325 num_frs_read += num_frs;
326 return result;
327 }
328};
329} // namespace bb::stdlib
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:128
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
static constexpr size_t NUM_BN254_SCALARS
Definition fq.hpp:146
static constexpr size_t NUM_BN254_SCALARS
Definition fr.hpp:148
constexpr uint64_t get_msb() const
static constexpr size_t calc_num_fields()
Calculates the size of a type (in its native form) in terms of frs.
bigfield< Builder, bb::Bn254FqParams > fq
typename Field::Builder Builder
static T convert_full_challenge(const fr &challenge)
Convert a full-width challenge to a target type (fr or fq).
static std::vector< fr > convert_goblin_fr_to_bn254_frs(const goblin_field< Builder > &input)
static std::vector< fr > convert_grumpkin_fr_to_bn254_frs(const fq &input)
element< Builder, fq, fr, curve::BN254::Group > bn254_commitment
static std::array< fr, 2 > split_challenge(const fr &challenge)
Split a challenge field element into two equal-width challenges.
static T deserialize_from_fields(std::span< const fr > fr_vec)
Core stdlib Transcript deserialization method.
static TargetType deserialize_from_frs(std::span< fr > elements, size_t &num_frs_read)
A stdlib VerificationKey-specific method.
static std::vector< fr > serialize_to_fields(const T &val)
Core stdlib Transcript serialization method.
static T convert_short_challenge(const fr &challenge)
A stdlib Transcript method needed to convert a short fr challenge limb to a bigfield one.
const Limb & get_limb(size_t i) const
Read-only access to a binary basis limb (element + maximum_value).
Definition bigfield.hpp:80
cycle_group represents a group Element of the proving system's embedded curve, i.e....
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:67
Builder * get_context() const
Definition field.hpp:445
OriginTag get_origin_tag() const
Definition field.hpp:372
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:838
static constexpr uint256_t modulus
Definition field.hpp:237
bool is_constant() const
Definition field.hpp:455
static field_t conditional_assign(const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
Definition field.hpp:398
goblin_field wraps x/y coordinates of bn254 group elements when using goblin
std::array< field_ct, 2 > limbs
AluTraceBuilder builder
Definition alu.test.cpp:124
std::pair< field_t< Builder >, field_t< Builder > > split_unique(const field_t< Builder > &field, const size_t lo_bits, const bool skip_range_constraints)
Split a bn254 scalar field element into unique lo and hi limbs.
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 ScalarIndex shift(ScalarIndex ctx, size_t d)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
VectorField result