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// Test parameters: <RecursiveFlavor, IO>
15// IO determines the public inputs structure (DefaultIO or RollupIO) for both inner and outer circuits
16template <typename RecursiveFlavor_, typename IO_> struct RecursiveVerifierTestParams {
17 using RecursiveFlavor = RecursiveFlavor_;
18 using IO = IO_;
19};
20
21// Run the recursive verifier tests with conventional Ultra builder and Goblin builder
22// Note: UltraRecursiveFlavor_<UltraCircuitBuilder> + RollupIO covers the rollup case
23using TestConfigs = testing::Types<
33
42template <typename Params> class RecursiveVerifierTest : public testing::Test {
43
44 using RecursiveFlavor = typename Params::RecursiveFlavor;
45 using IO = typename Params::IO;
46
47 // Define types for the inner circuit, i.e. the circuit whose proof will be recursively verified
48 using InnerFlavor = typename RecursiveFlavor::NativeFlavor;
50 using InnerBuilder = typename InnerFlavor::CircuitBuilder;
52 using InnerCommitment = InnerFlavor::Commitment;
53 using InnerFF = InnerFlavor::FF;
55
56 // IO types: InnerIO uses InnerBuilder, OuterIO uses OuterBuilder
60
61 // Defines types for the outer circuit, i.e. the circuit of the recursive verifier
62 using OuterBuilder = typename RecursiveFlavor::CircuitBuilder;
68 using OuterIO = IO;
69
70 // RecursiveVerifier uses IO that matches the test's IO type
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 (IO::HasIPA) {
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 (IO::HasIPA) {
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
265 bool result = output.points_accumulator.check();
266 info("input pairing points result: ", result);
267 EXPECT_EQ(result, native_result);
268
269 // Check 2: Ensure that the underlying native and recursive verification algorithms agree by ensuring
270 // the manifests produced by each agree.
271 auto recursive_manifest = verifier.get_transcript()->get_manifest();
272 auto native_manifest = native_verifier.get_transcript()->get_manifest();
273 for (size_t i = 0; i < recursive_manifest.size(); ++i) {
274 EXPECT_EQ(recursive_manifest[i], native_manifest[i]);
275 }
276
277 // Check 3: Construct and verify a proof of the recursive verifier circuit
278 {
279 auto prover_instance = std::make_shared<OuterProverInstance>(outer_circuit);
280 auto verification_key =
281 std::make_shared<typename OuterFlavor::VerificationKey>(prover_instance->get_precomputed());
282 info("Recursive Verifier: num gates = ", outer_circuit.get_num_finalized_gates());
283 OuterProver prover(prover_instance, verification_key);
284 // construct_proof() already returns combined proof (honk + IPA) for rollup flavors
285 auto proof = prover.construct_proof();
286 auto outer_vk_and_hash = std::make_shared<typename OuterFlavor::VKAndHash>(verification_key);
287 OuterVerifier verifier(outer_vk_and_hash);
288 bool result = verifier.verify_proof(proof).result;
289 ASSERT_TRUE(result);
290 }
291 // Check the size of the recursive verifier
293 const auto expected_gate_count = std::get<0>(acir_format::HONK_RECURSION_CONSTANTS<RecursiveFlavor>());
294 ASSERT_EQ(outer_circuit.get_num_finalized_gates(), expected_gate_count)
295 << "MegaZKHonk Recursive verifier changed in Ultra gate count! Update this value if you "
296 "are sure this is expected.";
297 }
298 }
299
306 {
307 for (size_t idx = 0; idx < static_cast<size_t>(TamperType::END); idx++) {
308 // Create an arbitrary inner circuit
309 auto inner_circuit = create_inner_circuit();
310
311 // Generate a proof over the inner circuit
312 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
313 // Generate the corresponding inner verification key
314 auto inner_verification_key =
315 std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->get_precomputed());
316 InnerProver inner_prover(prover_instance, inner_verification_key);
317 auto inner_proof = inner_prover.construct_proof();
318
319 // Tamper with the proof to be verified
320 TamperType tamper_type = static_cast<TamperType>(idx);
321 tamper_with_proof<InnerProver, InnerFlavor>(inner_prover, inner_proof, tamper_type);
322
323 // Create a recursive verification circuit for the proof of the inner circuit
324 OuterBuilder outer_circuit;
325 auto stdlib_vk_and_hash =
326 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, inner_verification_key);
327 RecursiveVerifier verifier{ stdlib_vk_and_hash };
328 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
329 VerifierOutput output = verifier.verify_proof(stdlib_inner_proof);
330
331 // Wrong Gemini witnesses lead to the pairing check failure in non-ZK case but don't break any
332 // constraints. In ZK-cases, tampering with Gemini witnesses leads to SmallSubgroupIPA consistency check
333 // failure.
334 if ((tamper_type != TamperType::MODIFY_GEMINI_WITNESS) || (InnerFlavor::HasZK)) {
335 // We expect the circuit check to fail due to the bad proof.
336 EXPECT_FALSE(CircuitChecker::check(outer_circuit));
337 } else {
338 EXPECT_TRUE(CircuitChecker::check(outer_circuit));
339 EXPECT_FALSE(output.points_accumulator.check());
340 }
341 }
342 }
351
352 {
353 for (size_t idx = 0; idx < 2; idx++) {
354 // Create an arbitrary inner circuit
355 auto inner_circuit = create_inner_circuit();
356
357 // Generate a proof over the inner circuit
358 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
359 // Generate the corresponding inner verification key
360 auto inner_verification_key =
361 std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->get_precomputed());
362 InnerProver inner_prover(prover_instance, inner_verification_key);
363 auto inner_proof = inner_prover.construct_proof();
364
365 // Tamper with the proof to be verified
366 tamper_with_proof<InnerFlavor>(inner_proof, /*end_of_proof*/ static_cast<bool>(idx));
367
368 // Create a recursive verification circuit for the proof of the inner circuit
369 OuterBuilder outer_circuit;
370 auto stdlib_vk_and_hash =
371 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, inner_verification_key);
372 RecursiveVerifier verifier{ stdlib_vk_and_hash };
373 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
374 VerifierOutput output = verifier.verify_proof(stdlib_inner_proof);
375
376 if (idx == 0) {
377 // We expect the circuit check to fail due to the bad proof.
378 EXPECT_FALSE(CircuitChecker::check(outer_circuit));
379 } else {
380 // Wrong witnesses lead to the pairing check failure in non-ZK case but don't break any
381 // constraints. In ZK-cases, tampering with Gemini witnesses leads to SmallSubgroupIPA consistency check
382 // failure.
383 EXPECT_TRUE(CircuitChecker::check(outer_circuit));
384 EXPECT_FALSE(output.points_accumulator.check());
385 }
386 }
387 }
388
399 {
400 // Create an arbitrary inner circuit
401 auto inner_circuit = create_inner_circuit();
402
403 // Generate a proof over the inner circuit
404 auto prover_instance = std::make_shared<InnerProverInstance>(inner_circuit);
405 auto verification_key =
406 std::make_shared<typename InnerFlavor::VerificationKey>(prover_instance->get_precomputed());
407 InnerProver inner_prover(prover_instance, verification_key);
408 auto inner_proof = inner_prover.construct_proof();
409
410 // Create a recursive verification circuit for the proof of the inner circuit
411 OuterBuilder outer_circuit;
412 auto stdlib_vk_and_hash =
413 std::make_shared<typename RecursiveFlavor::VKAndHash>(outer_circuit, verification_key);
414 RecursiveVerifier verifier{ stdlib_vk_and_hash };
415
416 // Fix witness for VK fields to ensure they're properly constrained
417 verifier.get_verifier_instance()->vk_and_hash->vk->num_public_inputs.fix_witness();
418 verifier.get_verifier_instance()->vk_and_hash->vk->pub_inputs_offset.fix_witness();
419 verifier.get_verifier_instance()->vk_and_hash->vk->log_circuit_size.fix_witness();
420
421 OuterStdlibProof stdlib_inner_proof(outer_circuit, inner_proof);
422 VerifierOutput output = verifier.verify_proof(stdlib_inner_proof);
423 auto pairing_points = output.points_accumulator;
424
425 // The pairing points are public outputs from the recursive verifier that will be verified externally via a
426 // pairing check. While they are computed within the circuit (via batch_mul for P0 and negation for P1), their
427 // output coordinates may not appear in multiple constraint gates. Calling fix_witness() adds explicit
428 // constraints on these values. Without these constraints, the StaticAnalyzer detects unconstrained variables
429 // (coordinate limbs) that appear in only one gate. This ensures the pairing point coordinates are properly
430 // constrained within the circuit itself, rather than relying solely on them being public outputs.
431 pairing_points.fix_witness();
432
433 // For RollupIO: Fix the IPA claim's bigfield elements (challenge and evaluation).
434 // When reconstructed from public inputs, bigfield::construct_from_limbs creates a prime_basis_limb
435 // that's computed as a linear combination of the binary limbs. Since the IPA claim is just propagated, this
436 // prime_basis_limb appears in only one gate.
437 if constexpr (IO::HasIPA) {
438 output.ipa_claim.opening_pair.challenge.fix_witness();
439 output.ipa_claim.opening_pair.evaluation.fix_witness();
440 }
441
442 info("Recursive Verifier: num gates = ", outer_circuit.get_num_finalized_gates_inefficient());
443
444 // Check for a failure flag in the recursive verifier circuit
445 EXPECT_EQ(outer_circuit.failed(), false) << outer_circuit.err();
446
447 outer_circuit.finalize_circuit(false);
448
449 // Run static analysis to detect unconstrained variables
450 // Use the appropriate analyzer based on the outer builder type
451 using Analyzer =
453 auto graph = Analyzer(outer_circuit);
454 auto [cc, variables_in_one_gate] = graph.analyze_circuit(/*filter_cc=*/true);
455
456 // We expect exactly one connected component (all variables properly connected)
457 EXPECT_EQ(cc.size(), 1);
458
459 // Expected variables in one gate:
460 // - Base count of is_infinity booleans (MegaBuilder only, one per deserialized commitment)
461 // - +1 for unused Shplonk power (non-ZK flavors only)
462 //
463 // AUDITTODO: When using MegaBuilder as outer circuit, goblin_element::from_witness() creates
464 // is_point_at_infinity boolean witnesses for each deserialized commitment. These bools are only
465 // constrained to be 0/1 (via bool gate) but are not linked to the actual point coordinates.
466 size_t expected_unconstrained = 0;
467 if constexpr (IsMegaBuilder<OuterBuilder>) {
468 // Number of is_infinity booleans depends on number of commitments in the proof
469 if constexpr (IsAnyOf<RecursiveFlavor,
472 expected_unconstrained = 31; // Mega proofs have more commitments
473 } else {
474 expected_unconstrained = 28; // Ultra proofs have fewer commitments
475 }
476 }
477 // Add 1 for unused Shplonk power in non-ZK flavors
478 if constexpr (!RecursiveFlavor::HasZK) {
479 expected_unconstrained += 1;
480 }
481 EXPECT_EQ(variables_in_one_gate.size(), expected_unconstrained);
482 }
483};
484
486
488{
489 TestFixture::test_inner_circuit();
490}
491
492HEAVY_TYPED_TEST(RecursiveVerifierTest, RecursiveVerificationKey)
493{
494 TestFixture::test_recursive_verification_key_creation();
495}
496
497HEAVY_TYPED_TEST(RecursiveVerifierTest, SingleRecursiveVerification)
498{
499 TestFixture::test_recursive_verification();
500};
501
503{
504 using RecursiveFlavor = typename TypeParam::RecursiveFlavor;
505 if constexpr (IsAnyOf<RecursiveFlavor,
509 TestFixture::test_independent_vk_hash();
510 } else {
511 GTEST_SKIP() << "Not built for this parameter";
512 }
513};
514
515HEAVY_TYPED_TEST(RecursiveVerifierTest, SingleRecursiveVerificationFailure)
516{
517 TestFixture::test_recursive_verification_fails();
518};
519
526HEAVY_TYPED_TEST(RecursiveVerifierTest, GraphAnalysisOfRecursiveVerifier)
527{
528 TestFixture::test_recursive_verification_with_graph_analysis();
529};
530
531#ifdef DISABLE_HEAVY_TESTS
532// Null test
533TEST(RecursiveVerifierTest, DoNothingTestToEnsureATestExists) {}
534#endif
535} // namespace bb::stdlib::recursion::honk
The recursive counterpart to the "native" Mega flavor.
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.
const std::shared_ptr< Instance > & get_verifier_instance() const
Get the verifier instance (for accessing VK and witness commitments in Chonk/Goblin)
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.
static void test_recursive_verification()
Construct a recursive verification circuit for the proof of an inner circuit then call check_circuit ...
typename InnerFlavor::VerifierCommitmentKey NativeVerifierCommitmentKey
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.
std::conditional_t< IO::HasIPA, bb::RollupIO, bb::DefaultIO > NativeIO
static void test_inner_circuit()
Create inner circuit and call check_circuit on it.
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_independent_vk_hash()
Ensures that the recursive verifier circuit for two inner circuits of different size is the same as t...
std::conditional_t< IO::HasIPA, RollupIO, DefaultIO< InnerBuilder > > InnerIO
std::conditional_t< IsMegaBuilder< OuterBuilder >, MegaFlavor, UltraFlavor > OuterFlavor
static void test_recursive_verification_fails()
Construct verifier circuits for proofs whose data have been tampered with. Expect failure.
static void test_recursive_verification_with_graph_analysis()
Test recursive verification with static graph analysis to detect unconstrained variables.
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,...
typename RecursiveVerifier::VerificationKey VerificationKey
The data that is propagated on the public inputs of a rollup circuit.
#define info(...)
Definition log.hpp:93
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< RecursiveVerifierTestParams< MegaRecursiveFlavor_< MegaCircuitBuilder >, DefaultIO< MegaCircuitBuilder > >, RecursiveVerifierTestParams< MegaRecursiveFlavor_< UltraCircuitBuilder >, DefaultIO< UltraCircuitBuilder > >, RecursiveVerifierTestParams< UltraRecursiveFlavor_< UltraCircuitBuilder >, DefaultIO< UltraCircuitBuilder > >, RecursiveVerifierTestParams< UltraRecursiveFlavor_< UltraCircuitBuilder >, RollupIO >, RecursiveVerifierTestParams< UltraRecursiveFlavor_< MegaCircuitBuilder >, DefaultIO< MegaCircuitBuilder > >, RecursiveVerifierTestParams< UltraZKRecursiveFlavor_< UltraCircuitBuilder >, DefaultIO< UltraCircuitBuilder > >, RecursiveVerifierTestParams< UltraZKRecursiveFlavor_< MegaCircuitBuilder >, DefaultIO< MegaCircuitBuilder > >, RecursiveVerifierTestParams< MegaZKRecursiveFlavor_< MegaCircuitBuilder >, DefaultIO< MegaCircuitBuilder > >, RecursiveVerifierTestParams< MegaZKRecursiveFlavor_< UltraCircuitBuilder >, DefaultIO< UltraCircuitBuilder > > > TestConfigs
TYPED_TEST_SUITE(RecursiveVerifierTest, TestConfigs)
field< Bn254FrParams > fr
Definition fr.hpp:174
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
StaticAnalyzer_< bb::fr, bb::MegaCircuitBuilder > MegaStaticAnalyzer
Definition graph.hpp:189
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