Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
affine_element_impl.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#pragma once
8#include "./element.hpp"
11
12namespace bb::group_elements {
13template <class Fq, class Fr, class T>
14constexpr affine_element<Fq, Fr, T>::affine_element(const Fq& x, const Fq& y) noexcept
15 : x(x)
16 , y(y)
17{}
18
19template <class Fq, class Fr, class T>
20template <typename BaseField, typename CompileTimeEnabled>
22{
23 uint256_t x_coordinate = compressed;
24 x_coordinate.data[3] = x_coordinate.data[3] & (~0x8000000000000000ULL);
25 bool y_bit = compressed.get_bit(255);
26
27 Fq x = Fq(x_coordinate);
28 Fq y2 = (x.sqr() * x + T::b);
29 if constexpr (T::has_a) {
30 y2 += (x * T::a);
31 }
32 auto [is_quadratic_remainder, y] = y2.sqrt();
33 if (!is_quadratic_remainder) {
34 return affine_element(Fq::zero(), Fq::zero());
35 }
36 if (uint256_t(y).get_bit(0) != y_bit) {
37 y = -y;
38 }
39
40 return affine_element<Fq, Fr, T>(x, y);
41}
42
43template <class Fq, class Fr, class T>
44template <typename BaseField, typename CompileTimeEnabled>
46 const uint256_t& compressed) noexcept
47{
48 auto get_y_coordinate = [](const uint256_t& x_coordinate) {
49 Fq x = Fq(x_coordinate);
50 Fq y2 = (x.sqr() * x + T::b);
51 if constexpr (T::has_a) {
52 y2 += (x * T::a);
53 }
54 return y2.sqrt();
55 };
56
57 uint256_t x_1 = compressed;
58 uint256_t x_2 = compressed + Fr::modulus;
59 auto [is_quadratic_remainder_1, y_1] = get_y_coordinate(x_1);
60 auto [is_quadratic_remainder_2, y_2] = get_y_coordinate(x_2);
61
62 auto output_1 = is_quadratic_remainder_1 ? affine_element<Fq, Fr, T>(Fq(x_1), y_1)
64 auto output_2 = is_quadratic_remainder_2 ? affine_element<Fq, Fr, T>(Fq(x_2), y_2)
66
67 return { output_1, output_2 };
68}
69
70template <class Fq, class Fr, class T>
76
77template <class Fq, class Fr, class T>
78constexpr affine_element<Fq, Fr, T> affine_element<Fq, Fr, T>::operator*(const Fr& exponent) const noexcept
79{
80 return bb::group_elements::element(*this) * exponent;
81}
82
83template <class Fq, class Fr, class T>
84template <typename BaseField, typename CompileTimeEnabled>
85
87{
88 uint256_t out(x);
89 if (uint256_t(y).get_bit(0)) {
90 out.data[3] = out.data[3] | 0x8000000000000000ULL;
91 }
92 return out;
93}
94
95template <class Fq, class Fr, class T> affine_element<Fq, Fr, T> affine_element<Fq, Fr, T>::infinity()
96{
99 return e;
100}
101
102template <class Fq, class Fr, class T>
104{
105 affine_element result(*this);
106 result.self_set_infinity();
107 return result;
108}
109
110template <class Fq, class Fr, class T> constexpr void affine_element<Fq, Fr, T>::self_set_infinity() noexcept
111{
112 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
113 // We set the value of x equal to modulus to represent inifinty
114 x.data[0] = Fq::modulus.data[0];
115 x.data[1] = Fq::modulus.data[1];
116 x.data[2] = Fq::modulus.data[2];
117 x.data[3] = Fq::modulus.data[3];
118 } else {
119 (*this).x = Fq::zero();
120 (*this).y = Fq::zero();
121 x.self_set_msb();
122 }
123}
124
125template <class Fq, class Fr, class T> constexpr bool affine_element<Fq, Fr, T>::is_point_at_infinity() const noexcept
126{
127 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
128 // We check if the value of x is equal to modulus to represent inifinty
129 return ((x.data[0] ^ Fq::modulus.data[0]) | (x.data[1] ^ Fq::modulus.data[1]) |
130 (x.data[2] ^ Fq::modulus.data[2]) | (x.data[3] ^ Fq::modulus.data[3])) == 0;
131
132 } else {
133 return (x.is_msb_set());
134 }
135}
136
137template <class Fq, class Fr, class T> constexpr bool affine_element<Fq, Fr, T>::on_curve() const noexcept
138{
139 if (is_point_at_infinity()) {
140 return true;
141 }
142 Fq xxx = x.sqr() * x + T::b;
143 Fq yy = y.sqr();
144 if constexpr (T::has_a) {
145 xxx += (x * T::a);
146 }
147 return (xxx == yy);
148}
149
150template <class Fq, class Fr, class T>
151constexpr bool affine_element<Fq, Fr, T>::operator==(const affine_element& other) const noexcept
152{
153 bool this_is_infinity = is_point_at_infinity();
154 bool other_is_infinity = other.is_point_at_infinity();
155 bool both_infinity = this_is_infinity && other_is_infinity;
156 bool only_one_is_infinity = this_is_infinity != other_is_infinity;
157 return !only_one_is_infinity && (both_infinity || ((x == other.x) && (y == other.y)));
158}
159
165template <class Fq, class Fr, class T>
166constexpr bool affine_element<Fq, Fr, T>::operator>(const affine_element& other) const noexcept
167{
168 // We are setting point at infinity to always be the lowest element
169 if (is_point_at_infinity()) {
170 return false;
171 }
172 if (other.is_point_at_infinity()) {
173 return true;
174 }
175
176 if (x > other.x) {
177 return true;
178 }
179 if (x == other.x && y > other.y) {
180 return true;
181 }
182 return false;
183}
184
185template <class Fq, class Fr, class T>
187 const Fq& x, bool sign_bit) noexcept
188{
189 auto yy = x.sqr() * x + T::b;
190 if constexpr (T::has_a) {
191 yy += (x * T::a);
192 }
193 auto [found_root, y] = yy.sqrt();
194
195 if (found_root) {
196 if (uint256_t(y).get_bit(0) != sign_bit) {
197 y = -y;
198 }
199 return affine_element(x, y);
200 }
201 return std::nullopt;
202}
203
236template <class Fq, class Fr, class T>
238 uint8_t attempt_count) noexcept
240{
241 std::vector<uint8_t> target_seed(seed);
242 // expand by 2 bytes to cover incremental hash attempts
243 const size_t seed_size = seed.size();
244 for (size_t i = 0; i < 2; ++i) {
245 target_seed.push_back(0);
246 }
247 target_seed[seed_size] = attempt_count;
248 target_seed[seed_size + 1] = 0;
249 const auto hash_hi = blake3::blake3s_constexpr(&target_seed[0], target_seed.size());
250 target_seed[seed_size + 1] = 1;
251 const auto hash_lo = blake3::blake3s_constexpr(&target_seed[0], target_seed.size());
252 // custom serialize methods as common/serialize.hpp is not constexpr!
253 const auto read_uint256 = [](const uint8_t* in) {
254 const auto read_limb = [](const uint8_t* in, uint64_t& out) {
255 for (size_t i = 0; i < 8; ++i) {
256 out += static_cast<uint64_t>(in[i]) << ((7 - i) * 8);
257 }
258 };
259 uint256_t out = 0;
260 read_limb(&in[0], out.data[3]);
261 read_limb(&in[8], out.data[2]);
262 read_limb(&in[16], out.data[1]);
263 read_limb(&in[24], out.data[0]);
264 return out;
265 };
266 // interpret 64 byte hash output as a uint512_t, reduce to Fq element
267 //(512 bits of entropy ensures result is not biased as 512 >> Fq::modulus.get_msb())
268 Fq x(uint512_t(read_uint256(&hash_lo[0]), read_uint256(&hash_hi[0])));
269 bool sign_bit = hash_hi[0] > 127;
270 std::optional<affine_element> result = derive_from_x_coordinate(x, sign_bit);
271 if (result.has_value()) {
272 return result.value();
273 }
274 return hash_to_curve(seed, attempt_count + 1);
275}
276
277template <typename Fq, typename Fr, typename T>
279{
280 if (engine == nullptr) {
282 }
283
284 Fq x;
285 Fq y;
286 while (true) {
287 // Sample a random x-coordinate and check if it satisfies curve equation.
289 // Negate the y-coordinate based on a randomly sampled bit.
290 bool sign_bit = (engine->get_random_uint8() & 1) != 0;
291
292 std::optional<affine_element> result = derive_from_x_coordinate(x, sign_bit);
293
294 if (result.has_value()) {
295 return result.value();
296 }
297 }
298 throw_or_abort("affine_element::random_element error");
299 return affine_element<Fq, Fr, T>(x, y);
300}
301
302} // namespace bb::group_elements
static constexpr std::array< affine_element, 2 > from_compressed_unsafe(const uint256_t &compressed) noexcept
Reconstruct a point in affine coordinates from compressed form.
constexpr bool is_point_at_infinity() const noexcept
static constexpr affine_element hash_to_curve(const std::vector< uint8_t > &seed, uint8_t attempt_count=0) noexcept
Hash a seed buffer into a point.
static affine_element random_element(numeric::RNG *engine=nullptr) noexcept
Samples a random point on the curve.
constexpr uint256_t compress() const noexcept
constexpr void self_set_infinity() noexcept
constexpr affine_element operator+(const affine_element &other) const noexcept
static constexpr affine_element from_compressed(const uint256_t &compressed) noexcept
Reconstruct a point in affine coordinates from compressed form.
constexpr bool on_curve() const noexcept
constexpr bool operator==(const affine_element &other) const noexcept
static constexpr std::optional< affine_element > derive_from_x_coordinate(const Fq &x, bool sign_bit) noexcept
constexpr bool operator>(const affine_element &other) const noexcept
constexpr affine_element operator*(const Fr &exponent) const noexcept
constexpr affine_element set_infinity() const noexcept
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:33
constexpr bool get_bit(uint64_t bit_index) const
numeric::RNG & engine
uint256_t read_uint256(const uint8_t *data, size_t buffer_size=32)
uintx< uint256_t > uint512_t
Definition uintx.hpp:307
RNG & get_randomness()
Definition engine.cpp:203
constexpr std::array< uint8_t, BLAKE3_OUT_LEN > blake3s_constexpr(const uint8_t *input, size_t input_size)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr uint256_t modulus
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr field zero()
void throw_or_abort(std::string const &err)
bb::fq Fq