Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ultra_circuit_builder_elliptic.test.cpp
Go to the documentation of this file.
4
5#include <gtest/gtest.h>
6
7using namespace bb;
8
9class UltraCircuitBuilderElliptic : public ::testing::Test {
10 protected:
13
14 // Helper to create points for addition/subtraction: (p1, p2, p1 ± p2)
18
19 // Helper to create points for doubling: (p1, 2*p1)
23
24 static AdditionPoints create_add_points(uint64_t seed1 = 1, uint64_t seed2 = 2, bool is_addition = true)
25 {
28 affine_element result =
29 is_addition ? affine_element(element(p1) + element(p2)) : affine_element(element(p1) - element(p2));
30 return { p1, p2, result };
31 }
32
33 static DoublingPoints create_dbl_points(uint64_t seed = 1)
34 {
36 affine_element result(element(p1).dbl());
37 return { p1, result };
38 }
39
40 // Add all variables for an add gate and return wire indices as tuple
42 {
43 return std::make_tuple(builder.add_variable(points.p1.x),
44 builder.add_variable(points.p1.y),
45 builder.add_variable(points.p2.x),
46 builder.add_variable(points.p2.y),
47 builder.add_variable(points.result.x),
48 builder.add_variable(points.result.y));
49 }
50
51 // Add all variables for a dbl gate and return wire indices as tuple
53 {
54 return std::make_tuple(builder.add_variable(points.p1.x),
55 builder.add_variable(points.p1.y),
56 builder.add_variable(points.result.x),
57 builder.add_variable(points.result.y));
58 }
59};
60
61// Verifies that a valid elliptic curve point addition passes the circuit checker.
63{
65 auto points = create_add_points(1, 2, true);
66
67 auto [x1, y1, x2, y2, x3, y3] = add_add_gate_variables(builder, points);
68 builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 });
69
70 EXPECT_TRUE(CircuitChecker::check(builder));
71}
72
73// Verifies that invalidating any coordinate in an addition operation causes the circuit checker to fail.
75{
76 auto test_invalid_coordinate = [](auto modify_points) {
78 auto points = create_add_points(1, 2, true);
79 modify_points(points);
80 auto [x1, y1, x2, y2, x3, y3] = add_add_gate_variables(builder, points);
81 builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 });
82 EXPECT_FALSE(CircuitChecker::check(builder));
83 };
84
85 test_invalid_coordinate([](AdditionPoints& p) { p.p1.x += bb::fr(1); });
86 test_invalid_coordinate([](AdditionPoints& p) { p.p1.y += bb::fr(1); });
87 test_invalid_coordinate([](AdditionPoints& p) { p.p2.x += bb::fr(1); });
88 test_invalid_coordinate([](AdditionPoints& p) { p.p2.y += bb::fr(1); });
89 test_invalid_coordinate([](AdditionPoints& p) { p.result.x += bb::fr(1); });
90 test_invalid_coordinate([](AdditionPoints& p) { p.result.y += bb::fr(1); });
91}
92
93// Verifies that a valid elliptic curve point subtraction passes the circuit checker.
95{
97 auto points = create_add_points(1, 2, false); // false = subtraction
98 auto [x1, y1, x2, y2, x3, y3] = add_add_gate_variables(builder, points);
99 builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, -1 });
100 EXPECT_TRUE(CircuitChecker::check(builder));
101}
102
103// Verifies that invalidating any coordinate in a subtraction operation causes the circuit checker to fail.
105{
106 auto test_invalid_coordinate = [](auto modify_points) {
108 auto points = create_add_points(1, 2, /*is_addition=*/false);
109 modify_points(points);
110 auto [x1, y1, x2, y2, x3, y3] = add_add_gate_variables(builder, points);
111 builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, /*sign_coefficient=*/-1 });
112 EXPECT_FALSE(CircuitChecker::check(builder));
113 };
114
115 test_invalid_coordinate([](AdditionPoints& p) { p.p1.x += bb::fr(1); });
116 test_invalid_coordinate([](AdditionPoints& p) { p.p1.y += bb::fr(1); });
117 test_invalid_coordinate([](AdditionPoints& p) { p.p2.x += bb::fr(1); });
118 test_invalid_coordinate([](AdditionPoints& p) { p.p2.y += bb::fr(1); });
119 test_invalid_coordinate([](AdditionPoints& p) { p.result.x += bb::fr(1); });
120 test_invalid_coordinate([](AdditionPoints& p) { p.result.y += bb::fr(1); });
121}
122
123// Verifies that a valid elliptic curve point doubling passes the circuit checker.
125{
127 auto points = create_dbl_points(1);
128
129 auto [x1, y1, x3, y3] = add_dbl_gate_variables(builder, points);
130 builder.create_ecc_dbl_gate({ x1, y1, x3, y3 });
131
132 EXPECT_TRUE(CircuitChecker::check(builder));
133}
134
135// Verifies that invalidating any coordinate in a doubling operation causes the circuit checker to fail.
137{
138 auto test_invalid_coordinate = [](auto modify_points) {
140 auto points = create_dbl_points(1);
141 modify_points(points);
142 auto [x1, y1, x3, y3] = add_dbl_gate_variables(builder, points);
143 builder.create_ecc_dbl_gate({ x1, y1, x3, y3 });
144 EXPECT_FALSE(CircuitChecker::check(builder));
145 };
146
147 test_invalid_coordinate([](DoublingPoints& p) { p.p1.x += bb::fr(1); });
148 test_invalid_coordinate([](DoublingPoints& p) { p.p1.y += bb::fr(1); });
149 test_invalid_coordinate([](DoublingPoints& p) { p.result.x += bb::fr(1); });
150 test_invalid_coordinate([](DoublingPoints& p) { p.result.y += bb::fr(1); });
151}
152
153// Verifies that multiple independent elliptic curve operations can coexist in a circuit.
154TEST_F(UltraCircuitBuilderElliptic, MultipleOperationsUnchained)
155{
157
158 // Create three different operations
159 auto add_points = create_add_points(1, 2, true);
160 auto sub_points = create_add_points(1, 3, false);
161 auto dbl_points = create_dbl_points(2);
162
163 // Add all gates
164 auto [add_x1, add_y1, add_x2, add_y2, add_x3, add_y3] = add_add_gate_variables(builder, add_points);
165 auto [sub_x1, sub_y1, sub_x2, sub_y2, sub_x3, sub_y3] = add_add_gate_variables(builder, sub_points);
166 auto [dbl_x1, dbl_y1, dbl_x3, dbl_y3] = add_dbl_gate_variables(builder, dbl_points);
167
168 builder.create_ecc_add_gate({ add_x1, add_y1, add_x2, add_y2, add_x3, add_y3, /*sign_coefficient=*/1 });
169 builder.create_ecc_add_gate({ sub_x1, sub_y1, sub_x2, sub_y2, sub_x3, sub_y3, /*sign_coefficient=*/-1 });
170 builder.create_ecc_dbl_gate({ dbl_x1, dbl_y1, dbl_x3, dbl_y3 });
171
172 EXPECT_EQ(builder.blocks.elliptic.size(), 6UL); // 3 unchained operations, 2 gates each
173 EXPECT_TRUE(CircuitChecker::check(builder));
174}
175
176// Verifies that chaining two operations by reusing intermediate results reduces the gate count.
178{
180
181 // First addition: p1 + p2 = temp
182 auto first_add = create_add_points(1, 2, true);
183
184 // Second addition: temp + p3 = result
186 affine_element result(element(first_add.result) + element(p3));
187
188 // Add variables for first operation
189 auto [x1, y1, x2, y2, x_temp, y_temp] = add_add_gate_variables(builder, first_add);
190
191 // Add variables for second operation
192 uint32_t x3 = builder.add_variable(p3.x);
193 uint32_t y3 = builder.add_variable(p3.y);
194 uint32_t x_result = builder.add_variable(result.x);
195 uint32_t y_result = builder.add_variable(result.y);
196
197 builder.create_ecc_add_gate({ x1, y1, x2, y2, x_temp, y_temp, /*sign_coefficient=*/1 });
198 builder.create_ecc_add_gate({ x_temp, y_temp, x3, y3, x_result, y_result, /*sign_coefficient=*/1 });
199
200 EXPECT_EQ(builder.blocks.elliptic.size(), 3UL); // 2 chained operations = 2 + (2 - 1) gates
201 EXPECT_TRUE(CircuitChecker::check(builder));
202}
203
204// Verifies that a chain of three operations (add-double-add) correctly reuses intermediate results.
205TEST_F(UltraCircuitBuilderElliptic, ChainedOperationsWithDouble)
206{
208
209 // Chain: p1 + p2 = temp1, then 2*temp1 = temp2, then temp2 + p3 = result
210 auto first_add = create_add_points(1, 2, true);
211 affine_element temp1 = first_add.result;
212
213 // Double temp1
214 affine_element temp2(element(temp1).dbl());
215
216 // Add p3 to temp2
218 affine_element result(element(temp2) + element(p3));
219
220 // Add variables for first operation (addition)
221 auto [x1, y1, x2, y2, x_temp1, y_temp1] = add_add_gate_variables(builder, first_add);
222
223 // Add variables for second operation (doubling)
224 uint32_t x_temp2 = builder.add_variable(temp2.x);
225 uint32_t y_temp2 = builder.add_variable(temp2.y);
226
227 // Add variables for third operation (addition)
228 uint32_t x3 = builder.add_variable(p3.x);
229 uint32_t y3 = builder.add_variable(p3.y);
230 uint32_t x_result = builder.add_variable(result.x);
231 uint32_t y_result = builder.add_variable(result.y);
232
233 builder.create_ecc_add_gate({ x1, y1, x2, y2, x_temp1, y_temp1, /*sign_coefficient=*/1 });
234 builder.create_ecc_dbl_gate({ x_temp1, y_temp1, x_temp2, y_temp2 });
235 builder.create_ecc_add_gate({ x_temp2, y_temp2, x3, y3, x_result, y_result, /*sign_coefficient=*/1 });
236
237 EXPECT_EQ(builder.blocks.elliptic.size(), 4UL); // 3 chained operations, 2 + (2 - 1) + (2 - 1) gates
238 EXPECT_TRUE(CircuitChecker::check(builder));
239}
240
241// Verifies that invalidating a middle operation in a chain causes circuit checker to fail
242TEST_F(UltraCircuitBuilderElliptic, ChainedOperationsDoubleFailure)
243{
245
246 // Chain: p1 + p2 = temp1, then 2*temp1 = temp2 (INVALID), then temp2 + p3 = result
247 auto first_add = create_add_points(1, 2, true);
248 affine_element temp1 = first_add.result;
249
250 // Double temp1
251 affine_element temp2(element(temp1).dbl());
252 temp2.x += bb::fr(1); // Invalidate the middle result
253
254 // Add p3 to (invalid) temp2
256 affine_element result(element(temp2) + element(p3));
257
258 // Add variables for first operation (addition - valid)
259 auto [x1, y1, x2, y2, x_temp1, y_temp1] = add_add_gate_variables(builder, first_add);
260
261 // Add variables for second operation (doubling - INVALID)
262 uint32_t x_temp2 = builder.add_variable(temp2.x);
263 uint32_t y_temp2 = builder.add_variable(temp2.y);
264
265 // Add variables for third operation (addition)
266 uint32_t x3 = builder.add_variable(p3.x);
267 uint32_t y3 = builder.add_variable(p3.y);
268 uint32_t x_result = builder.add_variable(result.x);
269 uint32_t y_result = builder.add_variable(result.y);
270
271 builder.create_ecc_add_gate({ x1, y1, x2, y2, x_temp1, y_temp1, /*sign_coefficient=*/1 });
272 builder.create_ecc_dbl_gate({ x_temp1, y_temp1, x_temp2, y_temp2 });
273 builder.create_ecc_add_gate({ x_temp2, y_temp2, x3, y3, x_result, y_result, /*sign_coefficient=*/1 });
274
275 EXPECT_EQ(builder.blocks.elliptic.size(), 4UL); // 3 chained operations, 2 + (2 - 1) + (2 - 1) gates
276 // Should fail because the middle operation (doubling) has an invalid result
277 EXPECT_FALSE(CircuitChecker::check(builder));
278}
static auto add_dbl_gate_variables(UltraCircuitBuilder &builder, const DoublingPoints &points)
static DoublingPoints create_dbl_points(uint64_t seed=1)
static AdditionPoints create_add_points(uint64_t seed1=1, uint64_t seed2=2, bool is_addition=true)
static auto add_add_gate_variables(UltraCircuitBuilder &builder, const AdditionPoints &points)
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
static AffineElement commit_native(const std::vector< Fq > &inputs, GeneratorContext context={})
Given a vector of fields, generate a pedersen commitment using the indexed generators.
Definition pedersen.cpp:24
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:33
group_elements::affine_element< Fq, Fr, Params > affine_element
Definition group.hpp:42
group_elements::element< Fq, Fr, Params > element
Definition group.hpp:41
AluTraceBuilder builder
Definition alu.test.cpp:124
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:174
TEST_F(UltraCircuitBuilderElliptic, Addition)