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
76 template <typename BaseField = Fq,
77 typename CompileTimeEnabled = std::enable_if_t<(BaseField::modulus >> 255) == uint256_t(1), void>>
78 static constexpr std::array<affine_element, 2> from_compressed_unsafe(const uint256_t& compressed) noexcept;
79
80 constexpr affine_element& operator=(const affine_element& other) noexcept = default;
81
82 constexpr affine_element& operator=(affine_element&& other) noexcept = default;
83
84 constexpr affine_element operator+(const affine_element& other) const noexcept;
85
86 constexpr affine_element operator*(const Fr& exponent) const noexcept;
87
88 static constexpr affine_element infinity();
89 constexpr affine_element set_infinity() const noexcept;
90 constexpr void self_set_infinity() noexcept;
91
92 [[nodiscard]] constexpr bool is_point_at_infinity() const noexcept;
93
94 [[nodiscard]] constexpr bool on_curve() const noexcept;
95
96 static constexpr std::optional<affine_element> derive_from_x_coordinate(const Fq& x, bool sign_bit) noexcept;
97
103 static affine_element random_element(numeric::RNG* engine = nullptr) noexcept;
104 static constexpr affine_element hash_to_curve(const std::vector<uint8_t>& seed, uint8_t attempt_count = 0) noexcept
105 requires SupportsHashToCurve<Params>;
106
107 constexpr bool operator==(const affine_element& other) const noexcept;
108
109 constexpr affine_element operator-() const noexcept { return { x, -y }; }
110
111 constexpr bool operator>(const affine_element& other) const noexcept;
112 constexpr bool operator<(const affine_element& other) const noexcept { return (other > *this); }
113
123 static void serialize_to_buffer(const affine_element& value, uint8_t* buffer, bool write_x_first = false)
124 {
125 using namespace serialize;
126 if (value.is_point_at_infinity()) {
127 // if we are infinity, just set all buffer bits to 1
128 // we only need this case because the below gets mangled converting from montgomery for infinity points
129 memset(buffer, 255, sizeof(Fq) * 2);
130 } else {
131 // Note: for historic reasons we will need to redo downstream hashes if we want this to always be written in
132 // the same order in our various serialization flows
133 write(buffer, write_x_first ? value.x : value.y);
134 write(buffer, write_x_first ? value.y : value.x);
135 }
136 }
137
150 static affine_element serialize_from_buffer(const uint8_t* buffer, bool write_x_first = false)
151 {
152 using namespace serialize;
153 // Does the buffer consist entirely of set bits? If so, we have a point at infinity
154 // Note that if it isn't, this loop should end early.
155 // We only need this case because the below gets mangled converting to montgomery for infinity points
157 std::all_of(buffer, buffer + sizeof(Fq) * 2, [](uint8_t val) { return val == 255; });
160 }
161 affine_element result;
162 // Note: for historic reasons we will need to redo downstream hashes if we want this to always be read in the
163 // same order in our various serialization flows
164 read(buffer, write_x_first ? result.x : result.y);
165 read(buffer, write_x_first ? result.y : result.x);
166 return result;
167 }
168
174 [[nodiscard]] inline std::vector<uint8_t> to_buffer() const
175 {
176 std::vector<uint8_t> buffer(sizeof(affine_element));
178 return buffer;
179 }
180
181 friend std::ostream& operator<<(std::ostream& os, const affine_element& a)
182 {
183 os << "{ " << a.x << ", " << a.y << " }";
184 return os;
185 }
186
198 std::span<const Fr> scalars,
199 size_t max_num_bits = 0,
200 bool with_edgecases = true,
201 const Fr& masking_scalar = Fr(1)) noexcept;
202
205
206 // Concept to detect if Fq is a field2 type
207 template <typename T>
208 static constexpr bool is_field2_v = requires(T t) {
209 t.c0;
210 t.c1;
211 };
212
213 // Msgpack serialization optimized for single uint256_t or array of uint256_t
215 // For regular fields (uint256_t), use uint256_t directly
216 // For field2 types, use std::array<uint256_t, 2>
218
222 };
223
224 void msgpack_pack(auto& packer) const
225 {
226 MsgpackRawAffineElement raw_element{};
227
228 if (is_point_at_infinity()) {
229 // Set all bits to 1 for infinity representation
230 constexpr uint256_t all_ones = {
231 0xffffffffffffffffUL, 0xffffffffffffffffUL, 0xffffffffffffffffUL, 0xffffffffffffffffUL
232 };
233
234 if constexpr (is_field2_v<Fq>) {
235 raw_element.x = { all_ones, all_ones };
236 raw_element.y = { all_ones, all_ones };
237 } else {
238 raw_element.x = all_ones;
239 raw_element.y = all_ones;
240 }
241 } else {
242 // Note: field assignment operators internally call from_montgomery_form()
243 if constexpr (is_field2_v<Fq>) {
244 raw_element.x = { x.c0, x.c1 };
245 raw_element.y = { y.c0, y.c1 };
246 } else {
247 raw_element.x = x;
248 raw_element.y = y;
249 }
250 }
251 packer.pack(raw_element);
252 }
253
254 void msgpack_unpack(auto o)
255 {
256 MsgpackRawAffineElement raw_element = o;
257
258 // Check if this is point at infinity (all bits set)
259 constexpr uint256_t all_ones = {
260 0xffffffffffffffffUL, 0xffffffffffffffffUL, 0xffffffffffffffffUL, 0xffffffffffffffffUL
261 };
262
263 bool is_infinity;
264 if constexpr (is_field2_v<Fq>) {
265 is_infinity = (raw_element.x[0] == all_ones && raw_element.x[1] == all_ones &&
266 raw_element.y[0] == all_ones && raw_element.y[1] == all_ones);
267 } else {
268 is_infinity = (raw_element.x == all_ones && raw_element.y == all_ones);
269 }
270
271 if (is_infinity) {
273 } else {
274 // Note: field assignment operators internally call to_montgomery_form()
275 if constexpr (is_field2_v<Fq>) {
276 x.c0 = raw_element.x[0];
277 x.c1 = raw_element.x[1];
278 y.c0 = raw_element.y[0];
279 y.c1 = raw_element.y[1];
280 } else {
281 x = raw_element.x;
282 y = raw_element.y;
283 }
284 }
285 }
286 void msgpack_schema(auto& packer) const
287 {
288 std::string schema_name = msgpack_schema_name(*this);
289 if (packer.set_emitted(schema_name)) {
290 packer.pack(schema_name);
291 return; // already emitted
292 }
293 packer.pack_map(3);
294 packer.pack("__typename");
295 packer.pack(schema_name);
296 packer.pack("x");
297 packer.pack_schema(x);
298 packer.pack("y");
299 packer.pack_schema(y);
300 }
301};
302
303template <typename B, typename Fq_, typename Fr_, typename Params>
305{
306 using namespace serialize;
307 std::array<uint8_t, sizeof(element)> buffer;
308 read(it, buffer);
310 buffer.data(), /* use legacy field order */ true);
311}
312
313template <typename B, typename Fq_, typename Fr_, typename Params>
315{
316 using namespace serialize;
317 std::array<uint8_t, sizeof(element)> buffer;
319 element, buffer.data(), /* use legacy field order */ true);
320 write(it, buffer);
321}
322} // namespace bb::group_elements
323
friend std::ostream & operator<<(std::ostream &os, const affine_element &a)
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 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 batch_mul(std::span< const affine_element > points, std::span< const 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 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 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.
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:33
FF a
numeric::RNG & engine
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:50
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:39
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