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: 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
9#include "../bigfield/bigfield.hpp"
10#include "../bigfield/goblin_field.hpp"
11#include "../circuit_builders/circuit_builders_fwd.hpp"
12#include "../field/field.hpp"
13#include "../memory/rom_table.hpp"
14#include "../memory/twin_rom_table.hpp"
20
22
38template <class Builder_, class Fq, class Fr, class NativeGroup> class goblin_element {
39 public:
40 using Builder = Builder_;
41 using BaseField = Fq;
43 using biggroup_tag = goblin_element; // Facilitates a constexpr check IsBigGroup
44
45 // Number of bb::fr field elements used to represent a goblin element in the public inputs
46 static constexpr size_t PUBLIC_INPUTS_SIZE = BIGGROUP_PUBLIC_INPUTS_SIZE;
47
48 goblin_element() = default;
49 goblin_element(const typename NativeGroup::affine_element& input)
50 : _x(input.x)
51 , _y(input.y)
53 {}
54
55 // Construct a goblin biggroup element from its coordinates
56 // The on-curve check is skipped as it is performed in ECCVM (assert_on_curve is unused)
57 goblin_element(const Fq& x, const Fq& y, [[maybe_unused]] bool assert_on_curve = true)
58 : _x(x)
59 , _y(y)
60 , _is_infinity(false)
61 {}
62
63 goblin_element(const Fq& x, const Fq& y, const bool_ct is_infinity, [[maybe_unused]] bool assert_on_curve = true)
64 : _x(x)
65 , _y(y)
66 , _is_infinity(is_infinity)
67 {}
68
69 goblin_element(const goblin_element& other) = default;
70 goblin_element(goblin_element&& other) noexcept = default;
71 goblin_element& operator=(const goblin_element& other) = default;
72 goblin_element& operator=(goblin_element&& other) noexcept = default;
73 ~goblin_element() = default;
74
86 const std::string msg = "goblin_element::incomplete_assert_equal") const
87 {
88 is_point_at_infinity().assert_equal(other.is_point_at_infinity(), msg + " (infinity flag)");
89 _x.assert_equal(other._x, msg + " (x coordinate)");
90 _y.assert_equal(other._y, msg + " (y coordinate)");
91 }
92
93 static goblin_element from_witness(Builder* ctx, const typename NativeGroup::affine_element& input)
94 {
96 // ECCVM requires points at infinity to be represented by 0-value x/y coords
97 if (input.is_point_at_infinity()) {
98 Fq x = Fq::from_witness(ctx, bb::fq(0));
99 Fq y = Fq::from_witness(ctx, bb::fq(0));
100 out._x = x;
101 out._y = y;
102 } else {
103 Fq x = Fq::from_witness(ctx, input.x);
104 Fq y = Fq::from_witness(ctx, input.y);
105 out._x = x;
106 out._y = y;
107 }
108 out.set_point_at_infinity(witness_t<Builder>(ctx, input.is_point_at_infinity()));
110 return out;
111 }
112
117 {
118 this->_x.convert_constant_to_fixed_witness(builder);
119 this->_y.convert_constant_to_fixed_witness(builder);
121 }
122
127 {
128 // Origin tags should be updated within
129 this->_x.fix_witness();
130 this->_y.fix_witness();
131
132 // This is now effectively a constant
134 }
135
136 void validate_on_curve() const
137 {
138 // happens in goblin eccvm
139 }
140
142 {
143 uint256_t x = uint256_t(NativeGroup::one.x);
144 uint256_t y = uint256_t(NativeGroup::one.y);
145 Fq x_fq(ctx, x);
146 Fq y_fq(ctx, y);
147 return goblin_element(x_fq, y_fq);
148 }
149
151 {
152 Fr zero = Fr::from_witness_index(ctx, ctx->zero_idx());
153 zero.unset_free_witness_tag();
154 Fq x_fq(zero, zero);
155 Fq y_fq(zero, zero);
156 goblin_element result(x_fq, y_fq);
157 result.set_point_at_infinity(true);
158 return result;
159 }
160
163
165 {
166 auto builder = get_context(other);
167 return batch_mul({ *this, other }, { Fr(builder, 1), Fr(builder, 1) });
168 }
169
171 {
172 auto builder = get_context(other);
173 // Check that the internal accumulator is zero
174 BB_ASSERT(builder->op_queue->get_accumulator().is_point_at_infinity());
175
176 // Compute the result natively, and validate that result + other == *this
177 typename NativeGroup::affine_element result_value = typename NativeGroup::affine_element(
178 typename NativeGroup::element(get_value()) - typename NativeGroup::element(other.get_value()));
179
180 ecc_op_tuple op_tuple;
181 op_tuple = builder->queue_ecc_add_accum(other.get_value());
182 {
183 auto x_lo = Fr::from_witness_index(builder, op_tuple.x_lo);
184 auto x_hi = Fr::from_witness_index(builder, op_tuple.x_hi);
185 auto y_lo = Fr::from_witness_index(builder, op_tuple.y_lo);
186 auto y_hi = Fr::from_witness_index(builder, op_tuple.y_hi);
187 x_lo.assert_equal(other._x.limbs[0]);
188 x_hi.assert_equal(other._x.limbs[1]);
189 y_lo.assert_equal(other._y.limbs[0]);
190 y_hi.assert_equal(other._y.limbs[1]);
191 }
192 // if function queue_ecc_add_accum is used, op_tuple creates as a result of construct_and_populate_ultra_ops
193 // function. In case of queue_ecc_add_accum, scalar is zero, (z_1, z_2) = (scalar, 0) = (0, 0) and they just put
194 // in the wires.
195 builder->update_used_witnesses({ op_tuple.z_1, op_tuple.z_2 });
196
197 ecc_op_tuple op_tuple2 = builder->queue_ecc_add_accum(result_value);
198 auto x_lo = Fr::from_witness_index(builder, op_tuple2.x_lo);
199 auto x_hi = Fr::from_witness_index(builder, op_tuple2.x_hi);
200 auto y_lo = Fr::from_witness_index(builder, op_tuple2.y_lo);
201 auto y_hi = Fr::from_witness_index(builder, op_tuple2.y_hi);
202
203 // if function queue_ecc_add_accum is used, op_tuple creates as a result of construct_and_populate_ultra_ops
204 // function. In case of queue_ecc_add_accum, scalar is zero, (z_1, z_2) = (scalar, 0) = (0, 0) and they just put
205 // in the wires.
206 builder->update_used_witnesses({ op_tuple2.z_1, op_tuple2.z_2 });
207
208 Fq result_x(x_lo, x_hi);
209 Fq result_y(y_lo, y_hi);
210 goblin_element result(result_x, result_y);
211
212 // if the output is at infinity, this is represented by x/y coordinates being zero
213 // because they are all 136-bit, we can do a cheap zerocheck by first summing the limbs
214 auto op2_is_infinity = (x_lo.add_two(x_hi, y_lo) + y_hi).is_zero();
215 result.set_point_at_infinity(op2_is_infinity);
216 {
217 ecc_op_tuple op_tuple3 = builder->queue_ecc_eq();
218 auto x_lo = Fr::from_witness_index(builder, op_tuple3.x_lo);
219 auto x_hi = Fr::from_witness_index(builder, op_tuple3.x_hi);
220 auto y_lo = Fr::from_witness_index(builder, op_tuple3.y_lo);
221 auto y_hi = Fr::from_witness_index(builder, op_tuple3.y_hi);
222
223 x_lo.assert_equal(_x.limbs[0]);
224 x_hi.assert_equal(_x.limbs[1]);
225 y_lo.assert_equal(_y.limbs[0]);
226 y_hi.assert_equal(_y.limbs[1]);
227 }
228
229 // Set the tag of the result to the union of the tags of inputs
231 return result;
232 }
233
235 {
236 auto builder = get_context();
237 return point_at_infinity(builder) - *this;
238 }
239
241 {
242 *this = *this + other;
243 return *this;
244 }
246 {
247 *this = *this - other;
248 return *this;
249 }
251 {
252 return std::array<goblin_element, 2>{ *this + other, *this - other };
253 }
254
255 goblin_element operator*(const Fr& scalar) const { return batch_mul({ *this }, { scalar }); }
256
258 {
259 goblin_element negated = -(*this);
260 goblin_element result(*this);
261 result._y = Fq::conditional_assign(predicate, negated._y, result._y);
262 return result;
263 }
264
272 goblin_element conditional_select(const goblin_element& other, const bool_ct& predicate) const
273 {
274 goblin_element result(*this);
275 result._x = Fq::conditional_assign(predicate, other._x, result._x);
276 result._y = Fq::conditional_assign(predicate, other._y, result._y);
277 result._is_infinity =
279 return result;
280 }
281
283 {
284 // no need to normalize, all goblin eccvm operations are returned normalized
285 return *this;
286 }
287
289 {
290 // no need to reduce, all goblin eccvm operations are returned normalized
291 return *this;
292 }
293
295 {
296 auto builder = get_context();
297 return batch_mul({ *this }, { Fr(builder, 2) });
298 }
299
300 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1291) max_num_bits is unused; could implement and
301 // use this to optimize other operations. interface compatible with biggroup.hpp, the final parameter
302 // handle_edge_cases is not needed as this is always done in the eccvm
304 const std::vector<Fr>& scalars,
305 const size_t max_num_bits = 0,
306 const bool handle_edge_cases = false,
307 const Fr& masking_scalar = Fr(1));
308
309 // we use this data structure to add together a sequence of points.
310 // By tracking the previous values of x_1, y_1, \lambda, we can avoid
311
312 typename NativeGroup::affine_element get_value() const
313 {
314 bb::fq x_val = _x.get_value().lo;
315 bb::fq y_val = _y.get_value().lo;
316 auto result = typename NativeGroup::affine_element(x_val, y_val);
318 result.self_set_infinity();
319 }
320 return result;
321 }
322
324 {
325 if (_x.get_context() != nullptr) {
326 return _x.get_context();
327 }
328 if (_y.get_context() != nullptr) {
329 return _y.get_context();
330 }
331 return nullptr;
332 }
333
334 Builder* get_context(const goblin_element& other) const
335 {
336 if (_x.get_context() != nullptr) {
337 return _x.get_context();
338 }
339 if (_y.get_context() != nullptr) {
340 return _y.get_context();
341 }
342 if (other._x.get_context() != nullptr) {
343 return other._x.get_context();
344 }
345 if (other._y.get_context() != nullptr) {
346 return other._y.get_context();
347 }
348 return nullptr;
349 }
350
352 void set_point_at_infinity(const bool_ct& is_infinity) { _is_infinity = is_infinity; }
361 {
362 const bool_ct is_infinity = is_point_at_infinity();
363 goblin_element result(*this);
364 const Fq zero = Fq::zero();
365 result._x = Fq::conditional_assign(is_infinity, zero, result._x);
366 result._y = Fq::conditional_assign(is_infinity, zero, result._y);
367 return result;
368 }
369
371 {
372 return OriginTag(_x.get_origin_tag(), _y.get_origin_tag(), _is_infinity.get_origin_tag());
373 }
374
375 void set_origin_tag(const OriginTag& tag) const
376 {
377 _x.set_origin_tag(tag);
378 _y.set_origin_tag(tag);
380 }
381
386 {
387 _x.set_free_witness_tag();
388 _y.set_free_witness_tag();
390 }
391
396 {
397 _x.unset_free_witness_tag();
398 _y.unset_free_witness_tag();
400 }
410 uint32_t set_public() const
411 {
412 const uint32_t start_idx = _x.set_public();
413 _y.set_public();
414
415 return start_idx;
416 }
417
418 // Coordinate accessors (non-owning, const reference)
419 const Fq& x() const { return _x; }
420 const Fq& y() const { return _y; }
421 // Non-const accessors for internal use (e.g., fix_witness in tests)
422 Fq& x() { return _x; }
423 Fq& y() { return _y; }
424
434 {
435 const size_t FRS_PER_FQ = Fq::PUBLIC_INPUTS_SIZE;
436 std::span<const Fr, FRS_PER_FQ> x_limbs{ limbs.data(), FRS_PER_FQ };
437 std::span<const Fr, FRS_PER_FQ> y_limbs{ limbs.data() + FRS_PER_FQ, FRS_PER_FQ };
438
439 return { Fq::reconstruct_from_public(x_limbs), Fq::reconstruct_from_public(y_limbs) };
440 }
441
442 private:
446};
447
451 bb::g1>;
452
453template <typename C, typename Fq, typename Fr, typename G>
454inline std::ostream& operator<<(std::ostream& os, goblin_element<C, Fq, Fr, G> const& v)
455{
456 return os << "{ " << v.x() << " , " << v.y() << " }";
457}
458} // namespace bb::stdlib::element_goblin
459
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
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:59
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:153
void set_free_witness_tag()
Definition bool.hpp:155
static bool_t conditional_assign(const bool_t< Builder > &predicate, const bool_t &lhs, const bool_t &rhs)
Conditionally assign lhs or rhs based on predicate, always returns normalized result.
Definition bool.cpp:465
void unset_free_witness_tag()
Definition bool.hpp:156
void assert_equal(const bool_t &rhs, std::string const &msg="bool_t::assert_equal") const
Implements copy constraint for bool_t elements.
Definition bool.cpp:421
OriginTag get_origin_tag() const
Definition bool.hpp:154
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(const Fq &x, const Fq &y, const bool_ct is_infinity, bool assert_on_curve=true)
goblin_element checked_unconditional_add(const goblin_element &other) const
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)
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, const Fr &masking_scalar=Fr(1))
Goblin style batch multiplication.
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 and infinity flag are all 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 get_standard_form() const
Enforce x and y coordinates of a point to be (0,0) in the case of point at infinity.
static goblin_element reconstruct_from_public(const std::span< const Fr, PUBLIC_INPUTS_SIZE > &limbs)
Reconstruct a goblin element from its representation as limbs stored in the public inputs.
goblin_element(const Fq &x, const Fq &y, bool assert_on_curve=true)
Builder * get_context(const goblin_element &other) const
static goblin_element point_at_infinity(Builder *ctx)
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
void set_point_at_infinity(const bool_ct &is_infinity)
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
static constexpr size_t PUBLIC_INPUTS_SIZE
static field reconstruct_from_public(const std::span< const field< V >, PUBLIC_INPUTS_SIZE > &limbs)
static constexpr field zero()
curve::BN254::BaseField Fq