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// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#pragma once
8
13#include "gtest/gtest.h"
14#include <vector>
15
16namespace acir_format {
17
18using namespace bb;
19using namespace bb::stdlib;
20
27
28template <typename InvalidWitnessTarget> struct Predicate {
30 InvalidWitnessTarget invalid_witness;
31
36
37 std::vector<std::string> static get_labels() { return { "ConstantTrue", "WitnessTrue", "WitnessFalse" }; }
38};
39
52template <typename T>
53concept TestBaseWithPredicate = requires {
54 // Required type aliases
55 typename T::Builder;
56 typename T::InvalidWitness;
57 typename T::InvalidWitness::Target;
58 typename T::AcirConstraint;
59
60 // Ensure InvalidWitness::Target is an enum
62
63 // Ensure that InvalidWitness::Target has a None value
64 { T::InvalidWitness::Target::None };
65
66 // InvalidWitness must provide static methods for test iteration
67 { T::InvalidWitness::get_all() } -> std::same_as<std::vector<typename T::InvalidWitness::Target>>;
68 { T::InvalidWitness::get_labels() } -> std::same_as<std::vector<std::string>>;
69
70 // Required static constraint manipulation methods
71 requires requires(typename T::AcirConstraint& constraint,
72 WitnessVector& witness_values,
73 const typename T::InvalidWitness::Target& invalid_witness_target) {
82 { T::invalidate_witness(constraint, witness_values, invalid_witness_target) } -> std::same_as<void>;
83
88 { T::generate_constraints(constraint, witness_values) } -> std::same_as<void>;
89 };
90};
91
95template <TestBaseWithPredicate Base> class TestClassWithPredicate {
96 public:
97 using Builder = Base::Builder;
98 using InvalidWitness = Base::InvalidWitness;
99 using InvalidWitnessTarget = InvalidWitness::Target;
100 using AcirConstraint = Base::AcirConstraint;
101
112 WitnessVector& witness_values,
114 {
115 switch (mode.test_case) {
117 constraint.predicate = WitnessOrConstant<bb::fr>::from_constant(bb::fr(1));
118 witness_values.pop_back();
119 break;
121 // Nothing to do - keep default witness predicate
122 break;
124 witness_values[constraint.predicate.index] = bb::fr(0);
125 break;
126 }
127 // Apply witness invalidation for all cases
128 Base::invalidate_witness(constraint, witness_values, mode.invalid_witness);
129 }
130
135 {
136 AcirConstraint constraint;
137 WitnessVector witness_values;
138 Base::generate_constraints(constraint, witness_values);
139 update_witness_based_on_predicate(constraint, witness_values, mode);
140
141 return { constraint, witness_values };
142 }
143
159 const InvalidWitnessTarget& invalid_witness_target)
160 {
161 Predicate<InvalidWitnessTarget> predicate = { .test_case = test_case,
162 .invalid_witness = invalid_witness_target };
163 auto [constraint, witness_values] = generate_constraints(predicate);
164
165 // Use the full ACIR flow: constraint -> Acir::Opcode -> Acir::Circuit -> circuit_serde_to_acir_format
166 AcirFormat constraint_system = constraint_to_acir_format(
167 constraint, /*max_witness_index=*/static_cast<uint32_t>(witness_values.size()) - 1);
168
169 AcirProgram program{ constraint_system, witness_values };
170 auto builder = create_circuit<Builder>(program);
171
172 return { CircuitChecker::check(builder), builder.failed(), builder.err() };
173 }
174
183 template <typename Flavor> static std::vector<size_t> test_vk_independence()
184 {
187
188 std::vector<size_t> num_gates;
189
190 for (auto [predicate_case, label] :
192 vinfo("Testing vk independence for predicate case: ", label);
193
194 // Generate the constraint system
195 Predicate<InvalidWitnessTarget> predicate = { .test_case = predicate_case,
196 .invalid_witness = InvalidWitnessTarget::None };
197 auto [constraint, witness_values] = generate_constraints(predicate);
198
199 // Use the full ACIR flow
200 AcirFormat constraint_system = constraint_to_acir_format(
201 constraint, /*max_witness_index=*/static_cast<uint32_t>(witness_values.size()) - 1);
202
203 // Construct the vks
204 std::shared_ptr<VerificationKey> vk_from_witness;
205 {
206 AcirProgram program{ constraint_system, witness_values };
207 auto builder = create_circuit<Builder>(program);
208 num_gates.emplace_back(builder.get_num_finalized_gates_inefficient());
209
210 auto prover_instance = std::make_shared<ProverInstance>(builder);
211 vk_from_witness = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
212
213 // Validate the builder
214 EXPECT_TRUE(CircuitChecker::check(builder));
215 }
216
217 std::shared_ptr<VerificationKey> vk_from_constraint;
218 {
219 AcirProgram program{ constraint_system, /*witness=*/{} };
220 auto builder = create_circuit<Builder>(program);
221 auto prover_instance = std::make_shared<ProverInstance>(builder);
222 vk_from_constraint = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
223 }
224
225 EXPECT_EQ(*vk_from_witness, *vk_from_constraint) << "Mismatch in the vks for predicate case " << label;
226 vinfo("VK independence passed for predicate case: ", label);
227 }
228
229 return num_gates;
230 }
231
243 static void test_constant_true(InvalidWitnessTarget default_invalid_witness_target)
244 {
245 // Constant true, no invalidation
246 {
247 auto [circuit_checker_result, builder_failed, _] =
248 test_constraints(PredicateTestCase::ConstantTrue, InvalidWitnessTarget::None);
249 EXPECT_TRUE(circuit_checker_result) << "Circuit checker failed.";
250 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly.";
251 }
252
253 // Constant true, default invalidation
254 {
255 auto [circuit_checker_result, builder_failed, builder_err] =
256 test_constraints(PredicateTestCase::ConstantTrue, default_invalid_witness_target);
257 // As `assert_equal` doesn't make the CircuitChecker fail, we need to check that either the CircuitChecker
258 // failed, or the builder error resulted from an assert_eq.
259 bool circuit_check_failed = !circuit_checker_result;
260 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
261 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
262 << "Circuit checker succeeded unexpectedly and no assert_eq failure.";
263 EXPECT_TRUE(builder_failed) << "Builder succeeded unexpectedly.";
264 }
265 }
266
278 static void test_witness_true(InvalidWitnessTarget default_invalid_witness_target)
279 {
280 // Witness true, no invalidation
281 {
282 auto [circuit_checker_result, builder_failed, _] =
283 test_constraints(PredicateTestCase::WitnessTrue, InvalidWitnessTarget::None);
284 EXPECT_TRUE(circuit_checker_result) << "Circuit checker failed.";
285 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly.";
286 }
287
288 // Witness true, default invalidation
289 {
290 auto [circuit_checker_result, builder_failed, builder_err] =
291 test_constraints(PredicateTestCase::WitnessTrue, default_invalid_witness_target);
292 // As `assert_equal` doesn't make the CircuitChecker fail, we need to check that either the CircuitChecker
293 // failed, or the builder error resulted from an assert_eq.
294 bool circuit_check_failed = !circuit_checker_result;
295 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
296 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
297 << "Circuit checker succeeded unexpectedly and no assert_eq failure.";
298 EXPECT_TRUE(builder_failed) << "Builder succeeded unexpectedly.";
299 }
300 }
301
309 static void test_witness_false()
310 {
311 for (auto [invalid_witness_target, target_label] :
312 zip_view(InvalidWitness::get_all(), InvalidWitness::get_labels())) {
313 vinfo("Testing invalid witness target: ", target_label);
314
315 auto [circuit_checker_result, builder_failed, _] =
316 test_constraints(PredicateTestCase::WitnessFalse, invalid_witness_target);
317
318 EXPECT_TRUE(circuit_checker_result) << "Check builder failed for invalid witness target " + target_label;
319 EXPECT_FALSE(builder_failed) << "Builder failed for invalid witness target " + target_label;
320 vinfo("Passed invalid witness target: ", target_label);
321 }
322 }
323
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(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 // Only validate witness true failure for actual invalidation targets (skip None)
350 if (invalid_witness_target != InvalidWitnessTarget::None) {
351 // Check that the same configuration would have failed if the predicate was witness true
352 auto [circuit_checker_result, builder_failed, builder_err] =
353 test_constraints(PredicateTestCase::WitnessTrue, invalid_witness_target);
354
355 // As `assert_equal` doesn't make the CircuitChecker fail, we need to check that either the
356 // CircuitChecker failed, or the builder error resulted from an assert_eq.
357 bool circuit_check_failed = !circuit_checker_result;
358 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
359 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
360 << "Circuit checker succeeded unexpectedly and no assert_eq failure for invalid witness target " +
361 target_label;
362 EXPECT_TRUE(builder_failed) << "Builder succeeded for invalid witness target " + target_label;
363 vinfo("Passed invalid witness target (witness true confirmation): ", target_label);
364 }
365 }
366 }
367
382 static std::vector<std::string> test_invalid_witnesses()
383 {
384 std::vector<std::string> error_msgs;
385 for (auto [predicate_case, predicate_label] :
387 for (auto [target, label] : zip_view(InvalidWitness::get_all(), InvalidWitness::get_labels())) {
388 auto [circuit_checker_result, builder_failed, builder_err] = test_constraints(predicate_case, target);
389 error_msgs.emplace_back(builder_err);
390
391 if (predicate_case != PredicateTestCase::WitnessFalse) {
392 // If the predicate is not witness false, invalid witnesses should cause failure
393 if (target != InvalidWitnessTarget::None) {
394 bool circuit_check_failed = !circuit_checker_result;
395 bool assert_eq_error_present = (builder_err.find("assert_eq") != std::string::npos);
396 EXPECT_TRUE(circuit_check_failed || assert_eq_error_present)
397 << "Circuit checker succeeded unexpectedly and no assert_eq failure for invalid witness "
398 "target " +
399 label + " with predicate " + predicate_label;
400 EXPECT_TRUE(builder_failed) << "Builder succeeded unexpectedly for invalid witness target " +
401 label + " with predicate " + predicate_label;
402 } else {
403 EXPECT_TRUE(circuit_checker_result)
404 << "Circuit checker failed unexpectedly for invalid witness target " + label +
405 " with predicate " + predicate_label;
406 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly for invalid witness target " +
407 label + " with predicate " + predicate_label;
408 }
409 } else {
410 // If the predicate is witness false, all cases should succeed
411 EXPECT_TRUE(circuit_checker_result) << "Circuit checker failed unexpectedly for invalid witness "
412 "target " +
413 label + " with predicate " + predicate_label;
414 EXPECT_FALSE(builder_failed) << "Builder failed unexpectedly for invalid witness target " + label +
415 " with predicate " + predicate_label;
416 }
417 }
418 }
419 return error_msgs;
420 }
421};
422
423} // 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::pair< AcirConstraint, WitnessVector > generate_constraints(const Predicate< InvalidWitnessTarget > &mode)
Generate constraints and witness values based on the predicate and the invalidation target.
static std::tuple< bool, bool, std::string > test_constraints(const PredicateTestCase &test_case, const InvalidWitnessTarget &invalid_witness_target)
General purpose testing function. It generates the test based on the predicate and invalidation targe...
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 void test_witness_true(InvalidWitnessTarget default_invalid_witness_target)
Test all cases in which the predicate is a witness holding the value true.
static void test_witness_false_slow()
Test all invalid witness cases for the witness false predicate case (slow comprehensive version).
static void update_witness_based_on_predicate(AcirConstraint &constraint, WitnessVector &witness_values, const Predicate< InvalidWitnessTarget > &mode)
Update the constraint and the witness based on the predicate.
static std::vector< size_t > test_vk_independence()
Test vk generation is independent of the witness values supplied.
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
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:80
Concept defining the requirements for the Base template parameter of TestClassWithPredicate.
AluTraceBuilder builder
Definition alu.test.cpp:124
UltraKeccakFlavor::VerificationKey VerificationKey
AcirFormat constraint_to_acir_format(const ConstraintType &constraint, uint32_t varnum)
Convert an AcirConstraint 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
static std::vector< std::string > get_labels()
static std::vector< PredicateTestCase > get_all()
InvalidWitnessTarget invalid_witness
static WitnessOrConstant from_constant(FF value)