Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
affine_element.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Planned, auditors: [], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
8
15#include <cstring>
16#include <type_traits>
17#include <vector>
18
20
21// MSB of the top 64-bit limb in a uint256_t (bit 255). Used in point compression to encode the
22// y-coordinate parity bit, and cleared when recovering the x-coordinate.
23static constexpr uint64_t UINT256_TOP_LIMB_MSB = 0x8000000000000000ULL;
24
25template <typename T>
26concept SupportsHashToCurve = T::can_hash_to_curve;
27template <typename Fq_, typename Fr_, typename Params_> class alignas(64) affine_element {
28 public:
29 using Fq = Fq_;
30 using Fr = Fr_;
31 using Params = Params_;
32
33 using in_buf = const uint8_t*;
34 using vec_in_buf = const uint8_t*;
35 using out_buf = uint8_t*;
36 using vec_out_buf = uint8_t**;
37
41 static constexpr size_t PUBLIC_INPUTS_SIZE = 2 * Fq::PUBLIC_INPUTS_SIZE;
42
45
46 constexpr affine_element(const Fq& x, const Fq& y) noexcept;
47
48 constexpr affine_element(const affine_element& other) noexcept = default;
49
50 constexpr affine_element(affine_element&& other) noexcept = default;
51
52 static constexpr affine_element one() noexcept { return { Params::one_x, Params::one_y }; };
53
63 template <typename BaseField = Fq,
64 typename CompileTimeEnabled = std::enable_if_t<(BaseField::modulus >> 255) == uint256_t(0), void>>
65 static constexpr affine_element from_compressed(const uint256_t& compressed) noexcept;
66
75 template <typename BaseField = Fq,
76 typename CompileTimeEnabled = std::enable_if_t<(BaseField::modulus >> 255) == uint256_t(1), void>>
77 static constexpr std::array<affine_element, 2> from_compressed_unsafe(const uint256_t& compressed) noexcept;
78
79 constexpr affine_element& operator=(const affine_element& other) noexcept = default;
80
81 constexpr affine_element& operator=(affine_element&& other) noexcept = default;
82
83 constexpr affine_element operator+(const affine_element& other) const noexcept;
84
85 constexpr affine_element operator*(const Fr& exponent) const noexcept;
86
87 static constexpr affine_element infinity();
88 constexpr affine_element set_infinity() const noexcept;
89 constexpr void self_set_infinity() noexcept;
90
91 [[nodiscard]] constexpr bool is_point_at_infinity() const noexcept;
92
93 [[nodiscard]] constexpr bool on_curve() const noexcept;
94
109 [[nodiscard]] bool is_in_prime_subgroup() const noexcept;
110
111 static constexpr std::optional<affine_element> derive_from_x_coordinate(const Fq& x, bool sign_bit) noexcept;
112
118 static affine_element random_element(numeric::RNG* engine = nullptr) noexcept;
119 static affine_element hash_to_curve(const std::vector<uint8_t>& seed, uint8_t attempt_count = 0) noexcept
120 requires SupportsHashToCurve<Params>;
121
122 constexpr bool operator==(const affine_element& other) const noexcept;
123
124 constexpr affine_element operator-() const noexcept { return { x, -y }; }
125
126 constexpr bool operator>(const affine_element& other) const noexcept;
127 constexpr bool operator<(const affine_element& other) const noexcept { return (other > *this); }
128
138 static void serialize_to_buffer(const affine_element& value, uint8_t* buffer, bool write_x_first = false)
139 {
140 using namespace serialize;
141 if (value.is_point_at_infinity()) {
142 // if we are infinity, just set all buffer bits to 1
143 // we only need this case because the below gets mangled converting from montgomery for infinity points
144 memset(buffer, 255, sizeof(Fq) * 2);
145 } else {
146 // Note: for historic reasons we will need to redo downstream hashes if we want this to always be written in
147 // the same order in our various serialization flows
148 write(buffer, write_x_first ? value.x : value.y);
149 write(buffer, write_x_first ? value.y : value.x);
150 }
151 }
152
165 static affine_element serialize_from_buffer(const uint8_t* buffer, bool write_x_first = false)
166 {
167 using namespace serialize;
168 // Does the buffer consist entirely of set bits? If so, we have a point at infinity
169 // Note that if it isn't, this loop should end early.
170 // We only need this case because the below gets mangled converting to montgomery for infinity points
172 std::all_of(buffer, buffer + sizeof(Fq) * 2, [](uint8_t val) { return val == 255; });
175 }
176 affine_element result;
177 // Note: for historic reasons we will need to redo downstream hashes if we want this to always be read in the
178 // same order in our various serialization flows
179 read(buffer, write_x_first ? result.x : result.y);
180 read(buffer, write_x_first ? result.y : result.x);
181
182 // Validate the deserialized point lies on the curve
183 if (!result.on_curve()) {
184 throw_or_abort("affine_element::serialize_from_buffer: point is not on the curve");
185 }
186 return result;
187 }
188
194 [[nodiscard]] inline std::vector<uint8_t> to_buffer() const
195 {
196 std::vector<uint8_t> buffer(sizeof(affine_element));
198 return buffer;
199 }
200
201 friend std::ostream& operator<<(std::ostream& os, const affine_element& a)
202 {
203 os << "{ " << a.x << ", " << a.y << " }";
204 return os;
205 }
206
218 std::span<Fr> scalars,
219 size_t max_num_bits = 0,
220 bool with_edgecases = true,
221 const Fr& masking_scalar = Fr(1)) noexcept;
222
225
226 // Concept to detect if Fq is a field2 type
227 template <typename T>
228 static constexpr bool is_field2_v = requires(T t) {
229 t.c0;
230 t.c1;
231 };
232
233 // Msgpack serialization optimized for single uint256_t or array of uint256_t
235 // For regular fields (uint256_t), use uint256_t directly
236 // For field2 types, use std::array<uint256_t, 2>
238
242 };
243
244 void msgpack_pack(auto& packer) const
245 {
246 MsgpackRawAffineElement raw_element{};
247
248 if (is_point_at_infinity()) {
249 // Set all bits to 1 for infinity representation
250 constexpr uint256_t all_ones = {
251 0xffffffffffffffffUL, 0xffffffffffffffffUL, 0xffffffffffffffffUL, 0xffffffffffffffffUL
252 };
253
254 if constexpr (is_field2_v<Fq>) {
255 raw_element.x = { all_ones, all_ones };
256 raw_element.y = { all_ones, all_ones };
257 } else {
258 raw_element.x = all_ones;
259 raw_element.y = all_ones;
260 }
261 } else {
262 // Note: field assignment operators internally call from_montgomery_form()
263 if constexpr (is_field2_v<Fq>) {
264 raw_element.x = { x.c0, x.c1 };
265 raw_element.y = { y.c0, y.c1 };
266 } else {
267 raw_element.x = x;
268 raw_element.y = y;
269 }
270 }
271 packer.pack(raw_element);
272 }
273
274 void msgpack_unpack(auto o)
275 {
276 MsgpackRawAffineElement raw_element = o;
277
278 // Check if this is point at infinity (all bits set)
279 constexpr uint256_t all_ones = {
280 0xffffffffffffffffUL, 0xffffffffffffffffUL, 0xffffffffffffffffUL, 0xffffffffffffffffUL
281 };
282
283 bool is_infinity;
284 if constexpr (is_field2_v<Fq>) {
285 is_infinity = (raw_element.x[0] == all_ones && raw_element.x[1] == all_ones &&
286 raw_element.y[0] == all_ones && raw_element.y[1] == all_ones);
287 } else {
288 is_infinity = (raw_element.x == all_ones && raw_element.y == all_ones);
289 }
290
291 if (is_infinity) {
293 } else {
294 // Note: field assignment operators internally call to_montgomery_form()
295 if constexpr (is_field2_v<Fq>) {
296 x.c0 = raw_element.x[0];
297 x.c1 = raw_element.x[1];
298 y.c0 = raw_element.y[0];
299 y.c1 = raw_element.y[1];
300 } else {
301 x = raw_element.x;
302 y = raw_element.y;
303 }
304 }
305 }
306 void msgpack_schema(auto& packer) const
307 {
308 std::string schema_name = msgpack_schema_name(*this);
309 if (packer.set_emitted(schema_name)) {
310 packer.pack(schema_name);
311 return; // already emitted
312 }
313 packer.pack_map(3);
314 packer.pack("__typename");
315 packer.pack(schema_name);
316 packer.pack("x");
317 packer.pack_schema(x);
318 packer.pack("y");
319 packer.pack_schema(y);
320 }
321};
322
323template <typename B, typename Fq_, typename Fr_, typename Params>
325{
326 using namespace serialize;
327 std::array<uint8_t, sizeof(element)> buffer;
328 read(it, buffer);
330 buffer.data(), /* use legacy field order */ true);
331}
332
333template <typename B, typename Fq_, typename Fr_, typename Params>
335{
336 using namespace serialize;
337 std::array<uint8_t, sizeof(element)> buffer;
339 element, buffer.data(), /* use legacy field order */ true);
340 write(it, buffer);
341}
342} // namespace bb::group_elements
343
friend std::ostream & operator<<(std::ostream &os, const affine_element &a)
bool is_in_prime_subgroup() const noexcept
Check that the point lies in the prime-order subgroup of size Fr::modulus.
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
constexpr affine_element & operator=(const affine_element &other) noexcept=default
static affine_element random_element(numeric::RNG *engine=nullptr) noexcept
Samples a random point on the curve.
std::vector< uint8_t > to_buffer() const
Serialize the point to a byte vector.
void msgpack_pack(auto &packer) const
static affine_element batch_mul(std::span< const affine_element > points, std::span< Fr > scalars, size_t max_num_bits=0, bool with_edgecases=true, const Fr &masking_scalar=Fr(1)) noexcept
Multi-scalar multiplication: compute sum_i(scalars[i] * points[i])
static affine_element serialize_from_buffer(const uint8_t *buffer, bool write_x_first=false)
Restore point from a buffer.
constexpr void self_set_infinity() noexcept
constexpr affine_element & operator=(affine_element &&other) noexcept=default
static constexpr affine_element infinity()
constexpr bool operator<(const affine_element &other) const 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.
static affine_element hash_to_curve(const std::vector< uint8_t > &seed, uint8_t attempt_count=0) noexcept
Hash a seed buffer into a point.
constexpr bool on_curve() const noexcept
static constexpr affine_element one() noexcept
static constexpr size_t PUBLIC_INPUTS_SIZE
static constexpr std::optional< affine_element > derive_from_x_coordinate(const Fq &x, bool sign_bit) noexcept
static void serialize_to_buffer(const affine_element &value, uint8_t *buffer, bool write_x_first=false)
Serialize the point to the given buffer.
constexpr bool operator>(const affine_element &other) const noexcept
constexpr affine_element operator*(const Fr &exponent) const noexcept
void msgpack_schema(auto &packer) const
constexpr affine_element set_infinity() const noexcept
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:35
FF a
numeric::RNG & engine
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:60
void read(B &it, group_elements::affine_element< Fq_, Fr_, Params > &element)
void write(B &it, group_elements::affine_element< Fq_, Fr_, Params > const &element)
AffineElement const size_t Fq *scratch_space noexcept
std::string msgpack_schema_name(g1::affine_element const &)
Definition g1.hpp:38
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr size_t PUBLIC_INPUTS_SIZE
std::conditional_t< is_field2_v< Fq >, std::array< uint256_t, 2 >, uint256_t > FieldType
void throw_or_abort(std::string const &err)