Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ec_operations.test.cpp
Go to the documentation of this file.
1#include "ec_operations.hpp"
2#include "acir_format.hpp"
3
7
8#include <gtest/gtest.h>
9#include <vector>
10
11using namespace ::acir_format;
12
13enum class InputConstancy : uint8_t { None, Input1, Input2, Both };
14
24template <typename Builder_, InputConstancy Constancy> class EcOperationsTestingFunctions {
25 public:
26 using Builder = Builder_;
27 using AcirConstraint = EcAdd;
30 using FF = bb::fr;
31
33 public:
34 enum class Target : uint8_t {
35 None,
36 Input1, // Invalidate first input point
37 Input2, // Invalidate second input point
38 Result // Invalidate result output
39 };
40
45
46 static std::vector<std::string> get_labels() { return { "None", "Input1", "Input2", "Result" }; }
47 };
48
49 static ProgramMetadata generate_metadata() { return ProgramMetadata{}; }
50
51 static void generate_constraints(AcirConstraint& ec_add_constraint, WitnessVector& witness_values)
52 {
53 // Generate points on Grumpkin
56 GrumpkinPoint result = input1 + input2;
57 BB_ASSERT(result != GrumpkinPoint::one()); // Ensure that tampering works correctly
58
59 // Helper to add a point: either as witness or constant
60 auto construct_point = [&](const GrumpkinPoint& point, bool as_constant) -> std::vector<WitnessOrConstant<FF>> {
61 if (as_constant) {
62 // Point is constant
63 return { WitnessOrConstant<FF>::from_constant(point.x), WitnessOrConstant<FF>::from_constant(point.y) };
64 }
65 // Point is witness
66 std::vector<uint32_t> point_indices = add_to_witness_and_track_indices(witness_values, point);
67 return { WitnessOrConstant<FF>::from_index(point_indices[0]),
68 WitnessOrConstant<FF>::from_index(point_indices[1]) };
69 };
70
71 // Determine which inputs are constants based on the Constancy template parameter
72 constexpr bool input1_is_constant = (Constancy == InputConstancy::Input1 || Constancy == InputConstancy::Both);
73 constexpr bool input2_is_constant = (Constancy == InputConstancy::Input2 || Constancy == InputConstancy::Both);
74
75 // Add inputs according to constancy template parameter
76 auto input1_fields = construct_point(input1, input1_is_constant);
77 auto input2_fields = construct_point(input2, input2_is_constant);
78
79 // Construct result and predicate as witnesses
80 std::vector<uint32_t> result_indices = add_to_witness_and_track_indices(witness_values, result);
81 uint32_t predicate_index = add_to_witness_and_track_indices(witness_values, FF(1));
82
83 // Build the constraint
84 ec_add_constraint = EcAdd{
85 .input1_x = input1_fields[0],
86 .input1_y = input1_fields[1],
87 .input2_x = input2_fields[0],
88 .input2_y = input2_fields[1],
89 .predicate = WitnessOrConstant<FF>::from_index(predicate_index),
90 .result_x = result_indices[0],
91 .result_y = result_indices[1],
92 };
93 }
94
96 AcirConstraint constraint, WitnessVector witness_values, const InvalidWitness::Target& invalid_witness_target)
97 {
98 switch (invalid_witness_target) {
100 // Invalidate the first input by adding 1 to x coordinate
101 if constexpr (Constancy == InputConstancy::None || Constancy == InputConstancy::Input2) {
102 witness_values[constraint.input1_x.index] += bb::fr(1);
103 } else {
104 constraint.input1_x = WitnessOrConstant<FF>::from_constant(constraint.input1_x.value + bb::fr(1));
105 }
106 break;
107 }
109 // Invalidate the second input by adding 1 to x coordinate
110 if constexpr (Constancy == InputConstancy::None || Constancy == InputConstancy::Input1) {
111 witness_values[constraint.input2_x.index] += bb::fr(1);
112 } else {
113 constraint.input2_x = WitnessOrConstant<FF>::from_constant(constraint.input2_x.value + bb::fr(1));
114 }
115 break;
116 }
118 // Tamper with the result (always a witness) by setting it to the generator point
119 witness_values[constraint.result_x] = GrumpkinPoint::one().x;
120 witness_values[constraint.result_y] = GrumpkinPoint::one().y;
121 break;
122 }
124 default:
125 break;
126 }
127
128 return { constraint, witness_values };
129 };
130};
131
132template <typename Builder>
134 : public ::testing::Test,
135 public TestClassWithPredicate<EcOperationsTestingFunctions<Builder, InputConstancy::None>> {
136 protected:
138};
139
140template <typename Builder>
142 : public ::testing::Test,
143 public TestClassWithPredicate<EcOperationsTestingFunctions<Builder, InputConstancy::Input1>> {
144 protected:
146};
147
148template <typename Builder>
150 : public ::testing::Test,
151 public TestClassWithPredicate<EcOperationsTestingFunctions<Builder, InputConstancy::Input2>> {
152 protected:
154};
155
156template <typename Builder>
158 : public ::testing::Test,
159 public TestClassWithPredicate<EcOperationsTestingFunctions<Builder, InputConstancy::Both>> {
160 protected:
162};
163
164using BuilderTypes = testing::Types<UltraCircuitBuilder, MegaCircuitBuilder>;
165
170
171TYPED_TEST(EcOperationsTestsNoneConstant, GenerateVKFromConstraints)
172{
174 TestFixture::template test_vk_independence<Flavor>();
175}
176
178{
180 TestFixture::test_constant_true(TestFixture::InvalidWitnessTarget::Result);
181}
182
184{
186 TestFixture::test_witness_true(TestFixture::InvalidWitnessTarget::Result);
187}
188
190{
192 TestFixture::test_witness_false_slow();
193}
194
196{
198 [[maybe_unused]] std::vector<std::string> _ = TestFixture::test_invalid_witnesses();
199}
200
202{
204 TestFixture::template test_vk_independence<Flavor>();
205}
206
208{
210 TestFixture::test_constant_true(TestFixture::InvalidWitnessTarget::Result);
211}
212
214{
216 TestFixture::test_witness_true(TestFixture::InvalidWitnessTarget::Result);
217}
218
220{
222 TestFixture::test_witness_false_slow();
223}
224
226{
228 [[maybe_unused]] std::vector<std::string> _ = TestFixture::test_invalid_witnesses();
229}
230
232{
234 TestFixture::template test_vk_independence<Flavor>();
235}
236
238{
240 TestFixture::test_constant_true(TestFixture::InvalidWitnessTarget::Result);
241}
242
244{
246 TestFixture::test_witness_true(TestFixture::InvalidWitnessTarget::Result);
247}
248
250{
252 TestFixture::test_witness_false_slow();
253}
254
256{
258 [[maybe_unused]] std::vector<std::string> _ = TestFixture::test_invalid_witnesses();
259}
260
261TYPED_TEST(EcOperationsTestsBothConstant, GenerateVKFromConstraints)
262{
264 TestFixture::template test_vk_independence<Flavor>();
265}
266
268{
270 TestFixture::test_constant_true(TestFixture::InvalidWitnessTarget::Result);
271}
272
274{
276 TestFixture::test_witness_true(TestFixture::InvalidWitnessTarget::Result);
277}
278
280{
282 TestFixture::test_witness_false_slow();
283}
284
286{
288 [[maybe_unused]] std::vector<std::string> _ = TestFixture::test_invalid_witnesses();
289}
290
291// ============================================================
292// Infinity tests: the point at infinity is encoded as (0, 0).
293// ============================================================
295using FF = bb::fr;
296
297struct AcirPoint {
299 static AcirPoint from_native(const GrumpkinPoint& p) { return { p.x, p.y }; }
300 static AcirPoint infinity() { return { FF(0), FF(0) }; }
301};
302
303template <typename Builder> class EcOperationsInfinityTests : public ::testing::Test {
304 protected:
306
307 // Push an AcirPoint to the witness vector and return the [x, y] indices.
308 static std::array<uint32_t, 2> push_point(WitnessVector& witness, const AcirPoint& pt)
309 {
310 uint32_t xi = static_cast<uint32_t>(witness.size());
311 witness.emplace_back(pt.x);
312 uint32_t yi = static_cast<uint32_t>(witness.size());
313 witness.emplace_back(pt.y);
314 return { xi, yi };
315 }
316
317 // Build an EcAdd constraint (predicate=1) from three ACIR points.
318 // Returns the constraint and the populated witness vector.
320 {
321 WitnessVector witness;
322 auto i1 = push_point(witness, p1);
323 auto i2 = push_point(witness, p2);
324 auto r = push_point(witness, result);
325 uint32_t pred_idx = static_cast<uint32_t>(witness.size());
326 witness.emplace_back(FF(1));
327
328 EcAdd c{
329 .input1_x = WitnessOrConstant<FF>::from_index(i1[0]),
330 .input1_y = WitnessOrConstant<FF>::from_index(i1[1]),
331 .input2_x = WitnessOrConstant<FF>::from_index(i2[0]),
332 .input2_y = WitnessOrConstant<FF>::from_index(i2[1]),
333 .predicate = WitnessOrConstant<FF>::from_index(pred_idx),
334 .result_x = r[0],
335 .result_y = r[1],
336 };
337 return { c, witness };
338 }
339
340 // Run the circuit and return (satisfied, error_string).
341 static std::pair<bool, std::string> run_circuit(EcAdd constraint, WitnessVector witness)
342 {
343 AcirFormat cs = constraint_to_acir_format(constraint);
344 AcirProgram program{ cs, witness };
345 auto builder = create_circuit<Builder>(program, ProgramMetadata{});
346 bool ok = CircuitChecker::check(builder) && !builder.failed();
347 return { ok, builder.err() };
348 }
349};
350
352
353// P + (-P) = (0, 0): valid circuit.
355{
358 auto [constraint, witness] =
359 TestFixture::make_ec_add(AcirPoint::from_native(p), AcirPoint::from_native(-p), AcirPoint::infinity());
360 auto [ok, err] = TestFixture::run_circuit(constraint, witness);
361 EXPECT_TRUE(ok) << "P + (-P) = infinity should produce a valid circuit";
362}
363
364// Input point at infinity (∞ + P = P): valid circuit.
366{
369 auto [constraint, witness] =
370 TestFixture::make_ec_add(AcirPoint::infinity(), AcirPoint::from_native(p), AcirPoint::from_native(p));
371
372 auto [ok, err] = TestFixture::run_circuit(constraint, witness);
373 EXPECT_TRUE(ok) << "infinity + P = P should produce a valid circuit";
374}
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
static std::array< uint32_t, 2 > push_point(WitnessVector &witness, const AcirPoint &pt)
static std::pair< bool, std::string > run_circuit(EcAdd constraint, WitnessVector witness)
static std::pair< EcAdd, WitnessVector > make_ec_add(AcirPoint p1, AcirPoint p2, AcirPoint result)
static std::vector< std::string > get_labels()
Testing functions to generate the EcOperationTest test suite. Constancy specifies which inputs to the...
static ProgramMetadata generate_metadata()
static void generate_constraints(AcirConstraint &ec_add_constraint, WitnessVector &witness_values)
static std::pair< AcirConstraint, WitnessVector > invalidate_witness(AcirConstraint constraint, WitnessVector witness_values, const InvalidWitness::Target &invalid_witness_target)
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
static affine_element random_element(numeric::RNG *engine=nullptr) noexcept
Samples a random point on the curve.
static constexpr affine_element one() noexcept
group class. Represents an elliptic curve group element. Group is parametrised by Fq and Fr
Definition group.hpp:38
group_elements::affine_element< Fq, Fr, Params > affine_element
Definition group.hpp:44
AluTraceBuilder builder
Definition alu.test.cpp:124
bb::fr FF
TYPED_TEST_SUITE(EcOperationsTestsNoneConstant, BuilderTypes)
TYPED_TEST(EcOperationsTestsNoneConstant, GenerateVKFromConstraints)
bb::group< bb::fr, bb::fq, G1Params > g1
Definition grumpkin.hpp:46
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
field< Bn254FrParams > fr
Definition fr.hpp:155
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
::testing::Types< UltraCircuitBuilder, MegaCircuitBuilder > BuilderTypes
static AcirPoint from_native(const GrumpkinPoint &p)
static AcirPoint infinity()