Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
g2.test.cpp
Go to the documentation of this file.
1#include "g2.hpp"
2#include <gtest/gtest.h>
3
4using namespace bb;
5
6TEST(g2, DblCheckAgainstConstants)
7{
8 g2::element lhs = { { { 0x46debd5cd992f6ed, 0x674322d4f75edadd, 0x426a00665e5c4479, 0x1800deef121f1e76 },
9 { 0x97e485b7aef312c2, 0xf1aa493335a9e712, 0x7260bfb731fb5d25, 0x198e9393920d483a } },
10 { { 0x4ce6cc0166fa7daa, 0xe3d1e7690c43d37b, 0x4aab71808dcb408f, 0x12c85ea5db8c6deb },
11 { 0x55acdadcd122975b, 0xbc4b313370b38ef3, 0xec9e99ad690c3395, 0x90689d0585ff075 } },
12 { { 0x1, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0 } } };
13 g2::element expected = { { { 0x8fcae74c62173d99, 0xadb8624eb3bce1ad, 0x7b95c05d3e9c3c98, 0x11d65cded12c8731 },
14 { 0x913fa47117bd9d56, 0x17eb5f9e60297b13, 0x132207965bf363ee, 0x168dfeb5f21b6dc0 } },
15 { { 0x1c10da5c8693bc8, 0x152ff094bd258271, 0xeb12d62e95fef138, 0x2891f38f6935fd84 },
16 { 0x9f5265a7b4e4ae19, 0xfb6348cb8fdefd6c, 0x6259df5c8932f6b1, 0x53858cc3dba708f } },
17 { { 0x99cd9802cdf4fb54, 0xc7a3ced21887a6f6, 0x9556e3011b96811f, 0x2590bd4bb718dbd6 },
18 { 0xab59b5b9a2452eb6, 0x78966266e1671de6, 0xd93d335ad218672b, 0x120d13a0b0bfe0eb } } };
19
20 lhs.x = lhs.x.to_montgomery_form();
21 lhs.y = lhs.y.to_montgomery_form();
22 lhs.z = lhs.z.to_montgomery_form();
23 expected.x = expected.x.to_montgomery_form();
24 expected.y = expected.y.to_montgomery_form();
25 expected.z = expected.z.to_montgomery_form();
26
27 g2::element result;
28 result = lhs.dbl();
29 EXPECT_EQ(result == expected, true);
30}
31
32TEST(g2, MixedAddCheckAgainstConstants)
33{
34 g2::element lhs = { { { 0xfe0ee11d88ef9c7c, 0xa50b3642c93787df, 0x5c4925f0812249a3, 0x13360054113b26e5 },
35 { 0x85a786ba7563664d, 0xebb6adaab3da2d35, 0x2e5c4b3e8bfae51d, 0x860451c5f3cb08 } },
36 { { 0x1336c5c955c13e31, 0x99acf7e0bf631edd, 0x7544255d031dcb7c, 0x170f93b2ac0d088d },
37 { 0xd27a61c30f2f9b75, 0x27abf783f3139bb9, 0x84ee0a9379a3c860, 0x23df8ba46e8f6ea7 } },
38 { { 0x3b2009df97845379, 0x3262a4c15a3ad056, 0xc5852fece05e2563, 0x1bb45a345c7765a9 },
39 { 0xaeb423ce4f95d63, 0xa9dee5d2983c1985, 0x8120e98ba5901fdb, 0x181589d4f3580f3a } } };
40 g2::affine_element affine_rhs = {
41 { { 0x46debd5cd992f6ed, 0x674322d4f75edadd, 0x426a00665e5c4479, 0x1800deef121f1e76 },
42 { 0x97e485b7aef312c2, 0xf1aa493335a9e712, 0x7260bfb731fb5d25, 0x198e9393920d483a } },
43 { { 0x4ce6cc0166fa7daa, 0xe3d1e7690c43d37b, 0x4aab71808dcb408f, 0x12c85ea5db8c6deb },
44 { 0x55acdadcd122975b, 0xbc4b313370b38ef3, 0xec9e99ad690c3395, 0x90689d0585ff075 } }
45 };
46 g2::element expected = { { { 0x98399c68dd927f5, 0x585e18855b30df06, 0x9874333b9a1bab34, 0x2bb4f72523c319bf },
47 { 0x29e78f88e1516115, 0x9240c8e9ab1546d5, 0x8d350dc8b1c3b2b8, 0x17688e3c6ab5e4d2 } },
48 { { 0x1e57dc45f291a09e, 0xe54bbdd2e4e99866, 0x653c8c883714add1, 0xe71bea84e3257e6 },
49 { 0x75c1f2d7c18946a6, 0x315f562c7349c2e8, 0x686aea0f0df36a52, 0x9bfa6ed372f6a0e } },
50 { { 0xf5b3de9258529bb0, 0x532ab749f5abddd7, 0x448d9ba9d7eee9c0, 0x3053d1c7326c11a8 },
51 { 0x18457bf2457b178d, 0x8d9a26e09db091c1, 0xce0fce46e53efa63, 0x2594360eb4eaf8e4 } } };
52
53 lhs.x = lhs.x.to_montgomery_form();
54 lhs.y = lhs.y.to_montgomery_form();
55 lhs.z = lhs.z.to_montgomery_form();
56 affine_rhs.x = affine_rhs.x.to_montgomery_form();
57 affine_rhs.y = affine_rhs.y.to_montgomery_form();
58 expected.x = expected.x.to_montgomery_form();
59 expected.y = expected.y.to_montgomery_form();
60 expected.z = expected.z.to_montgomery_form();
61
62 g2::element result;
63
64 result = lhs + affine_rhs;
65 EXPECT_EQ(result == expected, true);
66}
67
68TEST(g2, AddCheckAgainstConstants)
69{
70 g2::element lhs = { { { 0xfe0ee11d88ef9c7c, 0xa50b3642c93787df, 0x5c4925f0812249a3, 0x13360054113b26e5 },
71 { 0x85a786ba7563664d, 0xebb6adaab3da2d35, 0x2e5c4b3e8bfae51d, 0x860451c5f3cb08 } },
72 { { 0x1336c5c955c13e31, 0x99acf7e0bf631edd, 0x7544255d031dcb7c, 0x170f93b2ac0d088d },
73 { 0xd27a61c30f2f9b75, 0x27abf783f3139bb9, 0x84ee0a9379a3c860, 0x23df8ba46e8f6ea7 } },
74 { { 0x3b2009df97845379, 0x3262a4c15a3ad056, 0xc5852fece05e2563, 0x1bb45a345c7765a9 },
75 { 0xaeb423ce4f95d63, 0xa9dee5d2983c1985, 0x8120e98ba5901fdb, 0x181589d4f3580f3a } } };
76 g2::element rhs = { { { 0x46debd5cd992f6ed, 0x674322d4f75edadd, 0x426a00665e5c4479, 0x1800deef121f1e76 },
77 { 0x97e485b7aef312c2, 0xf1aa493335a9e712, 0x7260bfb731fb5d25, 0x198e9393920d483a } },
78 { { 0x4ce6cc0166fa7daa, 0xe3d1e7690c43d37b, 0x4aab71808dcb408f, 0x12c85ea5db8c6deb },
79 { 0x55acdadcd122975b, 0xbc4b313370b38ef3, 0xec9e99ad690c3395, 0x90689d0585ff075 } },
80 { { 0x1, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0 } } };
81 g2::element expected = { { { 0x98399c68dd927f5, 0x585e18855b30df06, 0x9874333b9a1bab34, 0x2bb4f72523c319bf },
82 { 0x29e78f88e1516115, 0x9240c8e9ab1546d5, 0x8d350dc8b1c3b2b8, 0x17688e3c6ab5e4d2 } },
83 { { 0x1e57dc45f291a09e, 0xe54bbdd2e4e99866, 0x653c8c883714add1, 0xe71bea84e3257e6 },
84 { 0x75c1f2d7c18946a6, 0x315f562c7349c2e8, 0x686aea0f0df36a52, 0x9bfa6ed372f6a0e } },
85 { { 0xf5b3de9258529bb0, 0x532ab749f5abddd7, 0x448d9ba9d7eee9c0, 0x3053d1c7326c11a8 },
86 { 0x18457bf2457b178d, 0x8d9a26e09db091c1, 0xce0fce46e53efa63, 0x2594360eb4eaf8e4 } } };
87
88 lhs.x = lhs.x.to_montgomery_form();
89 lhs.y = lhs.y.to_montgomery_form();
90 lhs.z = lhs.z.to_montgomery_form();
91 rhs.x = rhs.x.to_montgomery_form();
92 rhs.y = rhs.y.to_montgomery_form();
93 rhs.z = rhs.z.to_montgomery_form();
94
95 expected.x = expected.x.to_montgomery_form();
96 expected.y = expected.y.to_montgomery_form();
97 expected.z = expected.z.to_montgomery_form();
98
99 g2::element result;
100 result = lhs + rhs;
101 EXPECT_EQ(result == expected, true);
102}
103
104TEST(g2, GroupExponentiationCheckAgainstConstants)
105{
106 fr scalar = { 0xc4199e4b971f705, 0xc8d89c916a23ab3d, 0x7ea3cd7c05c7af82, 0x2fdafbf994a8d400 };
107 g2::affine_element lhs = { { { 0x46debd5cd992f6ed, 0x674322d4f75edadd, 0x426a00665e5c4479, 0x1800deef121f1e76 },
108 { 0x97e485b7aef312c2, 0xf1aa493335a9e712, 0x7260bfb731fb5d25, 0x198e9393920d483a } },
109 { { 0x4ce6cc0166fa7daa, 0xe3d1e7690c43d37b, 0x4aab71808dcb408f, 0x12c85ea5db8c6deb },
110 { 0x55acdadcd122975b, 0xbc4b313370b38ef3, 0xec9e99ad690c3395, 0x90689d0585ff075 } } };
111 g2::affine_element expected = {
112 { { 0x3363a6e8193817c0, 0x5edb295efcf8a8f0, 0xe33df179b9821b84, 0xaa0f7e7c00600d3 },
113 { 0x91b09f192f2b3eb2, 0x3a27767998031cd5, 0xa44abe0ef5ba1c0f, 0x10bbc579ca6f412f } },
114 { { 0xa8850d9c027ba4db, 0xae6147163c4068a6, 0x5f73bedc2cd52fab, 0x159dfbb82478b51b },
115 { 0x33cccf11dd7d7fb2, 0xcbb3c7c098cbb079, 0x2e83153ab90a931d, 0x26d19735b36c2d08 } }
116 };
117
119 lhs.x = lhs.x.to_montgomery_form();
120 lhs.y = lhs.y.to_montgomery_form();
121 expected.x = expected.x.to_montgomery_form();
122 expected.y = expected.y.to_montgomery_form();
123
124 g2::affine_element result(g2::element(lhs) * scalar);
125
126 EXPECT_EQ(result == expected, true);
127}
128
129TEST(g2, Serialize)
130{
131 // test serializing random points
132 size_t num_repetitions(1);
133 for (size_t i = 0; i < num_repetitions; i++) {
134 g2::affine_element expected = g2::element::random_element();
135
136 std::array<uint8_t, sizeof(g2::affine_element)> buffer;
137
138 g2::affine_element::serialize_to_buffer(expected, &buffer[0]);
139
140 g2::affine_element result = g2::affine_element::serialize_from_buffer(&buffer[0]);
141
142 EXPECT_EQ(result == expected, true);
143 }
144
145 // test serializing the point at infinity
146 {
147 g2::affine_element expected = g2::element::random_element();
148 expected.self_set_infinity();
149 std::array<uint8_t, sizeof(g2::affine_element)> buffer;
150
151 g2::affine_element::serialize_to_buffer(expected, &buffer[0]);
152
153 g2::affine_element result = g2::affine_element::serialize_from_buffer(&buffer[0]);
154
155 ASSERT_TRUE(result.is_point_at_infinity());
156 EXPECT_EQ(result == expected, true);
157 }
158}
159
160template <class T> void write(const T t)
161{
162 FILE* fp = fopen("/dev/null", "wb");
163 static_cast<void>(fwrite(&t, sizeof(t), 1, fp));
164 static_cast<void>(fclose(fp));
165}
166
167#if !defined(__wasm__)
168TEST(g2, InitializationCheck)
169{
170 // NOLINTNEXTLINE not our fault googletest uses `goto`!
171 EXPECT_NO_THROW(write<g2::affine_element>({}));
172}
173#endif
174
175TEST(g2, GeneratorIsCorrect)
176{
177 // Values taken from https://eips.ethereum.org/EIPS/eip-197
179 g2::affine_element expected{ fq2{ fq("0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed"),
180 fq("0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2") },
181 fq2{ fq("0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa"),
182 fq("0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b") } };
183 EXPECT_EQ(generator, expected);
184}
185
186// The generator, infinity, and arbitrary scalar multiples of the generator must be accepted as
187// members of the BN254 G2 prime-order subgroup.
188TEST(g2, IsInPrimeSubgroupAcceptsSubgroupPoints)
189{
191 EXPECT_TRUE(gen.is_in_prime_subgroup());
192 EXPECT_TRUE(g2::affine_element::infinity().is_in_prime_subgroup());
193
194 for (size_t i = 0; i < 4; ++i) {
196 EXPECT_TRUE(P.is_in_prime_subgroup());
197 }
198}
199
200// BN254 G2 has cofactor h2 ≈ 2^254, so on-curve does NOT imply prime-order subgroup membership. The hardcoded point
201// below was constructed by sampling x = i + u (for the smallest positive integer i that yields a curve point) and
202// recovering y via Fq2 sqrt; because only a 1/h2 fraction of E'(Fq2) lies in G_r, this specimen lies in a cofactor
203// subgroup. Such a point must be rejected. Coordinates are in Montgomery form to match `Bn254G2Params::one_x` etc.
204TEST(g2, IsInPrimeSubgroupRejectsCofactorPoint)
205{
206 const g2::affine_element off_subgroup{
207 fq2{ fq(2), fq(1) },
208 fq2{ fq("0x101f7278419308b95099eca02dcee0c5381f4d26d1d62313f057167f064101ce"),
209 fq("0x2b76c179599bb92a963dac85546a005a777f7c13f6a7b75d5918b6b5808f5fde") }
210 };
211 ASSERT_TRUE(off_subgroup.on_curve());
212 EXPECT_FALSE(off_subgroup.is_in_prime_subgroup());
213
214 // Sanity check that scalar multiplication via the Fr-typed `*` operator does NOT detect
215 // subgroup membership — multiplying by `Fr(0)` (the additive identity, which equals `r mod r`)
216 // gives infinity for every input, including off-subgroup points. This is precisely why
217 // is_in_prime_subgroup() routes through a uint256_t scalar instead.
218 EXPECT_TRUE((off_subgroup * fr::zero()).is_point_at_infinity());
219}
220
221// Off-curve coordinates must be rejected: the Weierstrass group law is unsound off-curve, so the
222// [r]·P trick can return a false positive on attacker-supplied (x, y) that happens to satisfy
223// y² = x³ + b' for some b' ≠ b with a prime-r factor in its order.
224TEST(g2, IsInPrimeSubgroupRejectsOffCurvePoint)
225{
227 off_curve.y += fq2::one();
228 ASSERT_FALSE(off_curve.on_curve());
229 EXPECT_FALSE(off_curve.is_in_prime_subgroup());
230}
bool is_in_prime_subgroup() const noexcept
Check that the point lies in the prime-order subgroup of size Fr::modulus.
constexpr bool is_point_at_infinity() const noexcept
constexpr void self_set_infinity() noexcept
constexpr bool on_curve() const noexcept
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:35
constexpr element dbl() const noexcept
group class. Represents an elliptic curve group element. Group is parametrised by Fq and Fr
Definition group.hpp:38
group_elements::affine_element< Fq, Fr, Params > affine_element
Definition group.hpp:44
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:60
void write(const T t)
Definition g2.test.cpp:160
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FqParams > fq
Definition fq.hpp:153
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
static constexpr fq2 one_y
Definition g2.hpp:29
static constexpr fq2 one_x
Definition g2.hpp:25
BB_INLINE constexpr field to_montgomery_form() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr void self_to_montgomery_form() &noexcept
static constexpr field zero()