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 prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
131 auto honk_vk = std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->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.verifier_instance->vk_and_hash->vk->log_circuit_size.get_value()),
138 honk_vk->log_circuit_size);
139 EXPECT_EQ(static_cast<uint64_t>(verifier.verifier_instance->vk_and_hash->vk->num_public_inputs.get_value()),
140 honk_vk->num_public_inputs);
141 for (auto [vk_poly, native_vk_poly] :
142 zip_view(verifier.verifier_instance->vk_and_hash->vk->get_all(), honk_vk->get_all())) {
143 EXPECT_EQ(vk_poly.get_value(), native_vk_poly);
144 }
145 }
146
154 {
155 // Retrieves the trace blocks (each consisting of a specific gate) from the recursive verifier circuit
156 auto get_blocks = [](size_t inner_size) -> std::tuple<typename OuterBuilder::ExecutionTrace,
158 // Create an arbitrary inner circuit
159 auto inner_circuit = create_inner_circuit(inner_size);
160
161 // Generate a proof over the inner circuit
162 auto inner_prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
163 auto verification_key =
164 std::make_shared<typename InnerFlavor::VerificationKey>(inner_prover_instance->get_precomputed());
165 InnerProver inner_prover(inner_prover_instance, verification_key);
166 info("test circuit size: ", inner_prover_instance->dyadic_size());
167 auto inner_proof = inner_prover.construct_proof();
168
169 // Create a recursive verification circuit for the proof of the inner circuit
170 OuterBuilder outer_circuit;
171 auto stdlib_vk_and_hash =
172 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, verification_key);
173 RecursiveVerifier verifier{ &outer_circuit, stdlib_vk_and_hash };
174
175 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
176 typename RecursiveVerifier::Output verifier_output =
177 verifier.template verify_proof<OuterIO>(stdlib_inner_proof);
178
179 // IO of outer_circuit
180 OuterIO inputs;
181 inputs.pairing_inputs = verifier_output.points_accumulator;
182 if constexpr (HasIPAAccumulator<OuterFlavor>) {
183 // Add ipa claim
184 inputs.ipa_claim = verifier_output.ipa_claim;
185
186 // Store ipa_proof
187 outer_circuit.ipa_proof = verifier_output.ipa_proof.get_value();
188 };
189 inputs.set_public();
190
191 auto outer_prover_instance = std::make_shared<OuterProverInstance>(outer_circuit);
192 auto outer_verification_key =
193 std::make_shared<typename OuterFlavor::VerificationKey>(outer_prover_instance->get_precomputed());
194
195 return { outer_circuit.blocks, outer_verification_key };
196 };
197
198 auto [blocks_10, verification_key_10] = get_blocks(10);
199 auto [blocks_14, verification_key_14] = get_blocks(14);
200
201 compare_ultra_blocks_and_verification_keys<OuterFlavor>({ blocks_10, blocks_14 },
202 { verification_key_10, verification_key_14 });
203 }
204
210 {
211 // Create an arbitrary inner circuit
212 auto inner_circuit = create_inner_circuit();
213
214 // Generate a proof over the inner circuit
215 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
216 auto verification_key =
217 std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->get_precomputed());
218 InnerProver inner_prover(prover_instance, verification_key);
219 auto inner_proof = inner_prover.construct_proof();
220
221 // Create a recursive verification circuit for the proof of the inner circuit
222 OuterBuilder outer_circuit;
223 auto stdlib_vk_and_hash =
224 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, verification_key);
225 RecursiveVerifier verifier{ &outer_circuit, stdlib_vk_and_hash };
226 verifier.transcript->enable_manifest();
227
228 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
229 VerifierOutput output = verifier.template verify_proof<OuterIO>(stdlib_inner_proof);
230
231 // IO of outer_circuit
232 OuterIO inputs;
233 inputs.pairing_inputs = output.points_accumulator;
234 if constexpr (HasIPAAccumulator<OuterFlavor>) {
235 // Add ipa claim
236 inputs.ipa_claim = output.ipa_claim;
237
238 // Store ipa_proof
239 outer_circuit.ipa_proof = output.ipa_proof.get_value();
240 };
241 inputs.set_public();
242
243 // Check for a failure flag in the recursive verifier circuit
244 EXPECT_EQ(outer_circuit.failed(), false) << outer_circuit.err();
245
246 // Check 1: Perform native verification then perform the pairing on the outputs of the recursive
247 // verifier and check that the result agrees.
248 bool native_result = false;
249 InnerVerifier native_verifier(verification_key);
250 native_verifier.transcript->enable_manifest();
252 native_verifier.ipa_verification_key = VerifierCommitmentKey<curve::Grumpkin>(1 << CONST_ECCVM_LOG_N);
253 native_result =
254 native_verifier.template verify_proof<bb::RollupIO>(inner_proof, output.ipa_proof.get_value()).result;
255 } else {
256 native_result = native_verifier.template verify_proof<bb::DefaultIO>(inner_proof).result;
257 }
258
260 bool result =
261 pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(), output.points_accumulator.P1.get_value());
262 info("input pairing points result: ", result);
263 auto recursive_result =
264 pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(), output.points_accumulator.P1.get_value());
265 EXPECT_EQ(recursive_result, native_result);
266
267 // Check 2: Ensure that the underlying native and recursive verification algorithms agree by ensuring
268 // the manifests produced by each agree.
269 auto recursive_manifest = verifier.transcript->get_manifest();
270 auto native_manifest = native_verifier.transcript->get_manifest();
271 for (size_t i = 0; i < recursive_manifest.size(); ++i) {
272 EXPECT_EQ(recursive_manifest[i], native_manifest[i]);
273 }
274
275 // Check 3: Construct and verify a proof of the recursive verifier circuit
276 {
277 auto prover_instance = std::make_shared<OuterProverInstance>(outer_circuit);
278 auto verification_key =
279 std::make_shared<typename OuterFlavor::VerificationKey>(prover_instance->get_precomputed());
280 info("Recursive Verifier: num gates = ", outer_circuit.get_num_finalized_gates());
281 OuterProver prover(prover_instance, verification_key);
282 auto proof = prover.construct_proof();
284 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key = (1 << CONST_ECCVM_LOG_N);
285 OuterVerifier verifier(verification_key, ipa_verification_key);
286 bool result = verifier.template verify_proof<bb::RollupIO>(proof, prover_instance->ipa_proof).result;
287 ASSERT_TRUE(result);
288 } else {
289 OuterVerifier verifier(verification_key);
290 bool result = verifier.template verify_proof<bb::DefaultIO>(proof).result;
291 ASSERT_TRUE(result);
292 }
293 }
294 // Check the size of the recursive verifier
296 uint32_t NUM_GATES_EXPECTED = 808803;
297 ASSERT_EQ(static_cast<uint32_t>(outer_circuit.get_num_finalized_gates()), NUM_GATES_EXPECTED)
298 << "MegaZKHonk Recursive verifier changed in Ultra gate count! Update this value if you "
299 "are sure this is expected.";
300 }
301 }
302
309 {
310 for (size_t idx = 0; idx < static_cast<size_t>(TamperType::END); idx++) {
311 // Create an arbitrary inner circuit
312 auto inner_circuit = create_inner_circuit();
313
314 // Generate a proof over the inner circuit
315 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
316 // Generate the corresponding inner verification key
317 auto inner_verification_key =
318 std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->get_precomputed());
319 InnerProver inner_prover(prover_instance, inner_verification_key);
320 auto inner_proof = inner_prover.construct_proof();
321
322 // Tamper with the proof to be verified
323 TamperType tamper_type = static_cast<TamperType>(idx);
324 tamper_with_proof<InnerProver, InnerFlavor>(inner_prover, inner_proof, tamper_type);
325
326 // Create a recursive verification circuit for the proof of the inner circuit
327 OuterBuilder outer_circuit;
328 auto stdlib_vk_and_hash =
329 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, inner_verification_key);
330 RecursiveVerifier verifier{ &outer_circuit, stdlib_vk_and_hash };
331 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
332 VerifierOutput output = verifier.template verify_proof<OuterIO>(stdlib_inner_proof);
333
334 // Wrong Gemini witnesses lead to the pairing check failure in non-ZK case but don't break any
335 // constraints. In ZK-cases, tampering with Gemini witnesses leads to SmallSubgroupIPA consistency check
336 // failure.
337 if ((tamper_type != TamperType::MODIFY_GEMINI_WITNESS) || (InnerFlavor::HasZK)) {
338 // We expect the circuit check to fail due to the bad proof.
339 EXPECT_FALSE(CircuitChecker::check(outer_circuit));
340 } else {
341 EXPECT_TRUE(CircuitChecker::check(outer_circuit));
343 bool result = pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(),
344 output.points_accumulator.P1.get_value());
345 EXPECT_FALSE(result);
346 }
347 }
348 }
357
358 {
359 for (size_t idx = 0; idx < 2; idx++) {
360 // Create an arbitrary inner circuit
361 auto inner_circuit = create_inner_circuit();
362
363 // Generate a proof over the inner circuit
364 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
365 // Generate the corresponding inner verification key
366 auto inner_verification_key =
367 std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->get_precomputed());
368 InnerProver inner_prover(prover_instance, inner_verification_key);
369 auto inner_proof = inner_prover.construct_proof();
370
371 // Tamper with the proof to be verified
372 tamper_with_proof<InnerProver, InnerFlavor>(inner_proof, /*end_of_proof*/ static_cast<bool>(idx));
373
374 // Create a recursive verification circuit for the proof of the inner circuit
375 OuterBuilder outer_circuit;
376 auto stdlib_vk_and_hash =
377 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, inner_verification_key);
378 RecursiveVerifier verifier{ &outer_circuit, stdlib_vk_and_hash };
379 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
380 VerifierOutput output = verifier.template verify_proof<OuterIO>(stdlib_inner_proof);
381
382 if (idx == 0) {
383 // We expect the circuit check to fail due to the bad proof.
384 EXPECT_FALSE(CircuitChecker::check(outer_circuit));
385 } else {
386 // Wrong witnesses lead to the pairing check failure in non-ZK case but don't break any
387 // constraints. In ZK-cases, tampering with Gemini witnesses leads to SmallSubgroupIPA consistency check
388 // failure.
389 EXPECT_TRUE(CircuitChecker::check(outer_circuit));
391 bool result = pcs_vkey.pairing_check(output.points_accumulator.P0.get_value(),
392 output.points_accumulator.P1.get_value());
393 EXPECT_FALSE(result);
394 }
395 }
396 }
397};
398
400
402{
403 TestFixture::test_inner_circuit();
404}
405
406HEAVY_TYPED_TEST(RecursiveVerifierTest, RecursiveVerificationKey)
407{
408 TestFixture::test_recursive_verification_key_creation();
409}
410
411HEAVY_TYPED_TEST(RecursiveVerifierTest, SingleRecursiveVerification)
412{
413 TestFixture::test_recursive_verification();
414};
415
417{
418 if constexpr (IsAnyOf<TypeParam,
423 TestFixture::test_independent_vk_hash();
424 } else {
425 GTEST_SKIP() << "Not built for this parameter";
426 }
427};
428
429HEAVY_TYPED_TEST(RecursiveVerifierTest, SingleRecursiveVerificationFailure)
430{
431 TestFixture::test_recursive_verification_fails();
432};
433
434#ifdef DISABLE_HEAVY_TESTS
435// Null test
436TEST(RecursiveVerifierTest, DoNothingTestToEnsureATestExists) {}
437#endif
438} // namespace bb::stdlib::recursion::honk
The recursive counterpart to the "native" MegaZKFlavor.
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.
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:74
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