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;
40 using InnerBuilder = typename InnerFlavor::CircuitBuilder;
42 using InnerCommitment = InnerFlavor::Commitment;
43 using InnerFF = InnerFlavor::FF;
45 bb::stdlib::recursion::honk::RollupIO, // If RecursiveFlavor has IPA, then
46 // OuterVerifier is Rollup flavor
48
49 // Defines types for the outer circuit, i.e. the circuit of the recursive verifier
50 using OuterBuilder = typename RecursiveFlavor::CircuitBuilder;
60 bb::stdlib::recursion::honk::RollupIO, // If RecursiveFlavor has IPA, then
61 // OuterVerifier is Rollup flavor
63
66
69 using NativeVerifierCommitmentKey = typename InnerFlavor::VerifierCommitmentKey;
77 static InnerBuilder create_inner_circuit(size_t log_num_gates = 10)
78 {
80
81 // Create 2^log_n many add gates based on input log num gates
82 const size_t num_gates = (1 << log_num_gates);
83 for (size_t i = 0; i < num_gates; ++i) {
85 uint32_t a_idx = builder.add_variable(a);
86
89 fr d = a + b + c;
90 uint32_t b_idx = builder.add_variable(b);
91 uint32_t c_idx = builder.add_variable(c);
92 uint32_t d_idx = builder.add_variable(d);
93
94 builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
95 }
96
97 InnerIO::add_default(builder);
98
99 return builder;
100 }
101
102 public:
104
109 static void test_inner_circuit()
110 {
111 auto inner_circuit = create_inner_circuit();
112
113 bool result = CircuitChecker::check(inner_circuit);
114
115 EXPECT_EQ(result, true);
116 }
117
124 {
125 // Create an arbitrary inner circuit
126 auto inner_circuit = create_inner_circuit();
127 OuterBuilder outer_circuit;
128
129 // Compute native verification key
130 auto proving_key = std::make_shared<InnerDeciderProvingKey>(inner_circuit);
131 auto honk_vk = std::make_shared<typename InnerFlavor::VerificationKey>(proving_key->get_precomputed());
132 auto stdlib_vk_and_hash = std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, honk_vk);
133 // Instantiate the recursive verifier using the native verification key
134 RecursiveVerifier verifier{ &outer_circuit, stdlib_vk_and_hash };
135
136 // Spot check some values in the recursive VK to ensure it was constructed correctly
137 EXPECT_EQ(static_cast<uint64_t>(verifier.key->vk_and_hash->vk->log_circuit_size.get_value()),
138 honk_vk->log_circuit_size);
139 EXPECT_EQ(static_cast<uint64_t>(verifier.key->vk_and_hash->vk->num_public_inputs.get_value()),
140 honk_vk->num_public_inputs);
141 for (auto [vk_poly, native_vk_poly] : zip_view(verifier.key->vk_and_hash->vk->get_all(), honk_vk->get_all())) {
142 EXPECT_EQ(vk_poly.get_value(), native_vk_poly);
143 }
144 }
145
153 {
154 // Retrieves the trace blocks (each consisting of a specific gate) from the recursive verifier circuit
155 auto get_blocks = [](size_t inner_size) -> std::tuple<typename OuterBuilder::ExecutionTrace,
157 // Create an arbitrary inner circuit
158 auto inner_circuit = create_inner_circuit(inner_size);
159
160 // Generate a proof over the inner circuit
161 auto inner_proving_key = std::make_shared<InnerDeciderProvingKey>(inner_circuit);
162 auto verification_key =
163 std::make_shared<typename InnerFlavor::VerificationKey>(inner_proving_key->get_precomputed());
164 InnerProver inner_prover(inner_proving_key, verification_key);
165 info("test circuit size: ", inner_proving_key->dyadic_size());
166 auto inner_proof = inner_prover.construct_proof();
167
168 // Create a recursive verification circuit for the proof of the inner circuit
169 OuterBuilder outer_circuit;
170 auto stdlib_vk_and_hash =
171 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, verification_key);
172 RecursiveVerifier verifier{ &outer_circuit, stdlib_vk_and_hash };
173
174 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
175 typename RecursiveVerifier::Output verifier_output =
176 verifier.template verify_proof<OuterIO>(stdlib_inner_proof);
177
178 // IO of outer_circuit
179 OuterIO inputs;
180 inputs.pairing_inputs = verifier_output.points_accumulator;
181 if constexpr (HasIPAAccumulator<OuterFlavor>) {
182 // Add ipa claim
183 inputs.ipa_claim = verifier_output.ipa_claim;
184
185 // Store ipa_proof
186 outer_circuit.ipa_proof = verifier_output.ipa_proof.get_value();
187 };
188 inputs.set_public();
189
190 auto outer_proving_key = std::make_shared<OuterDeciderProvingKey>(outer_circuit);
191 auto outer_verification_key =
192 std::make_shared<typename OuterFlavor::VerificationKey>(outer_proving_key->get_precomputed());
193
194 return { outer_circuit.blocks, outer_verification_key };
195 };
196
197 auto [blocks_10, verification_key_10] = get_blocks(10);
198 auto [blocks_14, verification_key_14] = get_blocks(14);
199
200 compare_ultra_blocks_and_verification_keys<OuterFlavor>({ blocks_10, blocks_14 },
201 { verification_key_10, verification_key_14 });
202 }
203
209 {
210 // Create an arbitrary inner circuit
211 auto inner_circuit = create_inner_circuit();
212
213 // Generate a proof over the inner circuit
214 auto proving_key = std::make_shared<InnerDeciderProvingKey>(inner_circuit);
215 auto verification_key = std::make_shared<typename InnerFlavor::VerificationKey>(proving_key->get_precomputed());
216 InnerProver inner_prover(proving_key, verification_key);
217 auto inner_proof = inner_prover.construct_proof();
218
219 // Create a recursive verification circuit for the proof of the inner circuit
220 OuterBuilder outer_circuit;
221 auto stdlib_vk_and_hash =
222 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, verification_key);
223 RecursiveVerifier verifier{ &outer_circuit, stdlib_vk_and_hash };
224 verifier.transcript->enable_manifest();
225
226 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
227 VerifierOutput output = verifier.template verify_proof<OuterIO>(stdlib_inner_proof);
228
229 // IO of outer_circuit
230 OuterIO inputs;
231 inputs.pairing_inputs = output.points_accumulator;
232 if constexpr (HasIPAAccumulator<OuterFlavor>) {
233 // Add ipa claim
234 inputs.ipa_claim = output.ipa_claim;
235
236 // Store ipa_proof
237 outer_circuit.ipa_proof = output.ipa_proof.get_value();
238 };
239 inputs.set_public();
240
241 // Check for a failure flag in the recursive verifier circuit
242 EXPECT_EQ(outer_circuit.failed(), false) << outer_circuit.err();
243
244 // Check 1: Perform native verification then perform the pairing on the outputs of the recursive
245 // verifier and check that the result agrees.
246 bool native_result = false;
247 InnerVerifier native_verifier(verification_key);
248 native_verifier.transcript->enable_manifest();
250 native_verifier.ipa_verification_key = VerifierCommitmentKey<curve::Grumpkin>(1 << CONST_ECCVM_LOG_N);
251 native_result =
252 native_verifier.template verify_proof<bb::RollupIO>(inner_proof, output.ipa_proof.get_value()).result;
253 } else {
254 native_result = native_verifier.template verify_proof<bb::DefaultIO>(inner_proof).result;
255 }
256
258 bool result =
259 pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(), output.points_accumulator.P1.get_value());
260 info("input pairing points result: ", result);
261 auto recursive_result =
262 pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(), output.points_accumulator.P1.get_value());
263 EXPECT_EQ(recursive_result, native_result);
264
265 // Check 2: Ensure that the underlying native and recursive verification algorithms agree by ensuring
266 // the manifests produced by each agree.
267 auto recursive_manifest = verifier.transcript->get_manifest();
268 auto native_manifest = native_verifier.transcript->get_manifest();
269 for (size_t i = 0; i < recursive_manifest.size(); ++i) {
270 EXPECT_EQ(recursive_manifest[i], native_manifest[i]);
271 }
272
273 // Check 3: Construct and verify a proof of the recursive verifier circuit
274 {
275 auto proving_key = std::make_shared<OuterDeciderProvingKey>(outer_circuit);
276 auto verification_key =
277 std::make_shared<typename OuterFlavor::VerificationKey>(proving_key->get_precomputed());
278 info("Recursive Verifier: num gates = ", outer_circuit.get_num_finalized_gates());
279 OuterProver prover(proving_key, verification_key);
280 auto proof = prover.construct_proof();
282 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key = (1 << CONST_ECCVM_LOG_N);
283 OuterVerifier verifier(verification_key, ipa_verification_key);
284 bool result = verifier.template verify_proof<bb::RollupIO>(proof, proving_key->ipa_proof).result;
285 ASSERT_TRUE(result);
286 } else {
287 OuterVerifier verifier(verification_key);
288 bool result = verifier.template verify_proof<bb::DefaultIO>(proof).result;
289 ASSERT_TRUE(result);
290 }
291 }
292 // Check the size of the recursive verifier
294 uint32_t NUM_GATES_EXPECTED = 797234;
295 ASSERT_EQ(static_cast<uint32_t>(outer_circuit.get_num_finalized_gates()), NUM_GATES_EXPECTED)
296 << "MegaZKHonk Recursive verifier changed in Ultra gate count! Update this value if you "
297 "are sure this is expected.";
298 }
299 }
300
307 {
308 for (size_t idx = 0; idx < static_cast<size_t>(TamperType::END); idx++) {
309 // Create an arbitrary inner circuit
310 auto inner_circuit = create_inner_circuit();
311
312 // Generate a proof over the inner circuit
313 auto proving_key = std::make_shared<InnerDeciderProvingKey>(inner_circuit);
314 // Generate the corresponding inner verification key
315 auto inner_verification_key =
316 std::make_shared<typename InnerFlavor::VerificationKey>(proving_key->get_precomputed());
317 InnerProver inner_prover(proving_key, inner_verification_key);
318 auto inner_proof = inner_prover.construct_proof();
319
320 // Tamper with the proof to be verified
321 TamperType tamper_type = static_cast<TamperType>(idx);
322 tamper_with_proof<InnerProver, InnerFlavor>(inner_prover, inner_proof, tamper_type);
323
324 // Create a recursive verification circuit for the proof of the inner circuit
325 OuterBuilder outer_circuit;
326 auto stdlib_vk_and_hash =
327 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, inner_verification_key);
328 RecursiveVerifier verifier{ &outer_circuit, stdlib_vk_and_hash };
329 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
330 VerifierOutput output = verifier.template verify_proof<OuterIO>(stdlib_inner_proof);
331
332 // Wrong Gemini witnesses lead to the pairing check failure in non-ZK case but don't break any
333 // constraints. In ZK-cases, tampering with Gemini witnesses leads to SmallSubgroupIPA consistency check
334 // failure.
335 if ((tamper_type != TamperType::MODIFY_GEMINI_WITNESS) || (InnerFlavor::HasZK)) {
336 // We expect the circuit check to fail due to the bad proof.
337 EXPECT_FALSE(CircuitChecker::check(outer_circuit));
338 } else {
339 EXPECT_TRUE(CircuitChecker::check(outer_circuit));
341 bool result = pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(),
342 output.points_accumulator.P1.get_value());
343 EXPECT_FALSE(result);
344 }
345 }
346 }
355
356 {
357 for (size_t idx = 0; idx < 2; idx++) {
358 // Create an arbitrary inner circuit
359 auto inner_circuit = create_inner_circuit();
360
361 // Generate a proof over the inner circuit
362 auto proving_key = std::make_shared<InnerDeciderProvingKey>(inner_circuit);
363 // Generate the corresponding inner verification key
364 auto inner_verification_key =
365 std::make_shared<typename InnerFlavor::VerificationKey>(proving_key->get_precomputed());
366 InnerProver inner_prover(proving_key, inner_verification_key);
367 auto inner_proof = inner_prover.construct_proof();
368
369 // Tamper with the proof to be verified
370 tamper_with_proof<InnerProver, InnerFlavor>(inner_proof, /*end_of_proof*/ static_cast<bool>(idx));
371
372 // Create a recursive verification circuit for the proof of the inner circuit
373 OuterBuilder outer_circuit;
374 auto stdlib_vk_and_hash =
375 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, inner_verification_key);
376 RecursiveVerifier verifier{ &outer_circuit, stdlib_vk_and_hash };
377 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
378 VerifierOutput output = verifier.template verify_proof<OuterIO>(stdlib_inner_proof);
379
380 if (idx == 0) {
381 // We expect the circuit check to fail due to the bad proof.
382 EXPECT_FALSE(CircuitChecker::check(outer_circuit));
383 } else {
384 // Wrong witnesses lead to the pairing check failure in non-ZK case but don't break any
385 // constraints. In ZK-cases, tampering with Gemini witnesses leads to SmallSubgroupIPA consistency check
386 // failure.
387 EXPECT_TRUE(CircuitChecker::check(outer_circuit));
389 bool result = pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(),
390 output.points_accumulator.P1.get_value());
391 EXPECT_FALSE(result);
392 }
393 }
394 }
395};
396
398
400{
401 TestFixture::test_inner_circuit();
402}
403
404HEAVY_TYPED_TEST(RecursiveVerifierTest, RecursiveVerificationKey)
405{
406 TestFixture::test_recursive_verification_key_creation();
407}
408
409HEAVY_TYPED_TEST(RecursiveVerifierTest, SingleRecursiveVerification)
410{
411 TestFixture::test_recursive_verification();
412};
413
415{
416 if constexpr (IsAnyOf<TypeParam,
421 TestFixture::test_independent_vk_hash();
422 } else {
423 GTEST_SKIP() << "Not built for this parameter";
424 }
425};
426
427HEAVY_TYPED_TEST(RecursiveVerifierTest, SingleRecursiveVerificationFailure)
428{
429 TestFixture::test_recursive_verification_fails();
430};
431
432#ifdef DISABLE_HEAVY_TESTS
433// Null test
434TEST(RecursiveVerifierTest, DoNothingTestToEnsureATestExists) {}
435#endif
436} // namespace bb::stdlib::recursion::honk
A DeciderProvingKey is normally constructed from a finalized circuit and it contains all the informat...
The recursive counterpart to the "native" MegaZKFlavor.
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
The recursive counterpart to the "native" Ultra flavor.
The recursive counterpart to the "native" UltraRollupFlavor.
std::shared_ptr< Transcript > transcript
VerifierCommitmentKey< curve::Grumpkin > ipa_verification_key
The recursive counterpart to the Ultra flavor with ZK.
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
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< 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:70
AluTraceBuilder builder
Definition alu.test.cpp:123
FF a
FF b
Base class templates for structures that contain data parameterized by the fundamental polynomials of...
testing::Types< UltraRecursiveFlavor_< UltraCircuitBuilder >, UltraRollupRecursiveFlavor_< UltraCircuitBuilder >, UltraRecursiveFlavor_< MegaCircuitBuilder >, UltraZKRecursiveFlavor_< UltraCircuitBuilder >, UltraZKRecursiveFlavor_< MegaCircuitBuilder > > Flavors
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
testing::Types< UltraRecursiveFlavor_< UltraCircuitBuilder > > Flavors
TEST(RecursiveHonkTranscript, InterfacesMatch)
Test basic transcript functionality and check circuit.
TYPED_TEST_SUITE(BoomerangRecursiveVerifierTest, Flavors)
field< Bn254FrParams > fr
Definition fr.hpp:174
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.
#define HEAVY_TYPED_TEST(x, y)
Definition test.hpp:11