Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
biggroup_goblin.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
9#include "../bigfield/goblin_field.hpp"
10#include "../circuit_builders/circuit_builders_fwd.hpp"
13
15
31template <class Builder_, class Fq, class Fr, class NativeGroup> class goblin_element {
32 public:
33 using Builder = Builder_;
34 using BaseField = Fq;
36 using biggroup_tag = goblin_element; // Facilitates a constexpr check IsBigGroup
37
38 // Number of bb::fr field elements used to represent a goblin element in the public inputs
39 static constexpr size_t PUBLIC_INPUTS_SIZE = BIGGROUP_PUBLIC_INPUTS_SIZE;
40
41 goblin_element() = default;
42 goblin_element(const typename NativeGroup::affine_element& input)
43 : _x(input.x)
44 , _y(input.y)
45 {
46 // ECCVM requires points at infinity to be represented by (0, 0) coordinates
47 if (input.is_point_at_infinity()) {
48 _x = Fq(bb::fq(0));
49 _y = Fq(bb::fq(0));
50 }
51 }
52
53 // Construct a goblin biggroup element from its coordinates.
54 // The on-curve check is skipped as it is performed in ECCVM (assert_on_curve is unused).
55 // Point-at-infinity is represented by (0, 0) coordinates, enforced by ECCVM.
56 goblin_element(const Fq& x, const Fq& y, [[maybe_unused]] bool assert_on_curve = true)
57 : _x(x)
58 , _y(y)
59 {}
60
61 goblin_element(const goblin_element& other) = default;
62 goblin_element(goblin_element&& other) noexcept = default;
63 goblin_element& operator=(const goblin_element& other) = default;
64 goblin_element& operator=(goblin_element&& other) noexcept = default;
65 ~goblin_element() = default;
66
77 const std::string msg = "goblin_element::incomplete_assert_equal") const
78 {
79 _x.assert_equal(other._x, msg + " (x coordinate)");
80 _y.assert_equal(other._y, msg + " (y coordinate)");
81 }
82
83 static goblin_element from_witness(Builder* ctx, const typename NativeGroup::affine_element& input)
84 {
86
87 // ECCVM requires points at infinity to be represented by (0, 0) coordinates
88 if (input.is_point_at_infinity()) {
89 out._x = Fq::from_witness(ctx, bb::fq(0));
90 out._y = Fq::from_witness(ctx, bb::fq(0));
91 } else {
92 out._x = Fq::from_witness(ctx, input.x);
93 out._y = Fq::from_witness(ctx, input.y);
94 }
96 return out;
97 }
98
103 {
104 this->_x.convert_constant_to_fixed_witness(builder);
105 this->_y.convert_constant_to_fixed_witness(builder);
107 }
108
113 {
114 // Origin tags should be updated within
115 this->_x.fix_witness();
116 this->_y.fix_witness();
117
118 // This is now effectively a constant
120 }
121
122 void validate_on_curve() const
123 {
124 // happens in goblin eccvm
125 }
126
128 {
129 uint256_t x = uint256_t(NativeGroup::one.x);
130 uint256_t y = uint256_t(NativeGroup::one.y);
131 Fq x_fq(ctx, x);
132 Fq y_fq(ctx, y);
133 return goblin_element(x_fq, y_fq);
134 }
135
140 static goblin_element constant_infinity([[maybe_unused]] Builder* ctx)
141 {
142 Fq x_fq(ctx, uint256_t(0));
143 Fq y_fq(ctx, uint256_t(0));
144 return goblin_element(x_fq, y_fq);
145 }
146
149
151 {
152 auto builder = get_context(other);
153 return batch_mul({ *this, other }, { Fr(builder, 1), Fr(builder, 1) });
154 }
155
157 {
158 auto builder = get_context(other);
159 // Check that the internal accumulator is zero
160 BB_ASSERT(builder->op_queue->get_accumulator().is_point_at_infinity());
161
162 // Compute the result natively, and validate that result + other == *this
163 typename NativeGroup::affine_element result_value = typename NativeGroup::affine_element(
164 typename NativeGroup::element(get_value()) - typename NativeGroup::element(other.get_value()));
165
166 ecc_op_tuple op_tuple;
167 op_tuple = builder->queue_ecc_add_accum(other.get_value());
168 {
169 auto x_lo = Fr::from_witness_index(builder, op_tuple.x_lo);
170 auto x_hi = Fr::from_witness_index(builder, op_tuple.x_hi);
171 auto y_lo = Fr::from_witness_index(builder, op_tuple.y_lo);
172 auto y_hi = Fr::from_witness_index(builder, op_tuple.y_hi);
173 x_lo.assert_equal(other._x.limbs[0]);
174 x_hi.assert_equal(other._x.limbs[1]);
175 y_lo.assert_equal(other._y.limbs[0]);
176 y_hi.assert_equal(other._y.limbs[1]);
177 }
178 // if function queue_ecc_add_accum is used, op_tuple creates as a result of construct_and_populate_ultra_ops
179 // function. In case of queue_ecc_add_accum, scalar is zero, (z_1, z_2) = (scalar, 0) = (0, 0) and they just put
180 // in the wires.
181 builder->update_used_witnesses({ op_tuple.z_1, op_tuple.z_2 });
182
183 ecc_op_tuple op_tuple2 = builder->queue_ecc_add_accum(result_value);
184 auto x_lo = Fr::from_witness_index(builder, op_tuple2.x_lo);
185 auto x_hi = Fr::from_witness_index(builder, op_tuple2.x_hi);
186 auto y_lo = Fr::from_witness_index(builder, op_tuple2.y_lo);
187 auto y_hi = Fr::from_witness_index(builder, op_tuple2.y_hi);
188
189 // if function queue_ecc_add_accum is used, op_tuple creates as a result of construct_and_populate_ultra_ops
190 // function. In case of queue_ecc_add_accum, scalar is zero, (z_1, z_2) = (scalar, 0) = (0, 0) and they just put
191 // in the wires.
192 builder->update_used_witnesses({ op_tuple2.z_1, op_tuple2.z_2 });
193
194 Fq result_x(x_lo, x_hi);
195 Fq result_y(y_lo, y_hi);
196
197 goblin_element result(result_x, result_y);
198 {
199 ecc_op_tuple op_tuple3 = builder->queue_ecc_eq();
200 auto x_lo = Fr::from_witness_index(builder, op_tuple3.x_lo);
201 auto x_hi = Fr::from_witness_index(builder, op_tuple3.x_hi);
202 auto y_lo = Fr::from_witness_index(builder, op_tuple3.y_lo);
203 auto y_hi = Fr::from_witness_index(builder, op_tuple3.y_hi);
204
205 x_lo.assert_equal(_x.limbs[0]);
206 x_hi.assert_equal(_x.limbs[1]);
207 y_lo.assert_equal(_y.limbs[0]);
208 y_hi.assert_equal(_y.limbs[1]);
209 }
210
211 // Set the tag of the result to the union of the tags of inputs
213 return result;
214 }
215
217 {
218 auto builder = get_context();
219 return constant_infinity(builder) - *this;
220 }
221
223 {
224 *this = *this + other;
225 return *this;
226 }
228 {
229 *this = *this - other;
230 return *this;
231 }
233 {
234 return std::array<goblin_element, 2>{ *this + other, *this - other };
235 }
236
237 goblin_element operator*(const Fr& scalar) const { return batch_mul({ *this }, { scalar }); }
238
240 {
241 goblin_element negated = -(*this);
242 goblin_element result(*this);
243 result._y = Fq::conditional_assign(predicate, negated._y, result._y);
244 return result;
245 }
246
254 goblin_element conditional_select(const goblin_element& other, const bool_ct& predicate) const
255 {
256 goblin_element result(*this);
257 result._x = Fq::conditional_assign(predicate, other._x, result._x);
258 result._y = Fq::conditional_assign(predicate, other._y, result._y);
259 return result;
260 }
261
263 {
264 // no need to normalize, all goblin eccvm operations are returned normalized
265 return *this;
266 }
267
269 {
270 // no need to reduce, all goblin eccvm operations are returned normalized
271 return *this;
272 }
273
275 {
276 auto builder = get_context();
277 return batch_mul({ *this }, { Fr(builder, 2) });
278 }
279 // interface compatible with biggroup.hpp, the final parameter
280 // handle_edge_cases is not needed as this is always done in the eccvm
282 const std::vector<Fr>& scalars,
283 const size_t max_num_bits = 0,
284 const bool handle_edge_cases = false);
285
286 // we use this data structure to add together a sequence of points.
287 // By tracking the previous values of x_1, y_1, \lambda, we can avoid
288
289 typename NativeGroup::affine_element get_value() const
290 {
291 bb::fq x_val = _x.get_value().lo;
292 bb::fq y_val = _y.get_value().lo;
293 auto result = typename NativeGroup::affine_element(x_val, y_val);
294 // ECCVM represents point-at-infinity as (0, 0)
295 if (x_val == 0 && y_val == 0) {
296 result.self_set_infinity();
297 }
298 return result;
299 }
300
302 {
303 if (_x.get_context() != nullptr) {
304 return _x.get_context();
305 }
306 if (_y.get_context() != nullptr) {
307 return _y.get_context();
308 }
309 return nullptr;
310 }
311
312 Builder* get_context(const goblin_element& other) const
313 {
314 if (_x.get_context() != nullptr) {
315 return _x.get_context();
316 }
317 if (_y.get_context() != nullptr) {
318 return _y.get_context();
319 }
320 if (other._x.get_context() != nullptr) {
321 return other._x.get_context();
322 }
323 if (other._y.get_context() != nullptr) {
324 return other._y.get_context();
325 }
326 return nullptr;
327 }
328
329 // ECCVM guarantees infinity is canonically (0, 0), so no normalization needed.
330 goblin_element get_standard_form() const { return *this; }
331
332 OriginTag get_origin_tag() const { return OriginTag(_x.get_origin_tag(), _y.get_origin_tag()); }
333
334 void set_origin_tag(const OriginTag& tag) const
335 {
336 _x.set_origin_tag(tag);
337 _y.set_origin_tag(tag);
338 }
339
344 {
345 _x.set_free_witness_tag();
346 _y.set_free_witness_tag();
347 }
348
353 {
354 _x.unset_free_witness_tag();
355 _y.unset_free_witness_tag();
356 }
364 uint32_t set_public() const
365 {
366 const uint32_t start_idx = _x.set_public();
367 _y.set_public();
368
369 return start_idx;
370 }
371
372 // Coordinate accessors (non-owning, const reference)
373 const Fq& x() const { return _x; }
374 const Fq& y() const { return _y; }
375 // Non-const accessors for internal use (e.g., fix_witness in tests)
376 Fq& x() { return _x; }
377 Fq& y() { return _y; }
378
379 private:
382};
383
387 bb::g1>;
388
389template <typename C, typename Fq, typename Fr, typename G>
390inline std::ostream& operator<<(std::ostream& os, goblin_element<C, Fq, Fr, G> const& v)
391{
392 return os << "{ " << v.x() << " , " << v.y() << " }";
393}
394} // namespace bb::stdlib::element_goblin
395
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
group class. Represents an elliptic curve group element. Group is parametrised by Fq and Fr
Definition group.hpp:36
Implements boolean logic in-circuit.
Definition bool.hpp:60
Custom element class for when using goblin.
goblin_element operator+(const goblin_element &other) const
void set_origin_tag(const OriginTag &tag) const
goblin_element operator-(const goblin_element &other) const
goblin_element conditional_negate(const bool_ct &predicate) const
goblin_element checked_unconditional_add(const goblin_element &other) const
static goblin_element batch_mul(const std::vector< goblin_element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits=0, const bool handle_edge_cases=false)
Goblin style batch multiplication.
goblin_element conditional_select(const goblin_element &other, const bool_ct &predicate) const
Selects this if predicate is false, other if predicate is true.
static goblin_element one(Builder *ctx)
goblin_element operator*(const Fr &scalar) const
uint32_t set_public() const
Set the witness indices representing the goblin element to public.
goblin_element(goblin_element &&other) noexcept=default
std::array< goblin_element, 2 > checked_unconditional_add_sub(const goblin_element &other) const
goblin_element checked_unconditional_subtract(const goblin_element &other) const
goblin_element operator+=(const goblin_element &other)
void incomplete_assert_equal(const goblin_element &other, const std::string msg="goblin_element::incomplete_assert_equal") const
Asserts that two goblin elements are equal (i.e., x, y coordinates are equal).
NativeGroup::affine_element get_value() const
void set_free_witness_tag()
Set the free witness flag for the goblin element's tags.
void convert_constant_to_fixed_witness(Builder *builder)
Creates fixed witnesses from a constant element.
goblin_element(const Fq &x, const Fq &y, bool assert_on_curve=true)
Builder * get_context(const goblin_element &other) const
goblin_element(const typename NativeGroup::affine_element &input)
goblin_element operator-=(const goblin_element &other)
goblin_element & operator=(const goblin_element &other)=default
static goblin_element from_witness(Builder *ctx, const typename NativeGroup::affine_element &input)
void unset_free_witness_tag()
Unset the free witness flag for the goblin element's tags.
goblin_element & operator=(goblin_element &&other) noexcept=default
static goblin_element constant_infinity(Builder *ctx)
Creates a constant point at infinity with canonical (0, 0) coordinates.
goblin_element(const goblin_element &other)=default
goblin_field wraps x/y coordinates of bn254 group elements when using goblin
AluTraceBuilder builder
Definition alu.test.cpp:124
std::ostream & operator<<(std::ostream &os, goblin_element< C, Fq, Fr, G > const &v)
MegaCircuitBuilder_< field< Bn254FrParams > > MegaCircuitBuilder
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
Curve::ScalarField Fr
curve::BN254::BaseField Fq