Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
scalar_multiplication_safe_mode.test.cpp
Go to the documentation of this file.
6#include <gtest/gtest.h>
7
8using namespace bb;
9
10namespace {
12} // namespace
13
22template <class Curve> class ScalarMultiplicationSafeModeTest : public ::testing::Test {
23 public:
24 using Group = typename Curve::Group;
25 using Element = typename Curve::Element;
28
29 static constexpr size_t num_points = 1000;
30 static inline std::vector<AffineElement> generators{};
31 static inline std::vector<ScalarField> scalars{};
32
33 static void SetUpTestSuite()
34 {
35 generators.resize(num_points);
36 scalars.resize(num_points);
37 for (size_t i = 0; i < num_points; ++i) {
38 generators[i] = Group::one * Curve::ScalarField::random_element(&engine);
39 scalars[i] = Curve::ScalarField::random_element(&engine);
40 }
41 };
42
43 void test_duplicate_points_helper(size_t num_pts)
44 {
45 AffineElement base_point = generators[0];
46
47 std::vector<AffineElement> points(num_pts, base_point);
48 std::vector<ScalarField> test_scalars(num_pts);
49 ScalarField scalar_sum = ScalarField::zero();
50
51 for (size_t i = 0; i < num_pts; ++i) {
52 test_scalars[i] = scalars[i];
53 scalar_sum += test_scalars[i];
54 }
55
56 PolynomialSpan<ScalarField> scalar_span(0, test_scalars);
57
58 auto result = scalar_multiplication::pippenger<Curve>(scalar_span, points, /*handle_edge_cases=*/true);
59
60 AffineElement expected(base_point * scalar_sum);
61 EXPECT_EQ(AffineElement(result), expected) << "Failed for size " << num_pts;
62 }
63
65 {
66 // Test below SINGLE_MUL_THRESHOLD (16) - uses small_mul
68 // Test above SINGLE_MUL_THRESHOLD - uses jacobian pippenger
70 }
71
72 void test_point_and_negation_helper(size_t num_pairs)
73 {
74 const size_t num_pts = num_pairs * 2;
75
76 std::vector<AffineElement> points(num_pts);
77 std::vector<ScalarField> test_scalars(num_pts);
78
79 // Create pairs of (P, -P) with same scalar - should cancel to zero
80 for (size_t i = 0; i < num_pairs; ++i) {
81 points[2 * i] = generators[i];
82 points[2 * i + 1] = -generators[i];
83 test_scalars[2 * i] = ScalarField::one();
84 test_scalars[2 * i + 1] = ScalarField::one();
85 }
86
87 PolynomialSpan<ScalarField> scalar_span(0, test_scalars);
88
89 auto result = scalar_multiplication::pippenger<Curve>(scalar_span, points, /*handle_edge_cases=*/true);
90
91 EXPECT_EQ(AffineElement(result), Group::affine_point_at_infinity) << "Failed for " << num_pairs << " pairs";
92 }
93
95 {
96 // Test below SINGLE_MUL_THRESHOLD (16) - 5 pairs = 10 points
98 // Test above SINGLE_MUL_THRESHOLD - 30 pairs = 60 points
100 }
101
103 {
104 // Test sizes: 10 (below SINGLE_MUL_THRESHOLD) and 50 (above threshold)
105 for (size_t scale : { size_t{ 1 }, size_t{ 5 } }) {
106 const size_t num_dup = 4 * scale;
107 const size_t num_cancel_pairs = 2 * scale;
108 const size_t num_unique = 4 * scale;
109 const size_t num_pts = num_dup + num_cancel_pairs * 2 + num_unique; // 12 or 60
110
111 std::vector<AffineElement> points(num_pts);
112 std::vector<ScalarField> test_scalars(num_pts);
113
114 size_t idx = 0;
115 // First section: all same point
116 for (size_t i = 0; i < num_dup; ++i) {
117 points[idx] = generators[0];
118 test_scalars[idx] = scalars[i];
119 idx++;
120 }
121 // Middle section: pairs of point and negation
122 for (size_t i = 0; i < num_cancel_pairs; ++i) {
123 points[idx] = generators[i + 1];
124 points[idx + 1] = -generators[i + 1];
125 test_scalars[idx] = ScalarField::one();
126 test_scalars[idx + 1] = ScalarField::one();
127 idx += 2;
128 }
129 // Last section: unique points
130 for (size_t i = 0; i < num_unique; ++i) {
131 points[idx] = generators[100 + i];
132 test_scalars[idx] = scalars[100 + i];
133 idx++;
134 }
135
136 PolynomialSpan<ScalarField> scalar_span(0, test_scalars);
137
138 auto result = scalar_multiplication::pippenger<Curve>(scalar_span, points, /*handle_edge_cases=*/true);
139
140 // Compute expected: sum of duplicate scalars * generators[0] + sum of unique
141 Element expected;
142 expected.self_set_infinity();
143
144 ScalarField first_sum = ScalarField::zero();
145 for (size_t i = 0; i < num_dup; ++i) {
146 first_sum += scalars[i];
147 }
148 expected += generators[0] * first_sum;
149
150 for (size_t i = 0; i < num_unique; ++i) {
151 expected += generators[100 + i] * scalars[100 + i];
152 }
153
154 EXPECT_EQ(AffineElement(result), AffineElement(expected)) << "Failed for size " << num_pts;
155 }
156 }
157
159 {
160 // Test both below and above SINGLE_MUL_THRESHOLD (16)
161 for (size_t num_pts : { size_t{ 10 }, size_t{ 50 } }) {
162 AffineElement base_point = generators[0];
163
164 std::vector<AffineElement> points(num_pts, base_point);
165 std::vector<ScalarField> test_scalars(num_pts);
166
167 ScalarField scalar_sum = ScalarField::zero();
168 for (size_t i = 0; i < num_pts; ++i) {
169 test_scalars[i] = ScalarField::random_element(&engine);
170 scalar_sum += test_scalars[i];
171 }
172
173 PolynomialSpan<ScalarField> scalar_span(0, test_scalars);
174
175 auto result = scalar_multiplication::pippenger<Curve>(scalar_span, points, /*handle_edge_cases=*/true);
176
177 AffineElement expected(base_point * scalar_sum);
178 EXPECT_EQ(AffineElement(result), expected) << "Failed for size " << num_pts;
179 }
180 }
181
183 {
184 std::vector<AffineElement> points;
185 std::vector<ScalarField> test_scalars;
186
187 PolynomialSpan<ScalarField> scalar_span(0, test_scalars);
188
189 auto result = scalar_multiplication::pippenger<Curve>(scalar_span, points, /*handle_edge_cases=*/true);
190
191 EXPECT_EQ(AffineElement(result), Group::affine_point_at_infinity);
192 }
193
195 {
196 // Test that zero scalars contribute nothing to the result
197 for (size_t num_pts : { size_t{ 10 }, size_t{ 50 } }) {
198 std::vector<AffineElement> points(num_pts);
199 std::vector<ScalarField> test_scalars(num_pts);
200
201 // Half zero scalars, half random
202 Element expected;
203 expected.self_set_infinity();
204
205 for (size_t i = 0; i < num_pts; ++i) {
206 points[i] = generators[i];
207 if (i % 2 == 0) {
208 test_scalars[i] = ScalarField::zero();
209 // Zero scalar contributes nothing
210 } else {
211 test_scalars[i] = scalars[i];
212 expected += generators[i] * scalars[i];
213 }
214 }
215
216 PolynomialSpan<ScalarField> scalar_span(0, test_scalars);
217
218 auto result = scalar_multiplication::pippenger<Curve>(scalar_span, points, /*handle_edge_cases=*/true);
219
220 EXPECT_EQ(AffineElement(result), AffineElement(expected)) << "Failed for size " << num_pts;
221 }
222 }
223
225 {
226 // All zero scalars should produce infinity
227 for (size_t num_pts : { size_t{ 10 }, size_t{ 50 } }) {
228 std::vector<AffineElement> points(num_pts);
229 std::vector<ScalarField> test_scalars(num_pts, ScalarField::zero());
230
231 for (size_t i = 0; i < num_pts; ++i) {
232 points[i] = generators[i];
233 }
234
235 PolynomialSpan<ScalarField> scalar_span(0, test_scalars);
236
237 auto result = scalar_multiplication::pippenger<Curve>(scalar_span, points, /*handle_edge_cases=*/true);
238
239 EXPECT_EQ(AffineElement(result), Group::affine_point_at_infinity) << "Failed for size " << num_pts;
240 }
241 }
242
244 {
245 // Points at infinity in the input should be handled correctly
246 for (size_t num_pts : { size_t{ 12 }, size_t{ 51 } }) {
247 std::vector<AffineElement> points(num_pts);
248 std::vector<ScalarField> test_scalars(num_pts);
249
250 Element expected;
251 expected.self_set_infinity();
252
253 for (size_t i = 0; i < num_pts; ++i) {
254 if (i % 3 == 0) {
255 // Every third point is infinity
256 points[i] = Group::affine_point_at_infinity;
257 test_scalars[i] = scalars[i]; // Non-zero scalar, but infinity * s = infinity
258 } else {
259 points[i] = generators[i];
260 test_scalars[i] = scalars[i];
261 expected += generators[i] * scalars[i];
262 }
263 }
264
265 PolynomialSpan<ScalarField> scalar_span(0, test_scalars);
266
267 auto result = scalar_multiplication::pippenger<Curve>(scalar_span, points, /*handle_edge_cases=*/true);
268
269 EXPECT_EQ(AffineElement(result), AffineElement(expected)) << "Failed for size " << num_pts;
270 }
271 }
272
274 {
275 // All points at infinity should produce infinity regardless of scalars
276 for (size_t num_pts : { size_t{ 10 }, size_t{ 50 } }) {
277 std::vector<AffineElement> points(num_pts, Group::affine_point_at_infinity);
278 std::vector<ScalarField> test_scalars(num_pts);
279
280 for (size_t i = 0; i < num_pts; ++i) {
281 test_scalars[i] = scalars[i]; // Random non-zero scalars
282 }
283
284 PolynomialSpan<ScalarField> scalar_span(0, test_scalars);
285
286 auto result = scalar_multiplication::pippenger<Curve>(scalar_span, points, /*handle_edge_cases=*/true);
287
288 EXPECT_EQ(AffineElement(result), Group::affine_point_at_infinity) << "Failed for size " << num_pts;
289 }
290 }
291};
292
293using CurveTypes = ::testing::Types<bb::curve::BN254, bb::curve::Grumpkin>;
295
297{
298 TestFixture::test_duplicate_points();
299}
301{
302 TestFixture::test_point_and_negation();
303}
305{
306 TestFixture::test_mixed_duplicates_and_unique();
307}
308TYPED_TEST(ScalarMultiplicationSafeModeTest, AllSamePointDifferentScalars)
309{
310 TestFixture::test_all_same_point_different_scalars();
311}
313{
314 TestFixture::test_empty_input();
315}
317{
318 TestFixture::test_zero_scalars();
319}
321{
322 TestFixture::test_all_zero_scalars();
323}
325{
326 TestFixture::test_points_at_infinity_in_input();
327}
329{
330 TestFixture::test_all_points_at_infinity();
331}
Tests for pippenger safe mode (handle_edge_cases=true) which is used in native batch_mul....
typename Group::element Element
Definition grumpkin.hpp:62
typename grumpkin::g1 Group
Definition grumpkin.hpp:61
typename Group::affine_element AffineElement
Definition grumpkin.hpp:63
numeric::RNG & engine
RNG & get_randomness()
Definition engine.cpp:203
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
::testing::Types< curve::BN254, curve::Grumpkin > CurveTypes
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)