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: 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
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{
44 auto builder = validate_context<C>(validate_context<C>(points), validate_context<C>(scalars));
45
46 BB_ASSERT(builder != nullptr, "biggroup_goblin: builder context is invalid.");
47 BB_ASSERT(points.size() == scalars.size(), "biggroup_goblin: points and scalars lengths not equal.");
48
49 // Check that the internal accumulator is zero?
50 BB_ASSERT(builder->op_queue->get_accumulator().is_point_at_infinity());
51
52 // Loop over all points and scalars
53 size_t num_points = points.size();
54
55 OriginTag tag_union = OriginTag::constant(); // Initialize as CONSTANT so merging with input tags works correctly
56 for (size_t i = 0; i < num_points; ++i) {
57 auto& point = points[i];
58 auto& scalar = scalars[i];
59
60 // Merge tags
61 tag_union = OriginTag(tag_union, OriginTag(point.get_origin_tag(), scalar.get_origin_tag()));
62 // Populate the goblin-style ecc op gates for the given mul inputs
63 ecc_op_tuple op_tuple;
64 bool scalar_is_constant_equal_one = scalar.is_constant() && scalar.get_value() == 1;
65 if (scalar_is_constant_equal_one) { // if scalar is 1, there is no need to perform a mul
66 op_tuple = builder->queue_ecc_add_accum(point.get_value());
67 } else { // otherwise, perform a mul-then-accumulate
68 op_tuple = builder->queue_ecc_mul_accum(point.get_value(), scalar.get_value());
69 }
70
71 // Add constraints demonstrating that the EC point coordinates were decomposed faithfully. In particular, show
72 // that the lo-hi components that have been encoded in the op wires can be reconstructed via the limbs of the
73 // original point coordinates.
74 auto x_lo = Fr::from_witness_index(builder, op_tuple.x_lo);
75 auto x_hi = Fr::from_witness_index(builder, op_tuple.x_hi);
76 auto y_lo = Fr::from_witness_index(builder, op_tuple.y_lo);
77 auto y_hi = Fr::from_witness_index(builder, op_tuple.y_hi);
78 // Note: These constraints do not assume or enforce that the coordinates of the original point have been
79 // asserted to be in the field, only that they are less than the smallest power of 2 greater than the field
80 // modulus (a la the bigfield(lo, hi) constructor with can_overflow == false).
81 if (!point.get_value().is_point_at_infinity()) {
82 BB_ASSERT_LTE(uint1024_t(point._x.get_maximum_value()), Fq::DEFAULT_MAXIMUM_REMAINDER);
83 BB_ASSERT_LTE(uint1024_t(point._y.get_maximum_value()), Fq::DEFAULT_MAXIMUM_REMAINDER);
84 }
85 x_lo.assert_equal(point._x.limbs[0]);
86 x_hi.assert_equal(point._x.limbs[1]);
87 y_lo.assert_equal(point._y.limbs[0]);
88 y_hi.assert_equal(point._y.limbs[1]);
89
90 // Add constraints demonstrating proper decomposition of scalar into endomorphism scalars
91 if (!scalar_is_constant_equal_one) {
92 auto z_1 = Fr::from_witness_index(builder, op_tuple.z_1);
93 auto z_2 = Fr::from_witness_index(builder, op_tuple.z_2);
94 auto beta = G::Fr::cube_root_of_unity();
95 scalar.assert_equal(z_1 - z_2 * beta);
96 }
97 // There will be created op_tuple as a result of queue_ecc_mul_accum or queue_ecc_add_accum depending on scalar.
98 // What is more, all variables x_lo, x_hi, y_lo, y_hi from op_tuple are normalized witnesses, so assert_equal
99 // function doesn't create additional gates. Also
100 builder->update_used_witnesses({ op_tuple.x_lo, op_tuple.x_hi, op_tuple.y_lo, op_tuple.y_hi });
101 // if scalar equals one it means that we don't create gate for expression z_1 - z_2 * beta,
102 // so z_1 and z_2 will be in one gate for ecc_op
103 if (scalar_is_constant_equal_one) {
104 builder->update_used_witnesses({ op_tuple.z_1, op_tuple.z_2 });
105 }
106 }
107 // Populate equality gates based on the internal accumulator point
108 auto op_tuple = builder->queue_ecc_eq();
109 // When function queue_ecc_eq is called, scalar in function construct_and_populate_ultra_ops is zero by default.
110 // 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
111 // except for ecc_op gate, so we have to remove them from the scope using used_witnesses
112 builder->update_used_witnesses({ op_tuple.z_1, op_tuple.z_2 });
113 // Reconstruct the result of the batch mul using indices into the variables array
114 auto x_lo = Fr::from_witness_index(builder, op_tuple.x_lo);
115 auto x_hi = Fr::from_witness_index(builder, op_tuple.x_hi);
116 auto y_lo = Fr::from_witness_index(builder, op_tuple.y_lo);
117 auto y_hi = Fr::from_witness_index(builder, op_tuple.y_hi);
118 Fq point_x(x_lo, x_hi);
119 Fq point_y(y_lo, y_hi);
120
121 // Point-at-infinity is represented by (0, 0) coordinates; ECCVM enforces this.
122 goblin_element result(point_x, point_y);
123
124 // Set the tag of the result
125 result.set_origin_tag(tag_union);
126
127 return result;
128}
129
130} // namespace bb::stdlib::element_goblin
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:158
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)
Goblin style batch multiplication.
AluTraceBuilder builder
Definition alu.test.cpp:124
uintx< uint512_t > uint1024_t
Definition uintx.hpp:308
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 OriginTag constant()
static constexpr field cube_root_of_unity()