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(
171 updated_constraint, /*max_witness_index=*/static_cast<uint32_t>(updated_witness_values.size()) - 1);
172 AcirProgram program{ constraint_system, updated_witness_values };
173 auto builder = create_circuit<Builder>(program, Base::generate_metadata());
174
175 return { CircuitChecker::check(builder), builder.failed(), builder.err() };
176 }
177
186 template <typename Flavor> static std::vector<size_t> test_vk_independence()
187 {
190
191 std::vector<size_t> num_gates;
192
193 // Generate the constraint system
194 AcirConstraint valid_constraint;
195 WitnessVector valid_witness_values;
196 Base::generate_constraints(valid_constraint, valid_witness_values);
197
198 for (auto [predicate_case, label] :
200 vinfo("Testing vk independence for predicate case: ", label);
201
202 // Copy valid constraints and witness values
203 AcirConstraint constraint(valid_constraint);
204 WitnessVector witness_values(valid_witness_values);
205 // Update the constraint system based on the predicate mode
206 Predicate<InvalidWitnessTarget> predicate = { .test_case = predicate_case,
207 .invalid_witness = InvalidWitnessTarget::None };
208 auto [updated_constraint, updated_witness_values] =
209 update_witness_based_on_predicate(constraint, witness_values, predicate);
210
211 // Use the full ACIR flow
212 AcirFormat constraint_system = constraint_to_acir_format(
213 updated_constraint, /*max_witness_index=*/static_cast<uint32_t>(updated_witness_values.size()) - 1);
214 // Construct the vks
215 std::shared_ptr<VerificationKey> vk_from_witness;
216 {
217 AcirProgram program{ constraint_system, updated_witness_values };
218 auto builder = create_circuit<Builder>(program, Base::generate_metadata());
219 num_gates.emplace_back(builder.get_num_finalized_gates_inefficient());
220
221 auto prover_instance = std::make_shared<ProverInstance>(builder);
222 vk_from_witness = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
223
224 // Validate the builder
225 EXPECT_TRUE(CircuitChecker::check(builder));
226 EXPECT_FALSE(builder.failed());
227 }
228
229 std::shared_ptr<VerificationKey> vk_from_constraint;
230 {
231 AcirProgram program{ constraint_system, /*witness=*/{} };
232 auto builder = create_circuit<Builder>(program, Base::generate_metadata());
233 auto prover_instance = std::make_shared<ProverInstance>(builder);
234 vk_from_constraint = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
235 }
236
237 EXPECT_EQ(*vk_from_witness, *vk_from_constraint) << "Mismatch in the vks for predicate case " << label;
238 vinfo("VK independence passed for predicate case: ", label);
239 }
240
241 return num_gates;
242 }
243
255 static void test_constant_true(InvalidWitnessTarget default_invalid_witness_target)
256 {
257 // Generate the constraint system
258 AcirConstraint constraint;
259 WitnessVector witness_values;
260 Base::generate_constraints(constraint, witness_values);
261
262 // Constant true, no invalidation
263 {
264 auto [circuit_checker_result, builder_failed, _] = test_constraints(
265 constraint, witness_values, PredicateTestCase::ConstantTrue, InvalidWitnessTarget::None);
266 EXPECT_TRUE(circuit_checker_result) << "Circuit checker failed.";
267 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly.";
268 }
269
270 // Constant true, default invalidation
271 {
272 auto [circuit_checker_result, builder_failed, builder_err] = test_constraints(
273 constraint, witness_values, PredicateTestCase::ConstantTrue, default_invalid_witness_target);
274 // As `assert_equal` doesn't make the CircuitChecker fail, we need to check that either the CircuitChecker
275 // failed, or the builder error resulted from an assert_eq.
276 bool circuit_check_failed = !circuit_checker_result;
277 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
278 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
279 << "Circuit checker succeeded unexpectedly and no assert_eq failure.";
280 EXPECT_TRUE(builder_failed) << "Builder succeeded unexpectedly.";
281 }
282 }
283
295 static void test_witness_true(InvalidWitnessTarget default_invalid_witness_target)
296 {
297 // Generate the constraint system
298 AcirConstraint constraint;
299 WitnessVector witness_values;
300 Base::generate_constraints(constraint, witness_values);
301
302 // Witness true, no invalidation
303 {
304 auto [circuit_checker_result, builder_failed, _] = test_constraints(
305 constraint, witness_values, PredicateTestCase::WitnessTrue, InvalidWitnessTarget::None);
306 EXPECT_TRUE(circuit_checker_result) << "Circuit checker failed.";
307 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly.";
308 }
309
310 // Witness true, default invalidation
311 {
312 auto [circuit_checker_result, builder_failed, builder_err] = test_constraints(
313 constraint, witness_values, PredicateTestCase::WitnessTrue, default_invalid_witness_target);
314 // As `assert_equal` doesn't make the CircuitChecker fail, we need to check that either the CircuitChecker
315 // failed, or the builder error resulted from an assert_eq.
316 bool circuit_check_failed = !circuit_checker_result;
317 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
318 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
319 << "Circuit checker succeeded unexpectedly and no assert_eq failure.";
320 EXPECT_TRUE(builder_failed) << "Builder succeeded unexpectedly.";
321 }
322 }
323
331 static void test_witness_false()
332 {
333 // Generate the constraint system
334 AcirConstraint constraint;
335 WitnessVector witness_values;
336 Base::generate_constraints(constraint, witness_values);
337
338 for (auto [invalid_witness_target, target_label] :
339 zip_view(InvalidWitness::get_all(), InvalidWitness::get_labels())) {
340 vinfo("Testing invalid witness target: ", target_label);
341
342 auto [circuit_checker_result, builder_failed, _] =
343 test_constraints(constraint, witness_values, PredicateTestCase::WitnessFalse, invalid_witness_target);
344
345 EXPECT_TRUE(circuit_checker_result) << "Check builder failed for invalid witness target " + target_label;
346 EXPECT_FALSE(builder_failed) << "Builder failed for invalid witness target " + target_label;
347 vinfo("Passed invalid witness target: ", target_label);
348 }
349 }
350
364 {
365 // Generate the constraint system
366 AcirConstraint constraint;
367 WitnessVector witness_values;
368 Base::generate_constraints(constraint, witness_values);
369
370 for (auto [invalid_witness_target, target_label] :
371 zip_view(InvalidWitness::get_all(), InvalidWitness::get_labels())) {
372 vinfo("Testing invalid witness target: ", target_label);
373
374 auto [circuit_checker_result, builder_failed, _] =
375 test_constraints(constraint, witness_values, PredicateTestCase::WitnessFalse, invalid_witness_target);
376
377 EXPECT_TRUE(circuit_checker_result) << "Check builder failed for invalid witness target " + target_label;
378 EXPECT_FALSE(builder_failed) << "Builder failed for invalid witness target " + target_label;
379 vinfo("Passed invalid witness target: ", target_label);
380
381 // Only validate witness true failure for actual invalidation targets (skip None)
382 if (invalid_witness_target != InvalidWitnessTarget::None) {
383 // Check that the same configuration would have failed if the predicate was witness true
384 auto [circuit_checker_result, builder_failed, builder_err] = test_constraints(
385 constraint, witness_values, PredicateTestCase::WitnessTrue, invalid_witness_target);
386
387 // As `assert_equal` doesn't make the CircuitChecker fail, we need to check that either the
388 // CircuitChecker failed, or the builder error resulted from an assert_eq.
389 bool circuit_check_failed = !circuit_checker_result;
390 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
391 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
392 << "Circuit checker succeeded unexpectedly and no assert_eq failure for invalid witness target " +
393 target_label;
394 EXPECT_TRUE(builder_failed) << "Builder succeeded for invalid witness target " + target_label;
395 vinfo("Passed invalid witness target (witness true confirmation): ", target_label);
396 }
397 }
398 }
399
414 static std::vector<std::string> test_invalid_witnesses()
415 {
416 std::vector<std::string> error_msgs;
417
418 // Generate the constraint system
419 AcirConstraint constraint;
420 WitnessVector witness_values;
421 Base::generate_constraints(constraint, witness_values);
422
423 for (auto [predicate_case, predicate_label] :
425 for (auto [target, label] : zip_view(InvalidWitness::get_all(), InvalidWitness::get_labels())) {
426 auto [circuit_checker_result, builder_failed, builder_err] =
427 test_constraints(constraint, witness_values, predicate_case, target);
428 error_msgs.emplace_back(builder_err);
429
430 if (predicate_case != PredicateTestCase::WitnessFalse) {
431 // If the predicate is not witness false, invalid witnesses should cause failure
432 if (target != InvalidWitnessTarget::None) {
433 bool circuit_check_failed = !circuit_checker_result;
434 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
435 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
436 << "Circuit checker succeeded unexpectedly and no assert_eq failure for invalid witness "
437 "target " +
438 label + " with predicate " + predicate_label;
439 EXPECT_TRUE(builder_failed) << "Builder succeeded unexpectedly for invalid witness target " +
440 label + " with predicate " + predicate_label;
441 } else {
442 EXPECT_TRUE(circuit_checker_result)
443 << "Circuit checker failed unexpectedly for invalid witness target " + label +
444 " with predicate " + predicate_label;
445 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly for invalid witness target " +
446 label + " with predicate " + predicate_label;
447 }
448 } else {
449 // If the predicate is witness false, all cases should succeed
450 EXPECT_TRUE(circuit_checker_result) << "Circuit checker failed unexpectedly for invalid witness "
451 "target " +
452 label + " with predicate " + predicate_label;
453 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly for invalid witness target " + label +
454 " with predicate " + predicate_label;
455 }
456 }
457 }
458 return error_msgs;
459 }
460};
461
462} // 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.
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
Base Native verification key class.
Definition flavor.hpp:141
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, uint32_t varnum)
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)