Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
pairing_points.test.cpp
Go to the documentation of this file.
7#include <gtest/gtest.h>
8
9namespace bb::stdlib::recursion {
10
11template <typename Builder> class PairingPointsTests : public testing::Test {
12 public:
14};
15
16using Curves = testing::Types<stdlib::bn254<UltraCircuitBuilder>, stdlib::bn254<MegaCircuitBuilder>>;
18
20{
21 using Builder = typename TypeParam::Builder;
22 static constexpr size_t NUM_GATES_ADDED = 8;
23
25
26 size_t num_gates = builder.num_gates();
28 EXPECT_EQ(NUM_GATES_ADDED, builder.num_gates() - num_gates)
29 << "There has been a change in the number of gates required to set default PairingPoints as public inputs.";
30
31 EXPECT_TRUE(CircuitChecker::check(builder));
32}
33
35{
36 using Builder = TypeParam::Builder;
39
41
42 Group P0(DEFAULT_PAIRING_POINT_P0_X, DEFAULT_PAIRING_POINT_P0_Y, /*assert_on_curve=*/false);
43 Group P1(DEFAULT_PAIRING_POINT_P1_X, DEFAULT_PAIRING_POINT_P1_Y, /*assert_on_curve=*/false);
44 P0.convert_constant_to_fixed_witness(&builder);
45 P1.convert_constant_to_fixed_witness(&builder);
46 PairingPoints<TypeParam> pp(P0, P1);
47 pp.set_public();
48 EXPECT_TRUE(CircuitChecker::check(builder));
49
50 // Validate default PairingPoints
51 CommitmentKey commitment_key;
52 bb::PairingPoints<curve::BN254> native_pp(P0.get_value(), P1.get_value());
53 EXPECT_TRUE(native_pp.check()) << "Default PairingPoints are not valid pairing points.";
54}
55
56TYPED_TEST(PairingPointsTests, TaggingMechanismWorks)
57{
58 using Curve = TypeParam;
59 using Builder = typename Curve::Builder;
61 using Group = PairingPoints::Group;
62 using Fr = PairingPoints::Fr;
63 using NativeFr = typename Curve::ScalarFieldNative;
64
66
67 Fr scalar_one = Fr::from_witness(&builder, NativeFr::random_element());
68 Fr scalar_two = Fr::from_witness(&builder, NativeFr::random_element());
69 Group P0 = Group::batch_mul({ Group::one(&builder) }, { scalar_one });
70 Group P1 = Group::batch_mul({ Group::one(&builder) }, { scalar_two });
71
72 // Check that no pairing points exist
73 EXPECT_TRUE(builder.pairing_points_tagging.has_single_pairing_point_tag());
74
75 PairingPoints pp_one = { P0, P1 };
76 PairingPoints pp_two = { P0, P1 };
77
78 // Check the tags
79 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_one.tag_index), 0U);
80 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_two.tag_index), 1U);
81
82 // Check that there are two different pairing points in the builder
83 EXPECT_FALSE(builder.pairing_points_tagging.has_single_pairing_point_tag());
84
85 // Merge the tags
86 pp_one.aggregate(pp_two);
87
88 // Check that the tags have been merged
89 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_two.tag_index), 0U);
90 EXPECT_TRUE(builder.pairing_points_tagging.has_single_pairing_point_tag());
91
92 // Create two new pairing points and aggregate with aggregate_multiple
93 PairingPoints pp_three = { P0, P1 };
94 PairingPoints pp_four = { P0, P1 };
95
96 // Check the tags
97 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_three.tag_index), 2U);
98 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_four.tag_index), 3U);
99
100 // Check that there are two different pairing points in the builder
101 EXPECT_FALSE(builder.pairing_points_tagging.has_single_pairing_point_tag());
102
103 // Merge the tags
104 std::vector<PairingPoints> pp_to_be_aggregated = { pp_one, pp_three, pp_four };
105 PairingPoints aggregated_pp = PairingPoints::aggregate_multiple(pp_to_be_aggregated);
106
107 // Check that the tags have been merged
108 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_one.tag_index), 4U);
109 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_two.tag_index), 4U);
110 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_three.tag_index), 4U);
111 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_four.tag_index), 4U);
112 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(aggregated_pp.tag_index), 4U);
113 EXPECT_TRUE(builder.pairing_points_tagging.has_single_pairing_point_tag());
114}
115
116TYPED_TEST(PairingPointsTests, TaggingMechanismFails)
117{
118
119 using Curve = TypeParam;
120 using Builder = typename Curve::Builder;
122 using Group = PairingPoints::Group;
123 using Fr = PairingPoints::Fr;
124 using NativeFr = typename Curve::ScalarFieldNative;
127
129
130 Fr scalar_one = Fr::from_witness(&builder, NativeFr::random_element());
131 Fr scalar_two = Fr::from_witness(&builder, NativeFr::random_element());
132 Group P0 = Group::batch_mul({ Group::one(&builder) }, { scalar_one });
133 Group P1 = Group::batch_mul({ Group::one(&builder) }, { scalar_two });
134
135 PairingPoints pp_one = { P0, P1 };
136 PairingPoints pp_two = { P0, P1 };
137 PairingPoints pp_three = { P0, P1 };
138
139 // Check the tags
140 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_one.tag_index), 0U);
141 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_two.tag_index), 1U);
142 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_three.tag_index), 2U);
143
144 // Check that there are different pairing points in the builder
145 EXPECT_FALSE(builder.pairing_points_tagging.has_single_pairing_point_tag());
146
147 // Merge the tags
148 pp_one.aggregate(pp_two);
149
150 // Check that the tags have not been merged
151 EXPECT_FALSE(builder.pairing_points_tagging.has_single_pairing_point_tag());
152
153 // Create a ProverInstance, expect failure because pairing points have not been aggregated
155 ProverInstance prover_instance(builder),
156 "Pairing points must all be aggregated together. Either no pairing points should be created, or "
157 "all created pairing points must be aggregated into a single pairing point. Found 2 different "
158 "pairing points");
159
160 // Aggregate pairing points
161 pp_one.aggregate(pp_three);
162
163 // Create a ProverInstance, expect failure because pairing points have not been set to public
165 ProverInstance prover_instance(builder),
166 "Pairing points must be set to public in the circuit before constructing the ProverInstance.");
167
169 inputs.pairing_inputs = pp_one;
171
172 // Construct Prover instance successfully
173 ProverInstance prover_instance(builder);
174}
175
176TYPED_TEST(PairingPointsTests, CopyConstructorWorks)
177{
178 using Curve = TypeParam;
179 using Builder = typename Curve::Builder;
180
182 using Group = PairingPoints::Group;
183 using Fr = Curve::ScalarField;
184 using NativeFr = Curve::ScalarFieldNative;
185
187
188 Fr scalar_one = Fr::from_witness(&builder, NativeFr::random_element());
189 Fr scalar_two = Fr::from_witness(&builder, NativeFr::random_element());
190 Group P0 = Group::batch_mul({ Group::one(&builder) }, { scalar_one });
191 Group P1 = Group::batch_mul({ Group::one(&builder) }, { scalar_two });
192
193 PairingPoints pp_original = { P0, P1 };
194 PairingPoints pp_copy(pp_original);
195
196 // Check that there is only one tag
197 EXPECT_TRUE(builder.pairing_points_tagging.has_single_pairing_point_tag());
198
199 // Check that the tags are the same
200 BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_original.tag_index),
201 builder.pairing_points_tagging.get_tag(pp_copy.tag_index));
202}
203
204TYPED_TEST(PairingPointsTests, AggregateMultipleWithDuplicatePoints)
205{
206 using Curve = TypeParam;
207 using Builder = typename Curve::Builder;
209 using Group = PairingPoints::Group;
210
212
213 // Use default pairing points that are known to satisfy the pairing equation
214 Group P0(DEFAULT_PAIRING_POINT_P0_X, DEFAULT_PAIRING_POINT_P0_Y, /*assert_on_curve=*/false);
215 Group P1(DEFAULT_PAIRING_POINT_P1_X, DEFAULT_PAIRING_POINT_P1_Y, /*assert_on_curve=*/false);
216 P0.convert_constant_to_fixed_witness(&builder);
217 P1.convert_constant_to_fixed_witness(&builder);
218
219 // Create duplicate pairing points (same P0, P1)
220 PairingPoints pp_first = { P0, P1 };
221 PairingPoints pp_second = { P0, P1 }; // Duplicate
222 PairingPoints pp_third = { P0, P1 }; // Another duplicate
223
224 // Test aggregate_multiple with all duplicate points
225 // The n-1 optimization computes: P_agg = P₀ + r₁·P₁ + r₂·P₂
226 // With duplicates: P_agg = P + r₁·P + r₂·P = (1 + r₁ + r₂)·P
227 // This tests that the optimization handles the edge case where first point equals others
228 std::vector<PairingPoints> pp_vector = { pp_first, pp_second, pp_third };
229 PairingPoints aggregated = PairingPoints::aggregate_multiple(pp_vector);
230
231 // Circuit should be valid
232 EXPECT_TRUE(CircuitChecker::check(builder));
233
234 // Verify tags are properly merged
235 EXPECT_TRUE(builder.pairing_points_tagging.has_single_pairing_point_tag());
236
237 // Verify the result is exactly what we expect: (1 + r₁ + r₂)·(P0, P1)
238 // We replicate the challenge generation to compute the expected scalar
239 using Fr = typename Curve::ScalarField;
241 for (size_t idx = 0; idx < 3; ++idx) {
242 transcript.add_to_hash_buffer("first_component_" + std::to_string(idx), pp_vector[idx].P0());
243 transcript.add_to_hash_buffer("second_component_" + std::to_string(idx), pp_vector[idx].P1());
244 }
245 std::array<std::string, 2> challenge_labels = { "pp_aggregation_challenge_1", "pp_aggregation_challenge_2" };
246 std::array<Fr, 2> challenges = transcript.template get_challenges<Fr, 2>(challenge_labels);
247
248 // Compute expected result: (1 + r₁ + r₂)·P0
249 Fr total_scalar = Fr(1);
250 for (const auto& challenge : challenges) {
251 total_scalar += challenge;
252 }
253 Group expected_P0 = P0 * total_scalar;
254 Group expected_P1 = P1 * total_scalar;
255
256 // Verify the aggregated result matches the expected result
257 EXPECT_EQ(aggregated.P0().get_value(), expected_P0.get_value()) << "Aggregated P0 should equal (1 + r₁ + r₂)·P0";
258 EXPECT_EQ(aggregated.P1().get_value(), expected_P1.get_value()) << "Aggregated P1 should equal (1 + r₁ + r₂)·P1";
259
260 // The result should still be a valid pairing point (scalar multiple of the original)
261 bb::PairingPoints<typename Curve::NativeCurve> native_aggregated(aggregated.P0().get_value(),
262 aggregated.P1().get_value());
263 EXPECT_TRUE(native_aggregated.check())
264 << "Aggregated duplicate pairing points should still satisfy pairing equation";
265}
266
267} // namespace bb::stdlib::recursion
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
void add_to_hash_buffer(const std::string &label, const T &element)
Adds an element to the transcript.
CommitmentKey object over a pairing group 𝔾₁.
An object storing two EC points that represent the inputs to a pairing check.
bool check() const
Perform the pairing check.
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
Manages the data that is propagated on the public inputs of an application/function circuit.
AluTraceBuilder builder
Definition alu.test.cpp:124
AvmProvingInputs inputs
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
TYPED_TEST(PairingPointsTests, ConstructDefault)
TYPED_TEST_SUITE(PairingPointsTests, Curves)
testing::Types< stdlib::bn254< UltraCircuitBuilder >, stdlib::bn254< MegaCircuitBuilder > > Curves
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
Curve::ScalarField Fr
An object storing two EC points that represent the inputs to a pairing check.
static uint32_t set_default_to_public(Builder *builder)
Set the witness indices for the default limbs of the pairing points to public.
static PairingPoints aggregate_multiple(std::vector< PairingPoints > &pairing_points, bool handle_edge_cases=true)
Aggregate multiple PairingPoints using random linear combination.
void aggregate(PairingPoints const &other)
Compute a linear combination of the present pairing points with an input set of pairing points.
uint32_t set_public()
Set the witness indices for the pairing points to public.