Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
biggroup_goblin_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
14
38template <typename C, class Fq, class Fr, class G>
40 const std::vector<Fr>& scalars,
41 [[maybe_unused]] const size_t max_num_bits,
42 [[maybe_unused]] const bool handle_edge_cases,
43 [[maybe_unused]] const Fr& masking_scalar)
44{
45 auto builder = validate_context<C>(validate_context<C>(points), validate_context<C>(scalars));
46
47 BB_ASSERT(builder != nullptr, "biggroup_goblin: builder context is invalid.");
48 BB_ASSERT(points.size() == scalars.size(), "biggroup_goblin: points and scalars lengths not equal.");
49
50 // Check that the internal accumulator is zero?
51 BB_ASSERT(builder->op_queue->get_accumulator().is_point_at_infinity());
52
53 // Loop over all points and scalars
54 size_t num_points = points.size();
55
56 OriginTag tag_union{};
57 for (size_t i = 0; i < num_points; ++i) {
58 auto& point = points[i];
59 auto& scalar = scalars[i];
60
61 // Merge tags
62
63 tag_union = OriginTag(tag_union, OriginTag(point.get_origin_tag(), scalar.get_origin_tag()));
64 // Populate the goblin-style ecc op gates for the given mul inputs
65 ecc_op_tuple op_tuple;
66 bool scalar_is_constant_equal_one = scalar.is_constant() && scalar.get_value() == 1;
67 if (scalar_is_constant_equal_one) { // if scalar is 1, there is no need to perform a mul
68 op_tuple = builder->queue_ecc_add_accum(point.get_value());
69 } else { // otherwise, perform a mul-then-accumulate
70 op_tuple = builder->queue_ecc_mul_accum(point.get_value(), scalar.get_value());
71 }
72
73 // Add constraints demonstrating that the EC point coordinates were decomposed faithfully. In particular, show
74 // that the lo-hi components that have been encoded in the op wires can be reconstructed via the limbs of the
75 // original point coordinates.
76 auto x_lo = Fr::from_witness_index(builder, op_tuple.x_lo);
77 auto x_hi = Fr::from_witness_index(builder, op_tuple.x_hi);
78 auto y_lo = Fr::from_witness_index(builder, op_tuple.y_lo);
79 auto y_hi = Fr::from_witness_index(builder, op_tuple.y_hi);
80 // Note: These constraints do not assume or enforce that the coordinates of the original point have been
81 // asserted to be in the field, only that they are less than the smallest power of 2 greater than the field
82 // modulus (a la the bigfield(lo, hi) constructor with can_overflow == false).
83 if (!point.get_value().is_point_at_infinity()) {
84 BB_ASSERT_LTE(uint1024_t(point._x.get_maximum_value()), Fq::DEFAULT_MAXIMUM_REMAINDER);
85 BB_ASSERT_LTE(uint1024_t(point._y.get_maximum_value()), Fq::DEFAULT_MAXIMUM_REMAINDER);
86 }
87 x_lo.assert_equal(point._x.limbs[0]);
88 x_hi.assert_equal(point._x.limbs[1]);
89 y_lo.assert_equal(point._y.limbs[0]);
90 y_hi.assert_equal(point._y.limbs[1]);
91
92 // Add constraints demonstrating proper decomposition of scalar into endomorphism scalars
93 if (!scalar_is_constant_equal_one) {
94 auto z_1 = Fr::from_witness_index(builder, op_tuple.z_1);
95 auto z_2 = Fr::from_witness_index(builder, op_tuple.z_2);
96 auto beta = G::Fr::cube_root_of_unity();
97 scalar.assert_equal(z_1 - z_2 * beta);
98 }
99 // There will be created op_tuple as a result of queue_ecc_mul_accum or queue_ecc_add_accum depending on scalar.
100 // What is more, all variables x_lo, x_hi, y_lo, y_hi from op_tuple are normalized witnesses, so assert_equal
101 // function doesn't create additional gates. Also
102 builder->update_used_witnesses({ op_tuple.x_lo, op_tuple.x_hi, op_tuple.y_lo, op_tuple.y_hi });
103 // if scalar equals one it means that we don't create gate for expression z_1 - z_2 * beta,
104 // so z_1 and z_2 will be in one gate for ecc_op
105 if (scalar_is_constant_equal_one) {
106 builder->update_used_witnesses({ op_tuple.z_1, op_tuple.z_2 });
107 }
108 }
109 // Populate equality gates based on the internal accumulator point
110 auto op_tuple = builder->queue_ecc_eq();
111 // When function queue_ecc_eq is called, scalar in function construct_and_populate_ultra_ops is zero by default.
112 // so, (z_1, z_2) = (scalar, 0) and we just put the to the wires. But z_1 and z_2 aren't used anymore in the circuit
113 // except for ecc_op gate, so we have to remove them from the scope using used_witnesses
114 builder->update_used_witnesses({ op_tuple.z_1, op_tuple.z_2 });
115 // Reconstruct the result of the batch mul using indices into the variables array
116 auto x_lo = Fr::from_witness_index(builder, op_tuple.x_lo);
117 auto x_hi = Fr::from_witness_index(builder, op_tuple.x_hi);
118 auto y_lo = Fr::from_witness_index(builder, op_tuple.y_lo);
119 auto y_hi = Fr::from_witness_index(builder, op_tuple.y_hi);
120 Fq point_x(x_lo, x_hi);
121 Fq point_y(y_lo, y_hi);
122 goblin_element result = goblin_element(point_x, point_y);
123
124 // NOTE: this used to be set as a circuit constant from `op_tuple.return_is_infinity
125 // I do not see how this was secure as it meant a circuit constant could change depending on witness values
126 // e.g. x*[P] + y*[Q] where `x = y` and `[P] = -[Q]`
127 // TODO(@zac-williamson) what is op_queue.return_is_infinity actually used for? I don't see its value
128 auto op2_is_infinity = (x_lo.add_two(x_hi, y_lo) + y_hi).is_zero();
129 result.set_point_at_infinity(op2_is_infinity);
130
131 // Set the tag of the result
132 result.set_origin_tag(tag_union);
133
134 return result;
135}
136
137} // namespace bb::stdlib::element_goblin
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:152
Custom element class for when using goblin.
void set_origin_tag(const OriginTag &tag) 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, const Fr &masking_scalar=Fr(1))
Goblin style batch multiplication.
void set_point_at_infinity(const bool_ct &is_infinity)
AluTraceBuilder builder
Definition alu.test.cpp:124
uintx< uint512_t > uint1024_t
Definition uintx.hpp:309
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...
static constexpr field cube_root_of_unity()