Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
field_params_constants.test.cpp
Go to the documentation of this file.
1
27#include <array>
28#include <gtest/gtest.h>
29#include <string>
30
31using namespace bb;
32
33namespace {
34
35// Helper to convert a decimal string to uint256_t.
36uint256_t from_decimal(const std::string& dec_str)
37{
38 uint256_t result = 0;
39 for (char c : dec_str) {
40 result = result * 10 + static_cast<uint64_t>(c - '0');
41 }
42 return result;
43}
44
45// ============================================================
46// Config structs — one per field, providing expected constants
47// and feature flags that control which tests are exercised.
48// ============================================================
49
50struct Bn254FqTestConfig {
51 using Params = Bn254FqParams;
52 using Field = bb::fq;
53 // BN254 base field prime q
54 // References: https://eips.ethereum.org/EIPS/eip-196, https://hackmd.io/@jpw/bn254
55 static constexpr const char* expected_modulus_decimal =
56 "21888242871839275222246405745257275088696311157297823662689037894645226208583";
57 static constexpr bool has_cube_root = true;
58 static constexpr bool has_primitive_root = false;
59};
60
61struct Bn254FrTestConfig {
62 using Params = Bn254FrParams;
63 using Field = bb::fr;
64 // BN254 scalar field prime r (also Baby Jubjub base field)
65 // References: https://eips.ethereum.org/EIPS/eip-196, https://hackmd.io/@jpw/bn254
66 static constexpr const char* expected_modulus_decimal =
67 "21888242871839275222246405745257275088548364400416034343698204186575808495617";
68 static constexpr bool has_cube_root = true;
69 static constexpr bool has_primitive_root = true;
70};
71
72struct Secp256k1FqTestConfig {
74 using Field = secp256k1::fq;
75 // secp256k1 base field prime p = 2^256 - 2^32 - 977
76 // Reference: https://www.secg.org/sec2-v2.pdf
77 static constexpr const char* expected_modulus_decimal =
78 "115792089237316195423570985008687907853269984665640564039457584007908834671663";
79 static constexpr bool has_cube_root = true;
80 static constexpr bool has_primitive_root = false;
81};
82
83struct Secp256k1FrTestConfig {
85 using Field = secp256k1::fr;
86 // secp256k1 scalar field order
87 // Reference: https://www.secg.org/sec2-v2.pdf
88 static constexpr const char* expected_modulus_decimal =
89 "115792089237316195423570985008687907852837564279074904382605163141518161494337";
90 static constexpr bool has_cube_root = true;
91 static constexpr bool has_primitive_root = false;
92};
93
94struct Secp256r1FqTestConfig {
96 using Field = secp256r1::fq;
97 // secp256r1 (P-256) base field prime p = 2^256 - 2^224 + 2^192 + 2^96 - 1
98 // Reference: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
99 static constexpr const char* expected_modulus_decimal =
100 "115792089210356248762697446949407573530086143415290314195533631308867097853951";
101 static constexpr bool has_cube_root = false;
102 static constexpr bool has_primitive_root = false;
103};
104
105struct Secp256r1FrTestConfig {
107 using Field = secp256r1::fr;
108 // secp256r1 (P-256) scalar field order
109 // Reference: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
110 static constexpr const char* expected_modulus_decimal =
111 "115792089210356248762697446949407573529996955224135760342422259061068512044369";
112 static constexpr bool has_cube_root = false;
113 static constexpr bool has_primitive_root = false;
114};
115
116} // namespace
117
118// ============================================================
119// Typed test fixture
120// ============================================================
121
122template <typename Config> class FieldConstantsTest : public testing::Test {};
123
125
126// Verify that the 4 x 64-bit limbs reconstruct to the expected modulus.
128{
129 using Params = typename TypeParam::Params;
130 uint256_t expected = from_decimal(TypeParam::expected_modulus_decimal);
131 uint256_t actual{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
132 EXPECT_EQ(expected, actual);
133}
134
135// Verify R^2 mod p, where R = 2^256, stored in r_squared_{0-3}.
137{
138 using Params = typename TypeParam::Params;
139 uint256_t mod{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
140 uint512_t R = (uint512_t(1) << 256) % mod;
141 uint512_t expected = (R * R) % mod;
142 uint256_t actual{ Params::r_squared_0, Params::r_squared_1, Params::r_squared_2, Params::r_squared_3 };
143 EXPECT_EQ(expected.lo, actual);
144}
145
146// Verify r_inv = -(modulus^{-1}) mod 2^64, used for Montgomery reduction.
148{
149 using Params = typename TypeParam::Params;
150 uint256_t mod{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
151 uint512_t two_64 = uint512_t(1) << 64;
152 uint512_t neg_mod{ -mod, 0 };
153 uint64_t expected = neg_mod.invmod(two_64).lo.data[0];
154 EXPECT_EQ(Params::r_inv, expected);
155}
156
157// Verify r_inv_{0-3} = 2^{-64} mod p, used in the Barrett-Montgomery reduction.
159{
160 using Params = typename TypeParam::Params;
161 uint256_t mod{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
162 uint512_t two_64 = uint512_t(1) << 64;
163 uint256_t expected = two_64.invmod(mod).lo;
164 EXPECT_EQ(expected.data[0], Params::r_inv_0);
165 EXPECT_EQ(expected.data[1], Params::r_inv_1);
166 EXPECT_EQ(expected.data[2], Params::r_inv_2);
167 EXPECT_EQ(expected.data[3], Params::r_inv_3);
168}
169
170// Verify that beta = cube_root_of_unity() satisfies beta^3 = 1 and beta != 1.
172{
173 if constexpr (!TypeParam::has_cube_root) {
174 GTEST_SKIP() << "Cube root of unity is not defined for this field";
175 } else {
176 using Field = typename TypeParam::Field;
177 Field beta = Field::cube_root_of_unity();
178 EXPECT_EQ(beta * beta * beta, Field::one());
179 EXPECT_NE(beta, Field::one());
180 }
181}
182
183// Verify that get_root_of_unity(order) is a primitive 2^order root of unity.
184TYPED_TEST_P(FieldConstantsTest, PrimitiveRootOfUnity)
185{
186 if constexpr (!TypeParam::has_primitive_root) {
187 GTEST_SKIP() << "Primitive root of unity is not used for this field";
188 } else {
189 using Field = typename TypeParam::Field;
190 size_t order = Field::primitive_root_log_size();
191 Field root = Field::get_root_of_unity(order);
192 // root^{2^i} != 1 for all 0 <= i < order
193 for (size_t i = 0; i < order; i++) {
194 EXPECT_NE(root, Field::one());
195 root = root.sqr();
196 }
197 // root^{2^order} = 1
198 EXPECT_EQ(root, Field::one());
199 }
200}
201
202// Verify that coset_generator() is not a quadratic residue mod p, i.e. coset_gen^{(p-1)/2} != 1.
204{
205 using Params = typename TypeParam::Params;
206 using Field = typename TypeParam::Field;
207 uint256_t mod{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
208 Field coset_gen = Field::coset_generator();
209 EXPECT_NE(coset_gen.pow((mod - 1) / 2), Field::one());
210}
211
212// ============================================================
213// WASM consistency tests (9 x 29-bit limb representation)
214// ============================================================
215
216// Verify that the 9 x 29-bit WASM limbs reconstruct to the same modulus as the 4 x 64-bit limbs,
217// and that each limb fits in 29 bits.
218TYPED_TEST_P(FieldConstantsTest, WasmModulusConsistency)
219{
220 using Params = typename TypeParam::Params;
221 uint256_t mod{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
222 constexpr std::array<uint64_t, 9> wasm_limbs = { Params::modulus_wasm_0, Params::modulus_wasm_1,
223 Params::modulus_wasm_2, Params::modulus_wasm_3,
224 Params::modulus_wasm_4, Params::modulus_wasm_5,
225 Params::modulus_wasm_6, Params::modulus_wasm_7,
226 Params::modulus_wasm_8 };
227 uint512_t wasm_modulus = 0;
228 for (size_t i = 0; i < 9; i++) {
229 wasm_modulus += uint512_t(wasm_limbs[i]) << (29UL * i);
230 EXPECT_LT(wasm_limbs[i], uint64_t(1) << 29);
231 }
232 EXPECT_EQ(wasm_modulus.lo, mod);
233 EXPECT_EQ(wasm_modulus.hi, uint256_t(0));
234}
235
236// Verify R_wasm^2 mod p, where R_wasm = 2^261 = 2^(29*9).
238{
239 using Params = typename TypeParam::Params;
240 uint256_t mod{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
241 uint512_t R_wasm = uint512_t(1) << 261;
242 uint512_t R_wasm_mod = R_wasm % mod;
243 uint512_t expected = (R_wasm_mod * R_wasm_mod) % mod;
244 uint256_t actual{
245 Params::r_squared_wasm_0, Params::r_squared_wasm_1, Params::r_squared_wasm_2, Params::r_squared_wasm_3
246 };
247 EXPECT_EQ(expected.lo, actual);
248 EXPECT_EQ(expected.hi, uint256_t(0));
249}
250
251// Verify r_inv_wasm_{0-8} = 2^{-29} mod p, stored as 9 x 29-bit limbs.
252// Also verify each limb fits in 29 bits, and that it is smaller than the modulus
254{
255 using Params = typename TypeParam::Params;
256 uint256_t mod{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
257 constexpr std::array<uint64_t, 9> r_inv_wasm_limbs = {
258 Params::r_inv_wasm_0, Params::r_inv_wasm_1, Params::r_inv_wasm_2, Params::r_inv_wasm_3, Params::r_inv_wasm_4,
259 Params::r_inv_wasm_5, Params::r_inv_wasm_6, Params::r_inv_wasm_7, Params::r_inv_wasm_8
260 };
261 uint512_t r_inv_wasm = 0;
262 for (size_t i = 0; i < 9; i++) {
263 r_inv_wasm += uint512_t(r_inv_wasm_limbs[i]) << (29UL * i);
264 EXPECT_LT(r_inv_wasm_limbs[i], uint64_t(1) << 29);
265 }
266 uint512_t two_29 = uint512_t(1) << 29;
267 uint512_t expected = two_29.invmod(mod);
268 EXPECT_EQ(r_inv_wasm, expected);
269 EXPECT_LT(r_inv_wasm, uint512_t(mod));
270}
271
272// Verify cube_root_wasm is the same field element as cube_root_native but in WASM Montgomery form.
273// Since R_wasm = 2^261 and R_native = 2^256, converting native -> WASM multiplies by 2^5 = 32.
274TYPED_TEST_P(FieldConstantsTest, WasmCubeRootConsistency)
275{
276 if constexpr (!TypeParam::has_cube_root) {
277 GTEST_SKIP() << "Cube root is not used for this field";
278 } else {
279 using Params = typename TypeParam::Params;
280 uint256_t mod{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
281 uint256_t cube_root_native{
282 Params::cube_root_0, Params::cube_root_1, Params::cube_root_2, Params::cube_root_3
283 };
284 uint256_t cube_root_wasm{
285 Params::cube_root_wasm_0, Params::cube_root_wasm_1, Params::cube_root_wasm_2, Params::cube_root_wasm_3
286 };
287 // R_wasm / R_native = 2^261 / 2^256 = 2^5 = 32
288 uint512_t expected = (uint512_t(cube_root_native) * 32) % mod;
289 EXPECT_EQ(expected.lo, cube_root_wasm);
290 }
291}
292
293// Verify primitive_root_wasm is the same field element as primitive_root_native in WASM Montgomery form.
294TYPED_TEST_P(FieldConstantsTest, WasmPrimitiveRootConsistency)
295{
296 if constexpr (!TypeParam::has_primitive_root) {
297 GTEST_SKIP() << "Primitive root is not used for this field";
298 } else {
299 using Params = typename TypeParam::Params;
300 uint256_t mod{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
301 uint256_t primitive_root_native{
302 Params::primitive_root_0, Params::primitive_root_1, Params::primitive_root_2, Params::primitive_root_3
303 };
304 uint256_t primitive_root_wasm{ Params::primitive_root_wasm_0,
305 Params::primitive_root_wasm_1,
306 Params::primitive_root_wasm_2,
307 Params::primitive_root_wasm_3 };
308 // R_wasm / R_native = 2^261 / 2^256 = 2^5 = 32
309 uint512_t expected = (uint512_t(primitive_root_native) * 32) % mod;
310 EXPECT_EQ(expected.lo, primitive_root_wasm);
311 }
312}
313
314// Verify coset_generator_wasm is the same field element as coset_generator_native in WASM Montgomery form.
315TYPED_TEST_P(FieldConstantsTest, CosetGeneratorConsistency)
316{
317 using Params = typename TypeParam::Params;
318 uint256_t mod{ Params::modulus_0, Params::modulus_1, Params::modulus_2, Params::modulus_3 };
319 uint256_t coset_generator_native{
320 Params::coset_generator_0, Params::coset_generator_1, Params::coset_generator_2, Params::coset_generator_3
321 };
322 uint256_t coset_generator_wasm{ Params::coset_generator_wasm_0,
323 Params::coset_generator_wasm_1,
324 Params::coset_generator_wasm_2,
325 Params::coset_generator_wasm_3 };
326 // R_wasm / R_native = 2^261 / 2^256 = 2^5 = 32
327 uint512_t expected = (static_cast<uint512_t>(coset_generator_native) * 32) % mod;
328 EXPECT_EQ(expected, static_cast<uint512_t>(coset_generator_wasm));
329}
330
332 Modulus,
333 RSquared,
334 RInv,
335 PowMinus64,
336 CubeRootOfUnity,
337 PrimitiveRootOfUnity,
338 CosetGenerator,
339 WasmModulusConsistency,
340 WasmRSquared,
341 WasmPowMinus29,
342 WasmCubeRootConsistency,
343 WasmPrimitiveRootConsistency,
344 CosetGeneratorConsistency);
345
346using FieldTestTypes = ::testing::Types<Bn254FqTestConfig,
347 Bn254FrTestConfig,
348 Secp256k1FqTestConfig,
349 Secp256k1FrTestConfig,
350 Secp256r1FqTestConfig,
351 Secp256r1FrTestConfig>;
352
Parameters defining the base field of the BN254 curve.
Definition fq.hpp:28
Parameters defining the scalar field of the BN254 curve.
Definition fr.hpp:29
uintx invmod(const uintx &modulus) const
INSTANTIATE_TYPED_TEST_SUITE_P(AllFields, FieldConstantsTest, FieldTestTypes)
TYPED_TEST_P(FieldConstantsTest, Modulus)
TYPED_TEST_SUITE_P(FieldConstantsTest)
::testing::Types< Bn254FqTestConfig, Bn254FrTestConfig, Secp256k1FqTestConfig, Secp256k1FrTestConfig, Secp256r1FqTestConfig, Secp256r1FrTestConfig > FieldTestTypes
REGISTER_TYPED_TEST_SUITE_P(FieldConstantsTest, Modulus, RSquared, RInv, PowMinus64, CubeRootOfUnity, PrimitiveRootOfUnity, CosetGenerator, WasmModulusConsistency, WasmRSquared, WasmPowMinus29, WasmCubeRootConsistency, WasmPrimitiveRootConsistency, CosetGeneratorConsistency)
uintx< uint256_t > uint512_t
Definition uintx.hpp:306
field< FrParams > fr
field< FqParams > fq
field< FrParams > fr
field< FqParams > fq
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FqParams > fq
Definition fq.hpp:153
field< Bn254FrParams > fr
Definition fr.hpp:155
Parameters defining the base field of the secp256k1 curve.
Definition secp256k1.hpp:26
Parameters defining the scalar field of the secp256k1 curve.
Parameters defining the base field of the secp256r1 curve.
Definition secp256r1.hpp:24
Parameters defining the scalar field of the secp256r1 curve.