Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
test_class_predicate.hpp
Go to the documentation of this file.
1#pragma once
2
6#include "gtest/gtest.h"
7#include <vector>
8
9namespace acir_format {
10
11using namespace bb;
12using namespace bb::stdlib;
13
20
21template <typename InvalidWitnessTarget> struct Predicate {
23 InvalidWitnessTarget invalid_witness;
24
29
30 std::vector<std::string> static get_labels() { return { "ConstantTrue", "WitnessTrue", "WitnessFalse" }; }
31};
32
45template <typename T>
46concept TestBaseWithPredicate = requires {
47 // Required type aliases
48 typename T::Builder;
49 typename T::InvalidWitness;
50 typename T::InvalidWitness::Target;
51 typename T::AcirConstraint;
52
53 // Ensure InvalidWitness::Target is an enum
55
56 // Ensure that InvalidWitness::Target has a None value
57 { T::InvalidWitness::Target::None };
58
59 // InvalidWitness must provide static methods for test iteration
60 { T::InvalidWitness::get_all() } -> std::same_as<std::vector<typename T::InvalidWitness::Target>>;
61 { T::InvalidWitness::get_labels() } -> std::same_as<std::vector<std::string>>;
62
63 // Required static constraint manipulation methods
64 requires requires(typename T::AcirConstraint constraint,
65 WitnessVector witness_values,
66 const typename T::InvalidWitness::Target& invalid_witness_target) {
77 {
78 T::invalidate_witness(constraint, witness_values, invalid_witness_target)
80 };
81
82 requires requires(typename T::AcirConstraint& constraint, WitnessVector& witness_values) {
87 { T::generate_constraints(constraint, witness_values) } -> std::same_as<void>;
88
93 { T::generate_metadata() } -> std::same_as<ProgramMetadata>;
94 };
95};
96
100template <TestBaseWithPredicate Base_> class TestClassWithPredicate {
101 public:
102 using Base = Base_;
103 using Builder = Base::Builder;
104 using InvalidWitness = Base::InvalidWitness;
105 using InvalidWitnessTarget = InvalidWitness::Target;
106 using AcirConstraint = Base::AcirConstraint;
107
120 const AcirConstraint& constraint,
121 const WitnessVector& witness_values,
123 {
124 // Apply witness invalidation for all cases
125 auto [invalid_constraint, invalid_witness_values] =
126 Base::invalidate_witness(constraint, witness_values, mode.invalid_witness);
127
128 switch (mode.test_case) {
130 invalid_constraint.predicate = WitnessOrConstant<bb::fr>::from_constant(bb::fr(1));
131 invalid_witness_values.pop_back();
132 break;
134 // Nothing to do - keep default witness predicate
135 break;
137 invalid_witness_values[invalid_constraint.predicate.index] = bb::fr(0);
138 break;
139 }
140
141 return { invalid_constraint, invalid_witness_values };
142 }
143
160 WitnessVector& witness_values,
161 const PredicateTestCase& test_case,
162 const InvalidWitnessTarget& invalid_witness_target)
163 {
164 Predicate<InvalidWitnessTarget> predicate = { .test_case = test_case,
165 .invalid_witness = invalid_witness_target };
166 auto [updated_constraint, updated_witness_values] =
167 update_witness_based_on_predicate(constraint, witness_values, predicate);
168
169 // Use the full ACIR flow: constraint -> Acir::Opcode -> Acir::Circuit -> circuit_serde_to_acir_format
170 AcirFormat constraint_system = constraint_to_acir_format(updated_constraint);
171 AcirProgram program{ constraint_system, updated_witness_values };
172 auto builder = create_circuit<Builder>(program, Base::generate_metadata());
173
174 return { CircuitChecker::check(builder), builder.failed(), builder.err() };
175 }
176
185 template <typename Flavor> static std::vector<size_t> test_vk_independence()
186 {
189
190 std::vector<size_t> num_gates;
191
192 // Generate the constraint system
193 AcirConstraint valid_constraint;
194 WitnessVector valid_witness_values;
195 Base::generate_constraints(valid_constraint, valid_witness_values);
196
197 for (auto [predicate_case, label] :
199 vinfo("Testing vk independence for predicate case: ", label);
200
201 // Copy valid constraints and witness values
202 AcirConstraint constraint(valid_constraint);
203 WitnessVector witness_values(valid_witness_values);
204 // Update the constraint system based on the predicate mode
205 Predicate<InvalidWitnessTarget> predicate = { .test_case = predicate_case,
206 .invalid_witness = InvalidWitnessTarget::None };
207 auto [updated_constraint, updated_witness_values] =
208 update_witness_based_on_predicate(constraint, witness_values, predicate);
209
210 // Use the full ACIR flow
211 AcirFormat constraint_system = constraint_to_acir_format(updated_constraint);
212 // Construct the vks
213 std::shared_ptr<VerificationKey> vk_from_witness;
214 {
215 AcirProgram program{ constraint_system, updated_witness_values };
216 auto builder = create_circuit<Builder>(program, Base::generate_metadata());
217 num_gates.emplace_back(builder.get_num_finalized_gates_inefficient());
218
219 auto prover_instance = std::make_shared<ProverInstance>(builder);
220 vk_from_witness = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
221
222 // Validate the builder
223 EXPECT_TRUE(CircuitChecker::check(builder));
224 EXPECT_FALSE(builder.failed());
225 }
226
227 std::shared_ptr<VerificationKey> vk_from_constraint;
228 {
229 AcirProgram program{ constraint_system, /*witness=*/{} };
230 auto builder = create_circuit<Builder>(program, Base::generate_metadata());
231 auto prover_instance = std::make_shared<ProverInstance>(builder);
232 vk_from_constraint = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
233 }
234
235 EXPECT_EQ(*vk_from_witness, *vk_from_constraint) << "Mismatch in the vks for predicate case " << label;
236 vinfo("VK independence passed for predicate case: ", label);
237 }
238
239 return num_gates;
240 }
241
253 static void test_constant_true(InvalidWitnessTarget default_invalid_witness_target)
254 {
255 // Generate the constraint system
256 AcirConstraint constraint;
257 WitnessVector witness_values;
258 Base::generate_constraints(constraint, witness_values);
259
260 // Constant true, no invalidation
261 {
262 auto [circuit_checker_result, builder_failed, _] = test_constraints(
263 constraint, witness_values, PredicateTestCase::ConstantTrue, InvalidWitnessTarget::None);
264 EXPECT_TRUE(circuit_checker_result) << "Circuit checker failed.";
265 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly.";
266 }
267
268 // Constant true, default invalidation
269 {
270 auto [circuit_checker_result, builder_failed, builder_err] = test_constraints(
271 constraint, witness_values, PredicateTestCase::ConstantTrue, default_invalid_witness_target);
272 // As `assert_equal` doesn't make the CircuitChecker fail, we need to check that either the CircuitChecker
273 // failed, or the builder error resulted from an assert_eq.
274 bool circuit_check_failed = !circuit_checker_result;
275 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
276 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
277 << "Circuit checker succeeded unexpectedly and no assert_eq failure.";
278 EXPECT_TRUE(builder_failed) << "Builder succeeded unexpectedly.";
279 }
280 }
281
293 static void test_witness_true(InvalidWitnessTarget default_invalid_witness_target)
294 {
295 // Generate the constraint system
296 AcirConstraint constraint;
297 WitnessVector witness_values;
298 Base::generate_constraints(constraint, witness_values);
299
300 // Witness true, no invalidation
301 {
302 auto [circuit_checker_result, builder_failed, _] = test_constraints(
303 constraint, witness_values, PredicateTestCase::WitnessTrue, InvalidWitnessTarget::None);
304 EXPECT_TRUE(circuit_checker_result) << "Circuit checker failed.";
305 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly.";
306 }
307
308 // Witness true, default invalidation
309 {
310 auto [circuit_checker_result, builder_failed, builder_err] = test_constraints(
311 constraint, witness_values, PredicateTestCase::WitnessTrue, default_invalid_witness_target);
312 // As `assert_equal` doesn't make the CircuitChecker fail, we need to check that either the CircuitChecker
313 // failed, or the builder error resulted from an assert_eq.
314 bool circuit_check_failed = !circuit_checker_result;
315 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
316 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
317 << "Circuit checker succeeded unexpectedly and no assert_eq failure.";
318 EXPECT_TRUE(builder_failed) << "Builder succeeded unexpectedly.";
319 }
320 }
321
329 static void test_witness_false()
330 {
331 // Generate the constraint system
332 AcirConstraint constraint;
333 WitnessVector witness_values;
334 Base::generate_constraints(constraint, witness_values);
335
336 for (auto [invalid_witness_target, target_label] :
337 zip_view(InvalidWitness::get_all(), InvalidWitness::get_labels())) {
338 vinfo("Testing invalid witness target: ", target_label);
339
340 auto [circuit_checker_result, builder_failed, _] =
341 test_constraints(constraint, witness_values, PredicateTestCase::WitnessFalse, invalid_witness_target);
342
343 EXPECT_TRUE(circuit_checker_result) << "Check builder failed for invalid witness target " + target_label;
344 EXPECT_FALSE(builder_failed) << "Builder failed for invalid witness target " + target_label;
345 vinfo("Passed invalid witness target: ", target_label);
346 }
347 }
348
362 {
363 // Generate the constraint system
364 AcirConstraint constraint;
365 WitnessVector witness_values;
366 Base::generate_constraints(constraint, witness_values);
367
368 for (auto [invalid_witness_target, target_label] :
369 zip_view(InvalidWitness::get_all(), InvalidWitness::get_labels())) {
370 vinfo("Testing invalid witness target: ", target_label);
371
372 auto [circuit_checker_result, builder_failed, _] =
373 test_constraints(constraint, witness_values, PredicateTestCase::WitnessFalse, invalid_witness_target);
374
375 EXPECT_TRUE(circuit_checker_result) << "Check builder failed for invalid witness target " + target_label;
376 EXPECT_FALSE(builder_failed) << "Builder failed for invalid witness target " + target_label;
377 vinfo("Passed invalid witness target: ", target_label);
378
379 // Only validate witness true failure for actual invalidation targets (skip None)
380 if (invalid_witness_target != InvalidWitnessTarget::None) {
381 // Check that the same configuration would have failed if the predicate was witness true
382 auto [circuit_checker_result, builder_failed, builder_err] = test_constraints(
383 constraint, witness_values, PredicateTestCase::WitnessTrue, invalid_witness_target);
384
385 // As `assert_equal` doesn't make the CircuitChecker fail, we need to check that either the
386 // CircuitChecker failed, or the builder error resulted from an assert_eq.
387 bool circuit_check_failed = !circuit_checker_result;
388 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
389 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
390 << "Circuit checker succeeded unexpectedly and no assert_eq failure for invalid witness target " +
391 target_label;
392 EXPECT_TRUE(builder_failed) << "Builder succeeded for invalid witness target " + target_label;
393 vinfo("Passed invalid witness target (witness true confirmation): ", target_label);
394 }
395 }
396 }
397
412 static std::vector<std::string> test_invalid_witnesses()
413 {
414 std::vector<std::string> error_msgs;
415
416 // Generate the constraint system
417 AcirConstraint constraint;
418 WitnessVector witness_values;
419 Base::generate_constraints(constraint, witness_values);
420
421 for (auto [predicate_case, predicate_label] :
423 for (auto [target, label] : zip_view(InvalidWitness::get_all(), InvalidWitness::get_labels())) {
424 auto [circuit_checker_result, builder_failed, builder_err] =
425 test_constraints(constraint, witness_values, predicate_case, target);
426 error_msgs.emplace_back(builder_err);
427
428 if (predicate_case != PredicateTestCase::WitnessFalse) {
429 // If the predicate is not witness false, invalid witnesses should cause failure
430 if (target != InvalidWitnessTarget::None) {
431 bool circuit_check_failed = !circuit_checker_result;
432 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
433 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
434 << "Circuit checker succeeded unexpectedly and no assert_eq failure for invalid witness "
435 "target " +
436 label + " with predicate " + predicate_label;
437 EXPECT_TRUE(builder_failed) << "Builder succeeded unexpectedly for invalid witness target " +
438 label + " with predicate " + predicate_label;
439 } else {
440 EXPECT_TRUE(circuit_checker_result)
441 << "Circuit checker failed unexpectedly for invalid witness target " + label +
442 " with predicate " + predicate_label;
443 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly for invalid witness target " +
444 label + " with predicate " + predicate_label;
445 }
446 } else {
447 // If the predicate is witness false, all cases should succeed
448 EXPECT_TRUE(circuit_checker_result) << "Circuit checker failed unexpectedly for invalid witness "
449 "target " +
450 label + " with predicate " + predicate_label;
451 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly for invalid witness target " + label +
452 " with predicate " + predicate_label;
453 }
454 }
455 }
456 return error_msgs;
457 }
458};
459
460} // namespace acir_format
Test class for ACIR constraints that contain a predicate.
static void test_witness_false()
Test all invalid witness cases for the witness false predicate case.
static std::vector< std::string > test_invalid_witnesses()
Test all invalid witness targets across all predicate cases (comprehensive matrix test).
static void test_constant_true(InvalidWitnessTarget default_invalid_witness_target)
Test all cases in which the predicate is a constant holding the value true.
static std::pair< AcirConstraint, WitnessVector > update_witness_based_on_predicate(const AcirConstraint &constraint, const WitnessVector &witness_values, const Predicate< InvalidWitnessTarget > &mode)
Update the constraint and the witness based on the predicate.
static void test_witness_false_slow()
Test all invalid witness cases for the witness false predicate case (slow comprehensive version).
static std::vector< size_t > test_vk_independence()
Test vk generation is independent of the witness values supplied.
static std::tuple< bool, bool, std::string > test_constraints(AcirConstraint &constraint, WitnessVector &witness_values, const PredicateTestCase &test_case, const InvalidWitnessTarget &invalid_witness_target)
General purpose testing function. It tests the contraints based on the predicate and invalidation tar...
static void test_witness_true(InvalidWitnessTarget default_invalid_witness_target)
Test all cases in which the predicate is a witness holding the value true.
Base Native verification key class.
Definition flavor.hpp:172
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.
#define vinfo(...)
Definition log.hpp:94
Concept defining the requirements for the Base template parameter of TestClassWithPredicate.
AluTraceBuilder builder
Definition alu.test.cpp:124
AcirFormat constraint_to_acir_format(const ConstraintType &constraint)
Convert an AcirConstraint (single or vector) to AcirFormat by going through the full ACIR serde flow.
std::vector< bb::fr > WitnessVector
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:174
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Barretenberg's representation of ACIR constraints.
Struct containing both the constraints to be added to the circuit and the witness vector.
static std::vector< std::string > get_labels()
static std::vector< PredicateTestCase > get_all()
InvalidWitnessTarget invalid_witness
static WitnessOrConstant from_constant(FF value)