Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ultra_recursive_verifier.test.cpp
Go to the documentation of this file.
11
13
14// Run the recursive verifier tests with conventional Ultra builder and Goblin builder
15using Flavors = testing::Types<MegaRecursiveFlavor_<MegaCircuitBuilder>,
16 MegaRecursiveFlavor_<UltraCircuitBuilder>,
17 UltraRecursiveFlavor_<UltraCircuitBuilder>,
18 UltraRecursiveFlavor_<MegaCircuitBuilder>,
19 UltraZKRecursiveFlavor_<UltraCircuitBuilder>,
20 UltraZKRecursiveFlavor_<MegaCircuitBuilder>,
21 UltraRollupRecursiveFlavor_<UltraCircuitBuilder>,
22 MegaZKRecursiveFlavor_<MegaCircuitBuilder>,
23 MegaZKRecursiveFlavor_<UltraCircuitBuilder>>;
24
34template <typename RecursiveFlavor> class RecursiveVerifierTest : public testing::Test {
35
36 // Define types for the inner circuit, i.e. the circuit whose proof will be recursively verified
37 using InnerFlavor = typename RecursiveFlavor::NativeFlavor;
39 using InnerBuilder = typename InnerFlavor::CircuitBuilder;
41 using InnerCommitment = InnerFlavor::Commitment;
42 using InnerFF = InnerFlavor::FF;
44
45 // IO types for native verifiers (non-templated, in bb:: namespace)
48
49 // IO types for recursive verifiers (templated on Builder)
51 bb::stdlib::recursion::honk::RollupIO, // If RecursiveFlavor has IPA, then
52 // OuterVerifier is Rollup flavor
54
55 // Defines types for the outer circuit, i.e. the circuit of the recursive verifier
56 using OuterBuilder = typename RecursiveFlavor::CircuitBuilder;
67 bb::stdlib::recursion::honk::RollupIO, // If RecursiveFlavor has IPA, then
68 // OuterVerifier is Rollup flavor
70
73
76 using NativeVerifierCommitmentKey = typename InnerFlavor::VerifierCommitmentKey;
84 static InnerBuilder create_inner_circuit(size_t log_num_gates = 10)
85 {
87
88 // Create 2^log_n many add gates based on input log num gates
89 const size_t num_gates = (1 << log_num_gates);
90 for (size_t i = 0; i < num_gates; ++i) {
92 uint32_t a_idx = builder.add_variable(a);
93
96 fr d = a + b + c;
97 uint32_t b_idx = builder.add_variable(b);
98 uint32_t c_idx = builder.add_variable(c);
99 uint32_t d_idx = builder.add_variable(d);
100
101 builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
102 }
103
104 InnerIO::add_default(builder);
105
106 return builder;
107 }
108
109 public:
111
116 static void test_inner_circuit()
117 {
118 auto inner_circuit = create_inner_circuit();
119
120 bool result = CircuitChecker::check(inner_circuit);
121
122 EXPECT_EQ(result, true);
123 }
124
131 {
132 // Create an arbitrary inner circuit
133 auto inner_circuit = create_inner_circuit();
134 OuterBuilder outer_circuit;
135
136 // Compute native verification key
137 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
138 auto honk_vk = std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->get_precomputed());
139 auto stdlib_vk_and_hash = std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, honk_vk);
140 // Instantiate the recursive verifier using the native verification key
141 RecursiveVerifier verifier{ stdlib_vk_and_hash };
142
143 // Spot check some values in the recursive VK to ensure it was constructed correctly
144 EXPECT_EQ(
145 static_cast<uint64_t>(verifier.get_verifier_instance()->vk_and_hash->vk->log_circuit_size.get_value()),
146 honk_vk->log_circuit_size);
147 EXPECT_EQ(
148 static_cast<uint64_t>(verifier.get_verifier_instance()->vk_and_hash->vk->num_public_inputs.get_value()),
149 honk_vk->num_public_inputs);
150 for (auto [vk_poly, native_vk_poly] :
151 zip_view(verifier.get_verifier_instance()->vk_and_hash->vk->get_all(), honk_vk->get_all())) {
152 EXPECT_EQ(vk_poly.get_value(), native_vk_poly);
153 }
154 }
155
163 {
164 // Retrieves the trace blocks (each consisting of a specific gate) from the recursive verifier circuit
165 auto get_blocks = [](size_t inner_size) -> std::tuple<typename OuterBuilder::ExecutionTrace,
167 // Create an arbitrary inner circuit
168 auto inner_circuit = create_inner_circuit(inner_size);
169
170 // Generate a proof over the inner circuit
171 auto inner_prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
172 auto verification_key =
173 std::make_shared<typename InnerFlavor::VerificationKey>(inner_prover_instance->get_precomputed());
174 InnerProver inner_prover(inner_prover_instance, verification_key);
175 info("test circuit size: ", inner_prover_instance->dyadic_size());
176 auto inner_proof = inner_prover.construct_proof();
177
178 // Create a recursive verification circuit for the proof of the inner circuit
179 OuterBuilder outer_circuit;
180 auto stdlib_vk_and_hash =
181 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, verification_key);
182 RecursiveVerifier verifier{ stdlib_vk_and_hash };
183
184 // Convert native proof to stdlib and verify (verifier handles IPA splitting internally)
185 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
186 typename RecursiveVerifier::Output verifier_output = verifier.verify_proof(stdlib_inner_proof);
187
188 // IO of outer_circuit
190 inputs.pairing_inputs = verifier_output.points_accumulator;
191 if constexpr (HasIPAAccumulator<OuterFlavor>) {
192 // Add ipa claim
193 inputs.ipa_claim = verifier_output.ipa_claim;
194
195 // Store ipa_proof
196 outer_circuit.ipa_proof = verifier_output.ipa_proof.get_value();
197 };
198 inputs.set_public();
199
200 auto outer_prover_instance = std::make_shared<OuterProverInstance>(outer_circuit);
201 auto outer_verification_key =
202 std::make_shared<typename OuterFlavor::VerificationKey>(outer_prover_instance->get_precomputed());
203
204 return { outer_circuit.blocks, outer_verification_key };
205 };
206
207 auto [blocks_10, verification_key_10] = get_blocks(10);
208 auto [blocks_14, verification_key_14] = get_blocks(14);
209
210 compare_ultra_blocks_and_verification_keys<OuterFlavor>({ blocks_10, blocks_14 },
211 { verification_key_10, verification_key_14 });
212 }
213
219 {
220 // Create an arbitrary inner circuit
221 auto inner_circuit = create_inner_circuit();
222
223 // Generate a proof over the inner circuit
224 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
225 auto verification_key =
226 std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->get_precomputed());
227 InnerProver inner_prover(prover_instance, verification_key);
228 auto inner_proof = inner_prover.construct_proof();
229
230 // Create a recursive verification circuit for the proof of the inner circuit
231 OuterBuilder outer_circuit;
232 auto stdlib_vk_and_hash =
233 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, verification_key);
234 auto recursive_transcript = std::make_shared<typename RecursiveFlavor::Transcript>();
235 recursive_transcript->enable_manifest();
236 RecursiveVerifier verifier{ stdlib_vk_and_hash, recursive_transcript };
237
238 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
239 VerifierOutput output = verifier.verify_proof(stdlib_inner_proof);
240
241 // IO of outer_circuit
243 inputs.pairing_inputs = output.points_accumulator;
244 if constexpr (HasIPAAccumulator<OuterFlavor>) {
245 // Add ipa claim
246 inputs.ipa_claim = output.ipa_claim;
247
248 // Store ipa_proof
249 outer_circuit.ipa_proof = output.ipa_proof.get_value();
250 };
251 inputs.set_public();
252
253 // Check for a failure flag in the recursive verifier circuit
254 EXPECT_EQ(outer_circuit.failed(), false) << outer_circuit.err();
255
256 // Check 1: Perform native verification then perform the pairing on the outputs of the recursive
257 // verifier and check that the result agrees.
258 auto vk_and_hash = std::make_shared<typename InnerFlavor::VKAndHash>(verification_key);
260 native_transcript->enable_manifest();
261 InnerVerifier native_verifier(vk_and_hash, native_transcript);
262 // inner_proof already contains combined honk + IPA for rollup flavors
263 bool native_result = native_verifier.verify_proof(inner_proof).result;
264
266 bool result =
267 pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(), output.points_accumulator.P1.get_value());
268 info("input pairing points result: ", result);
269 auto recursive_result =
270 pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(), output.points_accumulator.P1.get_value());
271 EXPECT_EQ(recursive_result, native_result);
272
273 // Check 2: Ensure that the underlying native and recursive verification algorithms agree by ensuring
274 // the manifests produced by each agree.
275 auto recursive_manifest = verifier.get_transcript()->get_manifest();
276 auto native_manifest = native_verifier.get_transcript()->get_manifest();
277 for (size_t i = 0; i < recursive_manifest.size(); ++i) {
278 EXPECT_EQ(recursive_manifest[i], native_manifest[i]);
279 }
280
281 // Check 3: Construct and verify a proof of the recursive verifier circuit
282 {
283 auto prover_instance = std::make_shared<OuterProverInstance>(outer_circuit);
284 auto verification_key =
285 std::make_shared<typename OuterFlavor::VerificationKey>(prover_instance->get_precomputed());
286 info("Recursive Verifier: num gates = ", outer_circuit.get_num_finalized_gates());
287 OuterProver prover(prover_instance, verification_key);
288 // construct_proof() already returns combined proof (honk + IPA) for rollup flavors
289 auto proof = prover.construct_proof();
290 auto outer_vk_and_hash = std::make_shared<typename OuterFlavor::VKAndHash>(verification_key);
291 OuterVerifier verifier(outer_vk_and_hash);
292 bool result = verifier.verify_proof(proof).result;
293 ASSERT_TRUE(result);
294 }
295 // Check the size of the recursive verifier
297 const auto expected_gate_count = std::get<0>(acir_format::HONK_RECURSION_CONSTANTS<RecursiveFlavor>());
298 ASSERT_EQ(outer_circuit.get_num_finalized_gates(), expected_gate_count)
299 << "MegaZKHonk Recursive verifier changed in Ultra gate count! Update this value if you "
300 "are sure this is expected.";
301 }
302 }
303
310 {
311 for (size_t idx = 0; idx < static_cast<size_t>(TamperType::END); idx++) {
312 // Create an arbitrary inner circuit
313 auto inner_circuit = create_inner_circuit();
314
315 // Generate a proof over the inner circuit
316 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
317 // Generate the corresponding inner verification key
318 auto inner_verification_key =
319 std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->get_precomputed());
320 InnerProver inner_prover(prover_instance, inner_verification_key);
321 auto inner_proof = inner_prover.construct_proof();
322
323 // Tamper with the proof to be verified
324 TamperType tamper_type = static_cast<TamperType>(idx);
325 tamper_with_proof<InnerProver, InnerFlavor>(inner_prover, inner_proof, tamper_type);
326
327 // Create a recursive verification circuit for the proof of the inner circuit
328 OuterBuilder outer_circuit;
329 auto stdlib_vk_and_hash =
330 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, inner_verification_key);
331 RecursiveVerifier verifier{ stdlib_vk_and_hash };
332 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
333 VerifierOutput output = verifier.verify_proof(stdlib_inner_proof);
334
335 // Wrong Gemini witnesses lead to the pairing check failure in non-ZK case but don't break any
336 // constraints. In ZK-cases, tampering with Gemini witnesses leads to SmallSubgroupIPA consistency check
337 // failure.
338 if ((tamper_type != TamperType::MODIFY_GEMINI_WITNESS) || (InnerFlavor::HasZK)) {
339 // We expect the circuit check to fail due to the bad proof.
340 EXPECT_FALSE(CircuitChecker::check(outer_circuit));
341 } else {
342 EXPECT_TRUE(CircuitChecker::check(outer_circuit));
344 bool result = pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(),
345 output.points_accumulator.P1.get_value());
346 EXPECT_FALSE(result);
347 }
348 }
349 }
358
359 {
360 for (size_t idx = 0; idx < 2; idx++) {
361 // Create an arbitrary inner circuit
362 auto inner_circuit = create_inner_circuit();
363
364 // Generate a proof over the inner circuit
365 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
366 // Generate the corresponding inner verification key
367 auto inner_verification_key =
368 std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->get_precomputed());
369 InnerProver inner_prover(prover_instance, inner_verification_key);
370 auto inner_proof = inner_prover.construct_proof();
371
372 // Tamper with the proof to be verified
373 tamper_with_proof<InnerProver, InnerFlavor>(inner_proof, /*end_of_proof*/ static_cast<bool>(idx));
374
375 // Create a recursive verification circuit for the proof of the inner circuit
376 OuterBuilder outer_circuit;
377 auto stdlib_vk_and_hash =
378 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, inner_verification_key);
379 RecursiveVerifier verifier{ stdlib_vk_and_hash };
380 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
381 VerifierOutput output = verifier.verify_proof(stdlib_inner_proof);
382
383 if (idx == 0) {
384 // We expect the circuit check to fail due to the bad proof.
385 EXPECT_FALSE(CircuitChecker::check(outer_circuit));
386 } else {
387 // Wrong witnesses lead to the pairing check failure in non-ZK case but don't break any
388 // constraints. In ZK-cases, tampering with Gemini witnesses leads to SmallSubgroupIPA consistency check
389 // failure.
390 EXPECT_TRUE(CircuitChecker::check(outer_circuit));
392 bool result = pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(),
393 output.points_accumulator.P1.get_value());
394 EXPECT_FALSE(result);
395 }
396 }
397 }
398};
399
401
403{
404 TestFixture::test_inner_circuit();
405}
406
407HEAVY_TYPED_TEST(RecursiveVerifierTest, RecursiveVerificationKey)
408{
409 TestFixture::test_recursive_verification_key_creation();
410}
411
412HEAVY_TYPED_TEST(RecursiveVerifierTest, SingleRecursiveVerification)
413{
414 TestFixture::test_recursive_verification();
415};
416
418{
419 if constexpr (IsAnyOf<TypeParam,
424 TestFixture::test_independent_vk_hash();
425 } else {
426 GTEST_SKIP() << "Not built for this parameter";
427 }
428};
429
430HEAVY_TYPED_TEST(RecursiveVerifierTest, SingleRecursiveVerificationFailure)
431{
432 TestFixture::test_recursive_verification_fails();
433};
434
435#ifdef DISABLE_HEAVY_TESTS
436// Null test
437TEST(RecursiveVerifierTest, DoNothingTestToEnsureATestExists) {}
438#endif
439} // namespace bb::stdlib::recursion::honk
Manages the data that is propagated on the public inputs of an application/function circuit.
The recursive counterpart to the "native" MegaZKFlavor.
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
The data that is propagated on the public inputs of a rollup circuit.
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
The recursive counterpart to the "native" Ultra flavor.
UltraRollupFlavor extends UltraFlavor with IPA proof support.
The recursive counterpart to the "native" UltraRollupFlavor.
typename Flavor::VerificationKey VerificationKey
std::conditional_t< IsRecursive, stdlib::recursion::honk::UltraRecursiveVerifierOutput< Builder >, UltraVerifierOutput< Flavor > > Output
const std::shared_ptr< Transcript > & get_transcript() const
Get the transcript (for accessing manifest in tests)
Output verify_proof(const Proof &proof)
Perform ultra verification.
The recursive counterpart to the Ultra flavor with ZK.
A simple wrapper around a vector of stdlib field elements representing a proof.
Definition proof.hpp:19
Manages the data that is propagated on the public inputs of an application/function circuit.
Test suite for recursive verification of Honk proofs for both Ultra and Mega arithmetisation.
std::conditional_t< HasIPAAccumulator< InnerFlavor >, bb::RollupIO, bb::DefaultIO > NativeInnerIO
std::conditional_t< HasIPAAccumulator< OuterFlavor >, bb::RollupIO, bb::DefaultIO > NativeOuterIO
std::conditional_t< HasIPAAccumulator< RecursiveFlavor >, bb::stdlib::recursion::honk::RollupIO, bb::stdlib::recursion::honk::DefaultIO< OuterBuilder > > OuterIO
static InnerBuilder create_inner_circuit(size_t log_num_gates=10)
Create a non-trivial arbitrary inner circuit, the proof of which will be recursively verified.
typename InnerFlavor::VerifierCommitmentKey NativeVerifierCommitmentKey
std::conditional_t< IsMegaBuilder< OuterBuilder >, MegaFlavor, std::conditional_t< HasIPAAccumulator< RecursiveFlavor >, UltraRollupFlavor, UltraFlavor > > OuterFlavor
static void test_recursive_verification()
Construct a recursive verification circuit for the proof of an inner circuit then call check_circuit ...
static void test_recursive_verification_fails()
Construct verifier circuits for proofs whose data have been tampered with. Expect failure.
typename RecursiveVerifier::VerificationKey VerificationKey
static void test_recursive_verification_key_creation()
Instantiate a recursive verification key from the native verification key produced by the inner cicui...
static void test_recursive_verification_fails()
Tamper with a MegaZK proof in two ways. First, we modify the first non-zero value in the proof,...
std::conditional_t< HasIPAAccumulator< RecursiveFlavor >, bb::stdlib::recursion::honk::RollupIO, bb::stdlib::recursion::honk::DefaultIO< InnerBuilder > > InnerIO
static void test_inner_circuit()
Create inner circuit and call check_circuit on it.
static void test_independent_vk_hash()
Ensures that the recursive verifier circuit for two inner circuits of different size is the same as t...
The data that is propagated on the public inputs of a rollup circuit.
void info(Args... args)
Definition log.hpp:89
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
Base class templates for structures that contain data parameterized by the fundamental polynomials of...
AvmProvingInputs inputs
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
testing::Types< UltraRecursiveFlavor_< UltraCircuitBuilder > > Flavors
TYPED_TEST_SUITE(BoomerangRecursiveVerifierTest, Flavors)
field< Bn254FrParams > fr
Definition fr.hpp:174
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static field random_element(numeric::RNG *engine=nullptr) noexcept
An object storing two EC points that represent the inputs to a pairing check.
Output type for recursive ultra verification.
#define HEAVY_TYPED_TEST(x, y)
Definition test.hpp:11