From dbb4a1205528bdd8217ea2d15ccf060e2aa9b7d2 Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Mon, 4 Dec 2023 12:26:31 -0700 Subject: [PATCH] chore: Recursive verifier updates (#3452) This work contains the following updates: 1. Previously we used only `check_circuit()` to ensure correctness of the Honk recursive verifier circuits. This is somewhat incomplete if the arithmetization of the circuit is Goblin Ultra Honk since `check_circuit()` is only complete for the conventional Ultra arithmetization and does not cover Goblin style ecc op gates or the DataBus functionality. This PR swaps out `check_circuit` for genuine proof construction/verification which is more time consuming but also more robust. 2. Updates constructor of recursive verifier to take a native verification key directly rather than requiring prior external construction of the stdlib verification key. 3. Adds a debug only method for ensuring all selectors have the same length. Note: Currently the two recursive verifier test suites share a lot of code. A follow on should potentially combine the two suites. --- .../flavor/goblin_ultra_recursive.hpp | 3 +- .../barretenberg/flavor/ultra_recursive.hpp | 3 +- .../arithmetization/arithmetization.hpp | 2 +- .../goblin_ultra_circuit_builder.cpp | 10 +-- .../goblin_ultra_circuit_builder.hpp | 2 +- .../circuit_builder/ultra_circuit_builder.cpp | 35 +++++++++ .../circuit_builder/ultra_circuit_builder.hpp | 19 +++++ .../honk/verifier/goblin_verifier.test.cpp | 78 ++++++++++++------- .../verifier/ultra_recursive_verifier.cpp | 4 +- .../verifier/ultra_recursive_verifier.hpp | 3 +- .../recursion/honk/verifier/verifier.test.cpp | 71 +++++++++++------ .../barretenberg/ultra_honk/ultra_prover.hpp | 3 - 12 files changed, 167 insertions(+), 66 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp index cbe48a27994..79b573b02e1 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp @@ -46,6 +46,7 @@ template class GoblinUltraRecursive_ { using Commitment = typename Curve::Element; using CommitmentHandle = typename Curve::Element; using FF = typename Curve::ScalarField; + using NativeVerificationKey = flavor::GoblinUltra::VerificationKey; // Note(luke): Eventually this may not be needed at all using VerifierCommitmentKey = pcs::VerifierCommitmentKey; @@ -106,7 +107,7 @@ template class GoblinUltraRecursive_ { * @param builder * @param native_key Native verification key from which to extract the precomputed commitments */ - VerificationKey(CircuitBuilder* builder, const auto& native_key) + VerificationKey(CircuitBuilder* builder, std::shared_ptr native_key) : VerificationKey_>(native_key->circuit_size, native_key->num_public_inputs) { diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp index 6caba7a2294..5f068a5c2d4 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp @@ -54,6 +54,7 @@ template class UltraRecursive_ { using Commitment = typename Curve::Element; using CommitmentHandle = typename Curve::Element; using FF = typename Curve::ScalarField; + using NativeVerificationKey = flavor::Ultra::VerificationKey; // Note(luke): Eventually this may not be needed at all using VerifierCommitmentKey = pcs::VerifierCommitmentKey; @@ -250,7 +251,7 @@ template class UltraRecursive_ { * @param builder * @param native_key Native verification key from which to extract the precomputed commitments */ - VerificationKey(CircuitBuilder* builder, auto native_key) + VerificationKey(CircuitBuilder* builder, std::shared_ptr native_key) : VerificationKey_>(native_key->circuit_size, native_key->num_public_inputs) { this->q_m = Commitment::from_witness(builder, native_key->q_m); diff --git a/barretenberg/cpp/src/barretenberg/proof_system/arithmetization/arithmetization.hpp b/barretenberg/cpp/src/barretenberg/proof_system/arithmetization/arithmetization.hpp index c396301f7ba..05121c857f4 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/arithmetization/arithmetization.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/arithmetization/arithmetization.hpp @@ -137,7 +137,7 @@ template class UltraHonk { SelectorType& q_elliptic() { return selectors[8]; }; SelectorType& q_aux() { return selectors[9]; }; SelectorType& q_lookup_type() { return selectors[10]; }; - SelectorType& q_busread() { return this->selectors[11]; }; + SelectorType& q_busread() { return selectors[11]; }; SelectorType& q_poseidon2_external() { return this->selectors[12]; }; SelectorType& q_poseidon2_internal() { return this->selectors[13]; }; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp index 108e16471e0..0ce5b9fdfa1 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp @@ -47,7 +47,7 @@ template void GoblinUltraCircuitBuilder_::add_gates_to_ensure_ this->w_l.emplace_back(public_calldata[read_idx]); // populate with value of calldata at read index this->w_r.emplace_back(this->add_variable(FF(read_idx))); // populate with read index as witness calldata_read_counts[read_idx]++; // increment read count at read index - q_busread.emplace_back(1); // read selector on + q_busread().emplace_back(1); // read selector on // populate all other components with zero this->w_o.emplace_back(this->zero_idx); @@ -84,7 +84,7 @@ template void GoblinUltraCircuitBuilder_::add_gates_to_ensure_ this->q_lookup_type.emplace_back(0); this->q_elliptic.emplace_back(0); this->q_aux.emplace_back(0); - this->q_busread.emplace_back(0); + this->q_busread().emplace_back(0); this->q_poseidon2_external.emplace_back(1); this->q_poseidon2_internal.emplace_back(1); @@ -106,7 +106,7 @@ template void GoblinUltraCircuitBuilder_::add_gates_to_ensure_ this->q_lookup_type.emplace_back(0); this->q_elliptic.emplace_back(0); this->q_aux.emplace_back(0); - this->q_busread.emplace_back(0); + this->q_busread().emplace_back(0); this->q_poseidon2_external.emplace_back(0); this->q_poseidon2_internal.emplace_back(0); @@ -264,7 +264,7 @@ void GoblinUltraCircuitBuilder_::create_poseidon2_external_gate(const poseid this->q_lookup_type.emplace_back(0); this->q_elliptic.emplace_back(0); this->q_aux.emplace_back(0); - this->q_busread.emplace_back(0); + this->q_busread().emplace_back(0); this->q_poseidon2_external.emplace_back(1); this->q_poseidon2_internal.emplace_back(0); ++this->num_gates; @@ -288,7 +288,7 @@ void GoblinUltraCircuitBuilder_::create_poseidon2_internal_gate(const poseid this->q_lookup_type.emplace_back(0); this->q_elliptic.emplace_back(0); this->q_aux.emplace_back(0); - this->q_busread.emplace_back(0); + this->q_busread().emplace_back(0); this->q_poseidon2_external.emplace_back(0); this->q_poseidon2_internal.emplace_back(1); ++this->num_gates; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp index af7ba39c8a5..e037fe2571d 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp @@ -42,7 +42,7 @@ template class GoblinUltraCircuitBuilder_ : public UltraCircuitBui WireVector& ecc_op_wire_3 = std::get<2>(ecc_op_wires); WireVector& ecc_op_wire_4 = std::get<3>(ecc_op_wires); - SelectorVector& q_busread = this->selectors.q_busread(); + SelectorVector& q_busread() { return this->selectors.q_busread(); }; SelectorVector& q_poseidon2_external = this->selectors.q_poseidon2_external(); SelectorVector& q_poseidon2_internal = this->selectors.q_poseidon2_internal(); diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.cpp index bb4278a7dea..39443c58c7f 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.cpp @@ -80,6 +80,7 @@ void UltraCircuitBuilder_::add_gates_to_ensure_all_polys_are_no q_elliptic.emplace_back(1); q_aux.emplace_back(1); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; // Some relations depend on wire shifts so we add another gate with @@ -140,6 +141,7 @@ void UltraCircuitBuilder_::create_add_gate(const add_triple_num_gates; } @@ -172,6 +174,7 @@ void UltraCircuitBuilder_::create_big_add_gate(const add_quad_< q_elliptic.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; } @@ -266,6 +269,7 @@ void UltraCircuitBuilder_::create_big_mul_gate(const mul_quad_< q_elliptic.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; } @@ -292,6 +296,7 @@ void UltraCircuitBuilder_::create_balanced_add_gate(const add_q q_elliptic.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; // Why 3? TODO: return to this // The purpose of this gate is to do enable lazy 32-bit addition. @@ -334,6 +339,7 @@ void UltraCircuitBuilder_::create_mul_gate(const mul_triple_num_gates; } /** @@ -363,6 +369,7 @@ void UltraCircuitBuilder_::create_bool_gate(const uint32_t vari q_elliptic.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; } @@ -394,6 +401,7 @@ void UltraCircuitBuilder_::create_poly_gate(const poly_triple_< q_elliptic.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; } @@ -448,6 +456,7 @@ void UltraCircuitBuilder_::create_ecc_add_gate(const ecc_add_ga q_elliptic.emplace_back(1); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; } w_l.emplace_back(in.x2); @@ -466,6 +475,7 @@ void UltraCircuitBuilder_::create_ecc_add_gate(const ecc_add_ga q_elliptic.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; } @@ -512,6 +522,7 @@ void UltraCircuitBuilder_::create_ecc_dbl_gate(const ecc_dbl_ga q_lookup_type.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; } @@ -531,6 +542,7 @@ void UltraCircuitBuilder_::create_ecc_dbl_gate(const ecc_dbl_ga q_elliptic.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; } @@ -561,6 +573,7 @@ void UltraCircuitBuilder_::fix_witness(const uint32_t witness_i q_elliptic.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; } @@ -636,6 +649,7 @@ plookup::ReadData UltraCircuitBuilder_::create_gates_ q_elliptic.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); ++this->num_gates; } return read_data; @@ -945,6 +959,7 @@ void UltraCircuitBuilder_::create_sort_constraint(const std::ve q_lookup_type.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); } // dummy gate needed because of sort widget's check of next row w_l.emplace_back(variable_index[variable_index.size() - 1]); @@ -964,6 +979,7 @@ void UltraCircuitBuilder_::create_sort_constraint(const std::ve q_lookup_type.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); } // useful to put variables in the witness that aren't already used - e.g. the dummy variables of the range constraint in @@ -998,6 +1014,7 @@ void UltraCircuitBuilder_::create_dummy_constraints(const std:: q_lookup_type.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); } } @@ -1029,6 +1046,7 @@ void UltraCircuitBuilder_::create_sort_constraint_with_edges( q_lookup_type.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); // enforce range check for middle rows for (size_t i = gate_width; i < variable_index.size() - gate_width; i += gate_width) { @@ -1049,6 +1067,7 @@ void UltraCircuitBuilder_::create_sort_constraint_with_edges( q_lookup_type.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); } // enforce range checks of last row and ending at end if (variable_index.size() > gate_width) { @@ -1069,6 +1088,7 @@ void UltraCircuitBuilder_::create_sort_constraint_with_edges( q_lookup_type.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); } // dummy gate needed because of sort widget's check of next row @@ -1090,6 +1110,7 @@ void UltraCircuitBuilder_::create_sort_constraint_with_edges( q_lookup_type.emplace_back(0); q_aux.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); } // range constraint a value by decomposing it into limbs whose size should be the default range constraint size @@ -1206,6 +1227,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } case AUX_SELECTORS::LIMB_ACCUMULATE_2: { @@ -1217,6 +1239,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } case AUX_SELECTORS::NON_NATIVE_FIELD_1: { @@ -1228,6 +1251,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } case AUX_SELECTORS::NON_NATIVE_FIELD_2: { @@ -1239,6 +1263,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } case AUX_SELECTORS::NON_NATIVE_FIELD_3: { @@ -1250,6 +1275,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } case AUX_SELECTORS::ROM_CONSISTENCY_CHECK: { @@ -1265,6 +1291,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } case AUX_SELECTORS::RAM_CONSISTENCY_CHECK: { @@ -1281,6 +1308,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); q_arith.emplace_back(1); selectors.pad_additional(); + check_selector_length_consistency(); break; } case AUX_SELECTORS::RAM_TIMESTAMP_CHECK: { @@ -1294,6 +1322,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } case AUX_SELECTORS::ROM_READ: { @@ -1308,6 +1337,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); // read/write flag stored in q_c q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } case AUX_SELECTORS::RAM_READ: { @@ -1322,6 +1352,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); // read/write flag stored in q_c q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } case AUX_SELECTORS::RAM_WRITE: { @@ -1336,6 +1367,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(1); // read/write flag stored in q_c q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } default: { @@ -1347,6 +1379,7 @@ void UltraCircuitBuilder_::apply_aux_selectors(const AUX_SELECT q_c.emplace_back(0); q_arith.emplace_back(0); selectors.pad_additional(); + check_selector_length_consistency(); break; } } @@ -1869,6 +1902,7 @@ std::array UltraCircuitBuilder_::evaluate_non_nati q_aux.emplace_back(0); selectors.pad_additional(); } + check_selector_length_consistency(); this->num_gates += 4; return std::array{ @@ -1991,6 +2025,7 @@ std::array UltraCircuitBuilder_::evaluate_non_nati q_aux.emplace_back(0); selectors.pad_additional(); } + check_selector_length_consistency(); this->num_gates += 4; return std::array{ diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp index 8f332551832..9767781e72d 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp @@ -678,6 +678,25 @@ class UltraCircuitBuilder_ : public CircuitBuilderBase class GoblinRecursiveVerifierTest : public testing::Test { - // Define types relevant for inner circuit - using Flavor = ::proof_system::honk::flavor::GoblinUltra; - using InnerComposer = ::proof_system::honk::UltraComposer_; + // Define types relevant for testing + using UltraFlavor = ::proof_system::honk::flavor::Ultra; + using GoblinUltraFlavor = ::proof_system::honk::flavor::GoblinUltra; + using UltraComposer = ::proof_system::honk::UltraComposer_; + using GoblinUltraComposer = ::proof_system::honk::UltraComposer_; + + // Define types for the inner circuit, i.e. the circuit whose proof will be recursively verified + using InnerFlavor = GoblinUltraFlavor; + using InnerComposer = GoblinUltraComposer; using InnerBuilder = typename InnerComposer::CircuitBuilder; - using NativeVerifier = ::proof_system::honk::UltraVerifier_<::proof_system::honk::flavor::Ultra>; using InnerCurve = bn254; + using InnerCommitment = InnerFlavor::Commitment; + using InnerFF = InnerFlavor::FF; // Types for recursive verifier circuit using RecursiveFlavor = ::proof_system::honk::flavor::GoblinUltraRecursive_; @@ -31,6 +38,16 @@ template class GoblinRecursiveVerifierTest : public testi using OuterBuilder = BuilderType; using VerificationKey = typename RecursiveVerifier::VerificationKey; + // Helper for getting composer for prover/verifier of recursive (outer) circuit + template static auto get_outer_composer() + { + if constexpr (IsGoblinBuilder) { + return GoblinUltraComposer(); + } else { + return UltraComposer(); + } + } + /** * @brief Create a non-trivial arbitrary inner circuit, the proof of which will be recursively verified * @@ -130,25 +147,25 @@ template class GoblinRecursiveVerifierTest : public testi { // Create an arbitrary inner circuit auto inner_circuit = create_inner_circuit(); + OuterBuilder outer_circuit; // Compute native verification key InnerComposer inner_composer; auto instance = inner_composer.create_instance(inner_circuit); auto prover = inner_composer.create_prover(instance); // A prerequisite for computing VK - // Instantiate the recursive verification key from the native verification key - OuterBuilder outer_circuit; - auto verification_key = std::make_shared(&outer_circuit, instance->verification_key); + // Instantiate the recursive verifier using the native verification key + RecursiveVerifier verifier{ &outer_circuit, instance->verification_key }; // Spot check some values in the recursive VK to ensure it was constructed correctly - EXPECT_EQ(verification_key->circuit_size, instance->verification_key->circuit_size); - EXPECT_EQ(verification_key->log_circuit_size, instance->verification_key->log_circuit_size); - EXPECT_EQ(verification_key->num_public_inputs, instance->verification_key->num_public_inputs); - EXPECT_EQ(verification_key->q_m.get_value(), instance->verification_key->q_m); - EXPECT_EQ(verification_key->q_r.get_value(), instance->verification_key->q_r); - EXPECT_EQ(verification_key->sigma_1.get_value(), instance->verification_key->sigma_1); - EXPECT_EQ(verification_key->id_3.get_value(), instance->verification_key->id_3); - EXPECT_EQ(verification_key->lagrange_ecc_op.get_value(), instance->verification_key->lagrange_ecc_op); + EXPECT_EQ(verifier.key->circuit_size, instance->verification_key->circuit_size); + EXPECT_EQ(verifier.key->log_circuit_size, instance->verification_key->log_circuit_size); + EXPECT_EQ(verifier.key->num_public_inputs, instance->verification_key->num_public_inputs); + EXPECT_EQ(verifier.key->q_m.get_value(), instance->verification_key->q_m); + EXPECT_EQ(verifier.key->q_r.get_value(), instance->verification_key->q_r); + EXPECT_EQ(verifier.key->sigma_1.get_value(), instance->verification_key->sigma_1); + EXPECT_EQ(verifier.key->id_3.get_value(), instance->verification_key->id_3); + EXPECT_EQ(verifier.key->lagrange_ecc_op.get_value(), instance->verification_key->lagrange_ecc_op); } /** @@ -168,15 +185,13 @@ template class GoblinRecursiveVerifierTest : public testi // Create a recursive verification circuit for the proof of the inner circuit OuterBuilder outer_circuit; - auto verification_key = std::make_shared(&outer_circuit, instance->verification_key); - RecursiveVerifier verifier(&outer_circuit, verification_key); + RecursiveVerifier verifier{ &outer_circuit, instance->verification_key }; auto pairing_points = verifier.verify_proof(inner_proof); - // Check the recursive verifier circuit + // Check for a failure flag in the recursive verifier circuit EXPECT_EQ(outer_circuit.failed(), false) << outer_circuit.err(); - EXPECT_TRUE(outer_circuit.check_circuit()); - // Additional check 1: Perform native verification then perform the pairing on the outputs of the recursive + // Check 1: Perform native verification then perform the pairing on the outputs of the recursive // verifier and check that the result agrees. auto native_verifier = inner_composer.create_verifier(instance); auto native_result = native_verifier.verify_proof(inner_proof); @@ -184,15 +199,25 @@ template class GoblinRecursiveVerifierTest : public testi pairing_points[1].get_value()); EXPECT_EQ(recursive_result, native_result); - // Additional check 2: Ensure that the underlying native and recursive verification algorithms agree by ensuring + // Check 2: Ensure that the underlying native and recursive verification algorithms agree by ensuring // the manifests produced by each agree. auto recursive_manifest = verifier.transcript->get_manifest(); auto native_manifest = native_verifier.transcript->get_manifest(); - // recursive_manifest.print(); - // native_manifest.print(); for (size_t i = 0; i < recursive_manifest.size(); ++i) { EXPECT_EQ(recursive_manifest[i], native_manifest[i]); } + + // Check 3: Construct and verify a proof of the recursive verifier circuit + { + auto composer = get_outer_composer(); + auto instance = composer.create_instance(outer_circuit); + auto prover = composer.create_prover(instance); + auto verifier = composer.create_verifier(instance); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + + ASSERT(verified); + } } /** @@ -214,14 +239,13 @@ template class GoblinRecursiveVerifierTest : public testi // Arbitrarily tamper with the proof to be verified inner_prover.transcript->deserialize_full_transcript(); - inner_prover.transcript->sorted_accum_comm = Flavor::Commitment::one() * Flavor::FF::random_element(); + inner_prover.transcript->sorted_accum_comm = InnerCommitment::one() * InnerFF::random_element(); inner_prover.transcript->serialize_full_transcript(); inner_proof = inner_prover.export_proof(); // Create a recursive verification circuit for the proof of the inner circuit OuterBuilder outer_circuit; - auto verification_key = std::make_shared(&outer_circuit, instance->verification_key); - RecursiveVerifier verifier(&outer_circuit, verification_key); + RecursiveVerifier verifier{ &outer_circuit, instance->verification_key }; verifier.verify_proof(inner_proof); // We expect the circuit check to fail due to the bad proof diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp index 1a67be711dd..785f2c8331a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp @@ -9,8 +9,8 @@ namespace proof_system::plonk::stdlib::recursion::honk { template UltraRecursiveVerifier_::UltraRecursiveVerifier_(Builder* builder, - std::shared_ptr verifier_key) - : key(verifier_key) + std::shared_ptr native_verifier_key) + : key(std::make_shared(builder, native_verifier_key)) , builder(builder) {} diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp index 01e567c667d..67b5411025b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp @@ -14,11 +14,12 @@ template class UltraRecursiveVerifier_ { using Commitment = typename Flavor::Commitment; using GroupElement = typename Flavor::GroupElement; using VerificationKey = typename Flavor::VerificationKey; + using NativeVerificationKey = typename Flavor::NativeVerificationKey; using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; using Builder = typename Flavor::CircuitBuilder; using PairingPoints = std::array; - explicit UltraRecursiveVerifier_(Builder* builder, std::shared_ptr verifier_key = nullptr); + explicit UltraRecursiveVerifier_(Builder* builder, std::shared_ptr native_verifier_key); UltraRecursiveVerifier_(UltraRecursiveVerifier_&& other) = delete; UltraRecursiveVerifier_(const UltraRecursiveVerifier_& other) = delete; UltraRecursiveVerifier_& operator=(const UltraRecursiveVerifier_& other) = delete; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/verifier.test.cpp index ddb345b972b..3b045a35a73 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/verifier.test.cpp @@ -18,12 +18,18 @@ namespace proof_system::plonk::stdlib::recursion::honk { */ template class RecursiveVerifierTest : public testing::Test { - // Define types relevant for inner circuit - using Flavor = ::proof_system::honk::flavor::Ultra; - using InnerComposer = ::proof_system::honk::UltraComposer_; + // Define types relevant for testing + using UltraFlavor = ::proof_system::honk::flavor::Ultra; + using GoblinUltraFlavor = ::proof_system::honk::flavor::GoblinUltra; + using UltraComposer = ::proof_system::honk::UltraComposer_; + using GoblinUltraComposer = ::proof_system::honk::UltraComposer_; + + using InnerFlavor = UltraFlavor; + using InnerComposer = UltraComposer; using InnerBuilder = typename InnerComposer::CircuitBuilder; - using NativeVerifier = ::proof_system::honk::UltraVerifier_<::proof_system::honk::flavor::Ultra>; using InnerCurve = bn254; + using Commitment = InnerFlavor::Commitment; + using FF = InnerFlavor::FF; // Types for recursive verifier circuit using RecursiveFlavor = ::proof_system::honk::flavor::UltraRecursive_; @@ -31,6 +37,16 @@ template class RecursiveVerifierTest : public testing::Te using OuterBuilder = BuilderType; using VerificationKey = typename RecursiveVerifier::VerificationKey; + // Helper for getting composer for prover/verifier of recursive (outer) circuit + template static auto get_outer_composer() + { + if constexpr (IsGoblinBuilder) { + return GoblinUltraComposer(); + } else { + return UltraComposer(); + } + } + /** * @brief Create a non-trivial arbitrary inner circuit, the proof of which will be recursively verified * @@ -121,17 +137,17 @@ template class RecursiveVerifierTest : public testing::Te auto instance = inner_composer.create_instance(inner_circuit); auto prover = inner_composer.create_prover(instance); // A prerequisite for computing VK - // Instantiate the recursive verification key from the native verification key - auto verification_key = std::make_shared(&outer_circuit, instance->verification_key); + // Instantiate the recursive verifier using the native verification key + RecursiveVerifier verifier{ &outer_circuit, instance->verification_key }; // Spot check some values in the recursive VK to ensure it was constructed correctly - EXPECT_EQ(verification_key->circuit_size, instance->verification_key->circuit_size); - EXPECT_EQ(verification_key->log_circuit_size, instance->verification_key->log_circuit_size); - EXPECT_EQ(verification_key->num_public_inputs, instance->verification_key->num_public_inputs); - EXPECT_EQ(verification_key->q_m.get_value(), instance->verification_key->q_m); - EXPECT_EQ(verification_key->q_r.get_value(), instance->verification_key->q_r); - EXPECT_EQ(verification_key->sigma_1.get_value(), instance->verification_key->sigma_1); - EXPECT_EQ(verification_key->id_3.get_value(), instance->verification_key->id_3); + EXPECT_EQ(verifier.key->circuit_size, instance->verification_key->circuit_size); + EXPECT_EQ(verifier.key->log_circuit_size, instance->verification_key->log_circuit_size); + EXPECT_EQ(verifier.key->num_public_inputs, instance->verification_key->num_public_inputs); + EXPECT_EQ(verifier.key->q_m.get_value(), instance->verification_key->q_m); + EXPECT_EQ(verifier.key->q_r.get_value(), instance->verification_key->q_r); + EXPECT_EQ(verifier.key->sigma_1.get_value(), instance->verification_key->sigma_1); + EXPECT_EQ(verifier.key->id_3.get_value(), instance->verification_key->id_3); } /** @@ -152,15 +168,13 @@ template class RecursiveVerifierTest : public testing::Te // Create a recursive verification circuit for the proof of the inner circuit OuterBuilder outer_circuit; - auto verification_key = std::make_shared(&outer_circuit, instance->verification_key); - RecursiveVerifier verifier(&outer_circuit, verification_key); + RecursiveVerifier verifier{ &outer_circuit, instance->verification_key }; auto pairing_points = verifier.verify_proof(inner_proof); - // Check the recursive verifier circuit + // Check for a failure flag in the recursive verifier circuit EXPECT_EQ(outer_circuit.failed(), false) << outer_circuit.err(); - EXPECT_TRUE(outer_circuit.check_circuit()); - // Additional check 1: Perform native verification then perform the pairing on the outputs of the recursive + // Check 1: Perform native verification then perform the pairing on the outputs of the recursive // verifier and check that the result agrees. auto native_verifier = inner_composer.create_verifier(instance); auto native_result = native_verifier.verify_proof(inner_proof); @@ -168,15 +182,25 @@ template class RecursiveVerifierTest : public testing::Te pairing_points[1].get_value()); EXPECT_EQ(recursive_result, native_result); - // Additional check 2: Ensure that the underlying native and recursive verification algorithms agree by ensuring + // Check 2: Ensure that the underlying native and recursive verification algorithms agree by ensuring // the manifests produced by each agree. auto recursive_manifest = verifier.transcript->get_manifest(); auto native_manifest = native_verifier.transcript->get_manifest(); - // recursive_manifest.print(); - // native_manifest.print(); for (size_t i = 0; i < recursive_manifest.size(); ++i) { EXPECT_EQ(recursive_manifest[i], native_manifest[i]); } + + // Check 3: Construct and verify a proof of the recursive verifier circuit + { + auto composer = get_outer_composer(); + auto instance = composer.create_instance(outer_circuit); + auto prover = composer.create_prover(instance); + auto verifier = composer.create_verifier(instance); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + + ASSERT(verified); + } } /** @@ -199,14 +223,13 @@ template class RecursiveVerifierTest : public testing::Te // Arbitrarily tamper with the proof to be verified inner_prover.transcript->deserialize_full_transcript(); - inner_prover.transcript->sorted_accum_comm = Flavor::Commitment::one() * Flavor::FF::random_element(); + inner_prover.transcript->sorted_accum_comm = Commitment::one() * FF::random_element(); inner_prover.transcript->serialize_full_transcript(); inner_proof = inner_prover.export_proof(); // Create a recursive verification circuit for the proof of the inner circuit OuterBuilder outer_circuit; - auto verification_key = std::make_shared(&outer_circuit, instance->verification_key); - RecursiveVerifier verifier(&outer_circuit, verification_key); + RecursiveVerifier verifier{ &outer_circuit, instance->verification_key }; verifier.verify_proof(inner_proof); // We expect the circuit check to fail due to the bad proof diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp index fb81addb7cc..dece05e520c 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp @@ -42,9 +42,6 @@ template class UltraProver_ { std::shared_ptr transcript; - std::vector public_inputs; - size_t pub_inputs_offset; - proof_system::RelationParameters relation_parameters; CommitmentLabels commitment_labels;