Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
field_conversion.test.cpp
Go to the documentation of this file.
5#include <gtest/gtest.h>
6
8
10
11template <typename Builder> using fr = field_t<Builder>;
12template <typename Builder> using fq = bigfield<Builder, bb::Bn254FqParams>;
14template <typename Builder> using grumpkin_element = cycle_group<Builder>;
15
16template <typename Builder> class stdlib_field_conversion : public ::testing::Test {
17 public:
19
26 template <typename T, typename CreateFn>
27 void check_deserialization_gate_count(CreateFn create_native, uint32_t expected_gates, size_t num_elements = 1)
28 {
29 using NativeCodec = FrCodec;
31
32 for (size_t i = 0; i < num_elements; ++i) {
33 // Create native value and serialize
34 auto native_value = create_native();
35 auto native_fields = NativeCodec::serialize_to_fields(native_value);
36
37 // Create witnesses from "proof data"
38 std::vector<field_t<Builder>> witness_fields;
39 for (const auto& f : native_fields) {
40 witness_fields.push_back(field_t<Builder>::from_witness(&builder, f));
41 }
42
43 // Deserialize in circuit
44 [[maybe_unused]] auto deserialized = Codec::template deserialize_from_fields<T>(witness_fields);
45 }
46
47 check_circuit_and_gate_count(builder, expected_gates);
48 }
49
50 // Serialize and deserialize
51 template <typename T> void check_conversion(T in, bool valid_circuit = true, bool point_at_infinity = false)
52 {
53 size_t len = Codec::template calc_num_fields<T>();
54 auto frs = Codec::serialize_to_fields(in);
55 EXPECT_EQ(len, frs.size());
56 auto out = Codec::template deserialize_from_fields<T>(frs);
57 bool expected = std::is_same_v<Builder, UltraCircuitBuilder> ? !point_at_infinity : true;
58
59 EXPECT_EQ(in.get_value() == out.get_value(), expected);
60
61 auto ctx = in.get_context();
62
63 EXPECT_EQ(CircuitChecker::check(*ctx), valid_circuit);
64 }
65
66 template <typename T> void check_conversion_iterable(T x)
67 {
68 size_t len = Codec::template calc_num_fields<T>();
69 auto frs = Codec::template serialize_to_fields<T>(x);
70 EXPECT_EQ(len, frs.size());
71 auto y = Codec::template deserialize_from_fields<T>(frs);
72 EXPECT_EQ(x.size(), y.size());
73 for (auto [val1, val2] : zip_view(x, y)) {
74 EXPECT_EQ(val1.get_value(), val2.get_value());
75 }
76 }
77};
78
79using BuilderTypes = testing::Types<UltraCircuitBuilder, MegaCircuitBuilder>;
80
82
87{
88 using Builder = TypeParam;
90 bb::fr field_element_val(
91 std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); // 256 bits
92 fr<Builder> field_element(&builder, field_element_val);
93 this->check_conversion(field_element);
94
95 field_element_val = bb::fr::modulus_minus_two; // modulus - 2
96 field_element = fr<Builder>(&builder, field_element_val);
97 this->check_conversion(field_element);
98
99 field_element_val = bb::fr(1);
100 field_element = fr<Builder>(&builder, field_element_val);
101 this->check_conversion(field_element);
102}
103
107TYPED_TEST(stdlib_field_conversion, FieldConversionGrumpkinFr)
108{
109 using Builder = TypeParam;
111
112 // Constructing bigfield objects with bb::fq values
113 bb::fq field_element_val(
114 std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); // 256 bits
115 this->check_conversion(fq<Builder>::from_witness(&builder, field_element_val));
116}
117
122TYPED_TEST(stdlib_field_conversion, FieldConversionBN254AffineElement)
123{
124 using Builder = TypeParam;
125 { // Serialize and deserialize the bn254 generator
127
128 bn254_element<Builder> group_element =
129 bn254_element<Builder>::from_witness(&builder, curve::BN254::AffineElement::one());
130 this->check_conversion(group_element);
131 }
132 { // Serialize and deserialize a valid bn254 point
134
136 bn254_element<Builder> group_element = bn254_element<Builder>::from_witness(&builder, group_element_val);
137 this->check_conversion(group_element);
138 }
139
140 { // Serialize and deserialize random Grumpkin points
142 const size_t num_points = 50;
143 const curve::BN254::AffineElement native_generator = curve::BN254::AffineElement::one();
144
145 for (size_t i = 0; i < num_points; i++) {
146 bb::fr random_scalar = bb::fr::random_element();
147 bn254_element<Builder> group_element =
148 bn254_element<Builder>::from_witness(&builder, native_generator * random_scalar);
149 this->check_conversion(group_element);
150 }
151 }
152 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1527): Remove `point_at_infinity` flag when point at
153 // infinity is consistently represented.
154 { // Serialize and deserialize the point at infinity
156
157 bn254_element<Builder> group_element =
158 bn254_element<Builder>::from_witness(&builder, curve::BN254::AffineElement::infinity());
159 // The circuit is valid, because the point at infinity is set to `one`.
160 this->check_conversion(group_element, /* valid circuit */ true, /* point at infinity */ true);
161 }
162
163 { // Serialize and deserialize "coordinates" that do not correspond to any point on the curve
165
166 curve::BN254::AffineElement group_element_val(1, 4);
167 bn254_element<Builder> group_element;
169 EXPECT_THROW_OR_ABORT(group_element = bn254_element<Builder>::from_witness(&builder, group_element_val),
170 "");
171 } else {
172 group_element = bn254_element<Builder>::from_witness(&builder, group_element_val);
173 this->check_conversion(group_element);
174 }
175 }
176}
177
182TYPED_TEST(stdlib_field_conversion, FieldConversionGrumpkinAffineElement)
183{
184 using Builder = TypeParam;
185
186 { // Serialize and deserialize the Grumpkin generator
188 grumpkin_element<Builder> group_element =
189 grumpkin_element<Builder>::from_witness(&builder, curve::Grumpkin::AffineElement::one());
190 this->check_conversion(group_element);
191 }
192 { // Serialize and deserialize random Grumpkin points
194 const size_t num_points = 50;
195 const curve::Grumpkin::AffineElement native_generator = curve::Grumpkin::AffineElement::one();
196
197 for (size_t i = 0; i < num_points; i++) {
198 bb::fq random_scalar = bb::fq::random_element();
199 grumpkin_element<Builder> group_element =
200 grumpkin_element<Builder>::from_witness(&builder, native_generator * random_scalar);
201 this->check_conversion(group_element);
202 }
203 }
204
205 { // Serialize and deserialize "coordinates" that do not correspond to any point on the curve
206 BB_DISABLE_ASSERTS(); // Avoid on_curve assertion failure in cycle_group constructor
208
209 curve::Grumpkin::AffineElement group_element_val(12, 100);
211 this->check_conversion(group_element, /* valid circuit */ false);
212 }
213
214 { // Serialize and deserialize the point at infinity
216
217 grumpkin_element<Builder> group_element =
218 grumpkin_element<Builder>::from_witness(&builder, curve::Grumpkin::AffineElement::infinity());
219 this->check_conversion(group_element);
220 }
221}
222
223TYPED_TEST(stdlib_field_conversion, DeserializePointAtInfinity)
224{
225 using Builder = TypeParam;
226 using Codec = StdlibCodec<field_t<Builder>>;
229
230 {
231 std::vector<fr<Builder>> zeros(4, zero);
232
233 bn254_element<Builder> point_at_infinity =
234 Codec::template deserialize_from_fields<bn254_element<Builder>>(zeros);
235
236 EXPECT_TRUE(point_at_infinity.is_point_at_infinity().get_value());
237 EXPECT_TRUE(CircuitChecker::check(builder));
238 }
239 {
240 std::vector<fr<Builder>> zeros(2, zero);
241
242 grumpkin_element<Builder> point_at_infinity =
243 Codec::template deserialize_from_fields<grumpkin_element<Builder>>(zeros);
244
245 EXPECT_TRUE(point_at_infinity.is_point_at_infinity().get_value());
246 EXPECT_TRUE(CircuitChecker::check(builder));
247 }
248}
249
253TYPED_TEST(stdlib_field_conversion, FieldConversionArrayBn254Fr)
254{
255 using Builder = TypeParam;
257
258 // Constructing std::array objects with fr<Builder> values
259 std::array<fr<Builder>, 4> array_of_frs_4{
261 };
262 this->check_conversion_iterable(array_of_frs_4);
263
266 fr<Builder>(&builder, 215215125),
267 fr<Builder>(&builder, 102701750),
268 fr<Builder>(&builder, 367032),
269 fr<Builder>(&builder, 12985028),
271 this->check_conversion_iterable(array_of_frs_7);
272}
273
277TYPED_TEST(stdlib_field_conversion, FieldConversionArrayGrumpkinFr)
278{
279 using Builder = TypeParam;
281
282 // Constructing std::array objects with fq<Builder> values
283 std::array<fq<Builder>, 4> array_of_fqs_4{
285 &builder,
286 static_cast<bb::fq>(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"))),
288 &builder,
289 static_cast<bb::fq>(std::string("2bf1eaf87f7d27e8dc4056e9af975985bccc89077a21891d6c7b6ccce0631f95"))),
291 &builder,
292 static_cast<bb::fq>(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"))),
294 &builder,
295 static_cast<bb::fq>(std::string("018555a8eb50cf07f64b019ebaf3af3c925c93e631f3ecd455db07bbb52bbdd3"))),
296 };
297 this->check_conversion_iterable(array_of_fqs_4);
298}
299
303TYPED_TEST(stdlib_field_conversion, FieldConversionUnivariateBn254Fr)
304{
305 using Builder = TypeParam;
307
308 // Constructing Univariate objects with fr<Builder> values
309 Univariate<fr<Builder>, 4> univariate{
311 };
312 this->check_conversion_iterable(univariate);
313}
314
318TYPED_TEST(stdlib_field_conversion, FieldConversionUnivariateGrumpkinFr)
319{
320 using Builder = TypeParam;
322
323 // Constructing std::array objects with fq<Builder> values
324 Univariate<fq<Builder>, 4> univariate{
326 &builder,
327 static_cast<bb::fq>(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"))),
329 &builder,
330 static_cast<bb::fq>(std::string("2bf1eaf87f7d27e8dc4056e9af975985bccc89077a21891d6c7b6ccce0631f95"))),
332 &builder,
333 static_cast<bb::fq>(std::string("018555a8eb50cf07f64b019ebaf3af3c925c93e631f3ecd455db07bbb52bbdd3"))),
335 &builder,
336 static_cast<bb::fq>(std::string("2bf1eaf87f7d27e8dc4056e9af975985bccc89077a21891d6c7b6ccce0631f95"))) }
337 };
338 this->check_conversion_iterable(univariate);
339}
340
341// ============================================================================
342// Gate Count Tests for Deserialization Operations
343// ============================================================================
344
349TYPED_TEST(stdlib_field_conversion, GateCountScalarDeserialization)
350{
351 // Scalar deserialization adds no gates (just witness creation)
352 this->template check_deserialization_gate_count<fr<TypeParam>>([] { return bb::fr::random_element(); }, 0);
353}
354
358TYPED_TEST(stdlib_field_conversion, GateCountBigfieldDeserialization)
359{
360 // Deserializing a single bigfield element is expensive due to creating new ranges for range constraints
361 this->template check_deserialization_gate_count<fq<TypeParam>>([] { return bb::fq::random_element(); }, 3483);
362}
363
368TYPED_TEST(stdlib_field_conversion, GateCountMultipleBigfieldDeserialization)
369{
370 this->template check_deserialization_gate_count<fq<TypeParam>>([] { return bb::fq::random_element(); }, 3608, 10);
371}
372
377TYPED_TEST(stdlib_field_conversion, GateCountBN254PointDeserialization)
378{
379 using Builder = TypeParam;
380 // Ultra: full bigfield construction + on-curve validation
381 // Mega: only is_infinity check, range constraint and on_curve validation deferred to ECCVM and Translator
382 constexpr uint32_t expected = std::is_same_v<Builder, bb::UltraCircuitBuilder> ? 3789 : 5;
383 this->template check_deserialization_gate_count<bn254_element<Builder>>(
384 [] { return curve::BN254::AffineElement::random_element(); }, expected);
385}
386
390TYPED_TEST(stdlib_field_conversion, GateCountMultipleBN254PointDeserialization)
391{
392 using Builder = TypeParam;
393
394 constexpr uint32_t expected = std::is_same_v<Builder, bb::UltraCircuitBuilder> ? 4986 : 50;
395 this->template check_deserialization_gate_count<bn254_element<Builder>>(
396 [] { return curve::BN254::AffineElement::random_element(); }, expected, 10);
397}
398
403TYPED_TEST(stdlib_field_conversion, GateCountGrumpkinPointDeserialization)
404{
405 this->template check_deserialization_gate_count<grumpkin_element<TypeParam>>(
406 [] { return curve::Grumpkin::AffineElement::random_element(); }, 10);
407}
408
413TYPED_TEST(stdlib_field_conversion, GateCountArrayDeserialization)
414{
415 constexpr size_t SIZE = 8;
416 this->template check_deserialization_gate_count<std::array<fr<TypeParam>, SIZE>>(
417 [] {
419 for (size_t i = 0; i < SIZE; ++i) {
420 arr[i] = bb::fr::random_element();
421 }
422 return arr;
423 },
424 0);
425}
426
431TYPED_TEST(stdlib_field_conversion, GateCountUnivariateDeserialization)
432{
433 constexpr size_t LENGTH = 8;
434 this->template check_deserialization_gate_count<Univariate<fr<TypeParam>, LENGTH>>(
435 [] {
437 for (size_t i = 0; i < LENGTH; ++i) {
438 evals[i] = bb::fr::random_element();
439 }
440 return Univariate<bb::fr, LENGTH>(evals);
441 },
442 0);
443}
444
449TYPED_TEST(stdlib_field_conversion, BigfieldDeserializationFails)
450{
451 // Need to bypass an out-of-circuit range check
453 using Builder = TypeParam;
454 using Codec = StdlibCodec<field_t<Builder>>;
455
457
458 bb::fr low_limb = bb::fr(0);
459 // Create a limb from the value 2^136, that does not satisfy the condition < 2^136.
460 bb::fr high_limb = bb::fr(uint256_t(1) << (2 * fq<Builder>::NUM_LIMB_BITS));
461 info(high_limb);
462
465
466 // Deserialize as bigfield - this creates the bigfield from the two limbs
467 [[maybe_unused]] auto bigfield_val = Codec::template deserialize_from_fields<fq<Builder>>(circuit_fields);
468
469 // Circuit should fail validation
470 EXPECT_FALSE(CircuitChecker::check(builder));
471}
472
473} // namespace bb::stdlib::field_conversion_tests
#define EXPECT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:174
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:32
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
A univariate polynomial represented by its values on {0, 1,..., domain_end - 1}.
typename bb::g1 Group
Definition bn254.hpp:20
typename Group::affine_element AffineElement
Definition bn254.hpp:22
typename Group::affine_element AffineElement
Definition grumpkin.hpp:63
static std::vector< fr > serialize_to_fields(const T &val)
Core stdlib Transcript serialization method.
bool get_value() const
Definition bool.hpp:124
cycle_group represents a group Element of the proving system's embedded curve, i.e....
bool_t is_point_at_infinity() const
void check_conversion(T in, bool valid_circuit=true, bool point_at_infinity=false)
void check_deserialization_gate_count(CreateFn create_native, uint32_t expected_gates, size_t num_elements=1)
Helper to test gate counts for deserialization.
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:454
void info(Args... args)
Definition log.hpp:75
AluTraceBuilder builder
Definition alu.test.cpp:124
testing::Types< UltraCircuitBuilder, MegaCircuitBuilder > BuilderTypes
element< Builder, fq< Builder >, fr< Builder >, curve::BN254::Group > bn254_element
void check_circuit_and_gate_count(Builder &builder, uint32_t expected_gates_without_base)
Utility function for gate count checking and circuit verification.
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
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
field< Bn254FrParams > fr
Definition fr.hpp:174
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
uint8_t len
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr uint256_t modulus_minus_two