diff --git a/barretenberg/cpp/src/barretenberg/api/api_client_ivc.cpp b/barretenberg/cpp/src/barretenberg/api/api_client_ivc.cpp index 45debce95541..525ec4a9e6a7 100644 --- a/barretenberg/cpp/src/barretenberg/api/api_client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/api/api_client_ivc.cpp @@ -130,7 +130,8 @@ bool ClientIVCAPI::prove_and_verify(const std::filesystem::path& input_path) std::shared_ptr ivc = steps.accumulate(); // Construct the hiding kernel as the final step of the IVC - const bool verified = ivc->prove_and_verify(); + auto proof = ivc->prove(); + const bool verified = ClientIVC::verify(proof, ivc->get_vk()); return verified; } diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_client_ivc.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_client_ivc.cpp index a511fc2c4b71..d7e9fc73458e 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_client_ivc.cpp @@ -86,7 +86,8 @@ ClientIvcProve::Response ClientIvcProve::execute(BBApiRequest& request) && // We verify this proof. Another bb call to verify has some overhead of loading VK/proof/SRS, // and it is mysterious if this transaction fails later in the lifecycle. info("ClientIvcProve - verifying the generated proof as a sanity check"); - if (!request.ivc_in_progress->verify(proof)) { + ClientIVC::VerificationKey vk = request.ivc_in_progress->get_vk(); + if (!ClientIVC::verify(proof, vk)) { throw_or_abort("Failed to verify the generated proof!"); } diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index 97c739ffcb4a..324b8b705bb8 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -172,8 +172,6 @@ ClientIVC::perform_recursive_verification_and_databus_consistency_checks( } case QUEUE_TYPE::PG: case QUEUE_TYPE::PG_TAIL: { - BB_ASSERT_NEQ(input_verifier_accumulator, nullptr); - output_verifier_accumulator = perform_pg_recursive_verification(circuit, input_verifier_accumulator, verifier_instance, @@ -184,18 +182,10 @@ ClientIVC::perform_recursive_verification_and_databus_consistency_checks( break; } case QUEUE_TYPE::PG_FINAL: { - BB_ASSERT_NEQ(input_verifier_accumulator, nullptr); BB_ASSERT_EQ(stdlib_verification_queue.size(), size_t(1)); hide_op_queue_accumulation_result(circuit); - // Propagate the public inputs of the tail kernel by converting them to public inputs of the hiding circuit. - auto num_public_inputs = static_cast(honk_vk->num_public_inputs); - num_public_inputs -= KernelIO::PUBLIC_INPUTS_SIZE; // exclude fixed kernel_io public inputs - for (size_t i = 0; i < num_public_inputs; i++) { - verifier_inputs.proof[i].set_public(); - } - auto final_verifier_accumulator = perform_pg_recursive_verification(circuit, input_verifier_accumulator, verifier_instance, @@ -245,7 +235,7 @@ ClientIVC::perform_recursive_verification_and_databus_consistency_checks( kernel_input.output_pg_accum_hash.assert_equal(*prev_accum_hash); if (!is_hiding_kernel) { - // The hiding kernel has no return data but uses the traditional public-inputs mechanism + // The hiding kernel has no return data; it uses the traditional public-inputs mechanism bus_depot.set_kernel_return_data_commitment(witness_commitments.return_data); } } else { @@ -384,7 +374,7 @@ HonkProof ClientIVC::construct_oink_proof(const std::shared_ptrgate_challenges = prover_accumulation_transcript->template get_powers_of_challenge("gate_challenge", CONST_PG_LOG_N); - fold_output.accumulator = proving_key; // initialize the prover accum with the completed key + prover_accumulator = proving_key; // initialize the prover accum with the completed key HonkProof oink_proof = oink_prover.export_proof(); vinfo("oink proof constructed"); @@ -406,13 +396,14 @@ HonkProof ClientIVC::construct_pg_proof(const std::shared_ptr info("Accumulator hash in PG prover: ", accum_hash); } auto verifier_instance = std::make_shared>(honk_vk); - FoldingProver folding_prover({ fold_output.accumulator, proving_key }, + FoldingProver folding_prover({ prover_accumulator, proving_key }, { native_verifier_accum, verifier_instance }, transcript, trace_usage_tracker); - fold_output = folding_prover.prove(); + auto output = folding_prover.prove(); + prover_accumulator = output.accumulator; // update the prover accumulator vinfo("pg proof constructed"); - return fold_output.proof; + return output.proof; } /** @@ -472,8 +463,6 @@ void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptrcommitment_key = bn254_commitment_key; trace_usage_tracker.update(circuit); - honk_vk = precomputed_vk; - // We're accumulating a kernel if the verification queue is empty (because the kernel circuit contains recursive // verifiers for all the entries previously present in the verification queue) and if it's not the first accumulate // call (which will always be for an app circuit). @@ -495,22 +484,22 @@ void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr& verification_key) { // Note: a structured trace is not used for the hiding kernel auto hiding_decider_pk = std::make_shared(circuit, TraceSettings(), bn254_commitment_key); - honk_vk = std::make_shared(hiding_decider_pk->get_precomputed()); - auto& hiding_circuit_vk = honk_vk; + // Hiding circuit is proven by a MegaZKProver - MegaZKProver prover(hiding_decider_pk, hiding_circuit_vk, transcript); + MegaZKProver prover(hiding_decider_pk, verification_key, transcript); HonkProof proof = prover.construct_proof(); return proof; @@ -633,7 +622,7 @@ HonkProof ClientIVC::construct_mega_proof_for_hiding_kernel(ClientCircuit& circu ClientIVC::Proof ClientIVC::prove() { // deallocate the protogalaxy accumulator - fold_output.accumulator = nullptr; + prover_accumulator = nullptr; auto mega_proof = verification_queue.front().proof; // A transcript is shared between the Hiding circuit prover and the Goblin prover @@ -669,17 +658,6 @@ bool ClientIVC::verify(const Proof& proof, const VerificationKey& vk) return goblin_verified && mega_verified; } -/** - * @brief Verify a full proof of the IVC - * - * @param proof - * @return bool - */ -bool ClientIVC::verify(const Proof& proof) const -{ - return verify(proof, get_vk()); -} - /** * @brief Internal method for constructing a decider proof * @@ -688,36 +666,12 @@ bool ClientIVC::verify(const Proof& proof) const HonkProof ClientIVC::construct_decider_proof(const std::shared_ptr& transcript) { vinfo("prove decider..."); - fold_output.accumulator->commitment_key = bn254_commitment_key; - MegaDeciderProver decider_prover(fold_output.accumulator, transcript); + prover_accumulator->commitment_key = bn254_commitment_key; + MegaDeciderProver decider_prover(prover_accumulator, transcript); decider_prover.construct_proof(); return decider_prover.export_proof(); } -/** - * @brief Construct and verify a proof for the IVC - * @note Use of this method only makes sense when the prover and verifier are the same entity, e.g. in - * development/testing. - * - */ -bool ClientIVC::prove_and_verify() -{ - auto start = std::chrono::steady_clock::now(); - const auto proof = prove(); - auto end = std::chrono::steady_clock::now(); - auto diff = std::chrono::duration_cast(end - start); - vinfo("time to call ClientIVC::prove: ", diff.count(), " ms."); - - start = end; - const bool verified = verify(proof); - end = std::chrono::steady_clock::now(); - - diff = std::chrono::duration_cast(end - start); - vinfo("time to verify ClientIVC proof: ", diff.count(), " ms."); - - return verified; -} - // Proof methods size_t ClientIVC::Proof::size() const { @@ -842,7 +796,12 @@ ClientIVC::Proof ClientIVC::Proof::from_file_msgpack(const std::string& filename // VerificationKey construction ClientIVC::VerificationKey ClientIVC::get_vk() const { - return { honk_vk, std::make_shared(), std::make_shared() }; + BB_ASSERT_EQ(verification_queue.size(), 1UL); + BB_ASSERT_EQ(verification_queue.front().type == QUEUE_TYPE::MEGA, true); + auto verification_key = verification_queue.front().honk_vk; + return { verification_key, + std::make_shared(), + std::make_shared() }; } void ClientIVC::update_native_verifier_accumulator(const VerifierInputs& queue_entry, diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp index 5b61c7f278a1..de5d3c202ea3 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp @@ -257,8 +257,6 @@ class ClientIVC { ExecutionTraceUsageTracker trace_usage_tracker; private: - using ProverFoldOutput = FoldingResult; - // Transcript for CIVC prover (shared between Hiding circuit, Merge, ECCVM, and Translator) std::shared_ptr transcript = std::make_shared(); @@ -269,14 +267,13 @@ class ClientIVC { public: size_t num_circuits_accumulated = 0; // number of circuits accumulated so far - ProverFoldOutput fold_output; // prover accumulator and fold proof - HonkProof decider_proof; // decider proof to be verified in the hiding circuit + std::shared_ptr prover_accumulator; // current PG prover accumulator instance + HonkProof decider_proof; // decider proof to be verified in the hiding circuit std::shared_ptr recursive_verifier_native_accum; // native verifier accumulator used in recursive folding std::shared_ptr - native_verifier_accum; // native verifier accumulator used in prover folding - std::shared_ptr honk_vk; // honk vk to be completed and folded into the accumulator + native_verifier_accum; // native verifier accumulator used in prover folding // Set of tuples {proof, verification_key, type (Oink/PG)} to be recursively verified VerificationQueue verification_queue; @@ -327,14 +324,9 @@ class ClientIVC { static void hide_op_queue_accumulation_result(ClientCircuit& circuit); static void hide_op_queue_content_in_tail(ClientCircuit& circuit); static void hide_op_queue_content_in_hiding(ClientCircuit& circuit); - HonkProof construct_mega_proof_for_hiding_kernel(ClientCircuit& circuit); static bool verify(const Proof& proof, const VerificationKey& vk); - bool verify(const Proof& proof) const; - - bool prove_and_verify(); - HonkProof construct_decider_proof(const std::shared_ptr& transcript); VerificationKey get_vk() const; @@ -358,6 +350,9 @@ class ClientIVC { const std::shared_ptr& transcript, bool is_kernel); + HonkProof construct_honk_proof_for_hiding_kernel(ClientCircuit& circuit, + const std::shared_ptr& verification_key); + QUEUE_TYPE get_queue_type() const; static std::shared_ptr perform_oink_recursive_verification( diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp index eadf345d8aef..642b0f7d2412 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp @@ -67,10 +67,6 @@ class ClientIVCTests : public ::testing::Test { ClientIVC ivc{ num_circuits, trace_settings }; for (size_t j = 0; j < num_circuits; ++j) { - // Use default test settings for the mock hiding kernel since it's size must always be consistent - if (j == num_circuits - 1) { - settings = TestSettings{}; - } circuit_producer.construct_and_accumulate_next_circuit(ivc, settings); } return { ivc.prove(), ivc.get_vk() }; @@ -112,7 +108,8 @@ TEST_F(ClientIVCTests, BadProofFailure) for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { circuit_producer.construct_and_accumulate_next_circuit(ivc, settings); } - EXPECT_TRUE(ivc.prove_and_verify()); + auto proof = ivc.prove(); + EXPECT_TRUE(ClientIVC::verify(proof, ivc.get_vk())); } // The IVC throws an exception if the FIRST fold proof is tampered with @@ -139,7 +136,8 @@ TEST_F(ClientIVCTests, BadProofFailure) num_public_inputs); // tamper with first proof } } - EXPECT_FALSE(ivc.prove_and_verify()); + auto proof = ivc.prove(); + EXPECT_FALSE(ClientIVC::verify(proof, ivc.get_vk())); } // The IVC fails if the SECOND fold proof is tampered with @@ -160,7 +158,8 @@ TEST_F(ClientIVCTests, BadProofFailure) circuit.num_public_inputs()); // tamper with second proof } } - EXPECT_FALSE(ivc.prove_and_verify()); + auto proof = ivc.prove(); + EXPECT_FALSE(ClientIVC::verify(proof, ivc.get_vk())); } EXPECT_TRUE(true); @@ -313,7 +312,8 @@ TEST_F(ClientIVCTests, StructuredTraceOverflow) log2_num_gates += 1; } - EXPECT_TRUE(ivc.prove_and_verify()); + auto proof = ivc.prove(); + EXPECT_TRUE(ClientIVC::verify(proof, ivc.get_vk())); }; /** @@ -348,8 +348,9 @@ TEST_F(ClientIVCTests, DynamicTraceOverflow) ivc, { .log2_num_gates = test.log2_num_arith_gates[idx] }); } - EXPECT_EQ(check_accumulator_target_sum_manual(ivc.fold_output.accumulator), true); - EXPECT_TRUE(ivc.prove_and_verify()); + EXPECT_EQ(check_accumulator_target_sum_manual(ivc.prover_accumulator), true); + auto proof = ivc.prove(); + EXPECT_TRUE(ClientIVC::verify(proof, ivc.get_vk())); } } @@ -421,5 +422,6 @@ TEST_F(ClientIVCTests, DatabusFailure) ivc.accumulate(circuit, vk); } - EXPECT_FALSE(ivc.prove_and_verify()); + auto proof = ivc.prove(); + EXPECT_FALSE(ClientIVC::verify(proof, ivc.get_vk())); }; diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/mock_circuit_producer.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/mock_circuit_producer.hpp index 30f9d981d423..44478f28b85d 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/mock_circuit_producer.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/mock_circuit_producer.hpp @@ -202,6 +202,11 @@ class PrivateFunctionExecutionMockCircuitProducer { std::pair> create_next_circuit_and_vk(ClientIVC& ivc, TestSettings settings = {}) { + // If this is a mock hiding kernel, remove the settings and use a default (non-structured) trace + if (ivc.num_circuits_accumulated == ivc.get_num_circuits() - 1) { + settings = TestSettings{}; + ivc.trace_settings = TraceSettings{}; + } auto circuit = create_next_circuit(ivc, settings.log2_num_gates, settings.num_public_inputs); return { circuit, get_verification_key(circuit, ivc.trace_settings) }; } diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp index b4bacd16136e..cb6c7ecc3ae7 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp @@ -32,8 +32,12 @@ TEST_F(MockKernelTest, PinFoldingKernelSizes) auto [circuit, vk] = circuit_producer.create_next_circuit_and_vk(ivc); ivc.accumulate(circuit, vk); - EXPECT_TRUE(circuit.blocks.has_overflow); // trace overflow mechanism should be triggered + // Expect trace overflow for all but the hiding kernel (final circuit) + if (idx < NUM_CIRCUITS - 1) { + EXPECT_TRUE(circuit.blocks.has_overflow); + EXPECT_EQ(ivc.prover_accumulator->log_dyadic_size(), 19); + } else { + EXPECT_FALSE(circuit.blocks.has_overflow); + } } - - EXPECT_EQ(ivc.fold_output.accumulator->log_dyadic_size(), 19); } diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp index a034bcf98cd0..6584fcc3f0d0 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp @@ -13,23 +13,6 @@ namespace bb { -/** - * @brief Verify an IVC proof - * - */ -bool verify_ivc(ClientIVC::Proof& proof, ClientIVC& ivc) -{ - bool verified = ivc.verify(proof); - - // This is a benchmark, not a test, so just print success or failure to the log - if (verified) { - info("IVC successfully verified!"); - } else { - info("IVC failed to verify."); - } - return verified; -} - /** * @brief Perform a specified number of circuit accumulation rounds * diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp index e117af832254..e630574ef248 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp @@ -511,7 +511,7 @@ TEST_F(AcirIntegrationTest, DISABLED_ClientIVCMsgpackInputs) std::shared_ptr ivc = steps.accumulate(); ClientIVC::Proof proof = ivc->prove(); - EXPECT_TRUE(ivc->verify(proof)); + EXPECT_TRUE(ivc->verify(proof, ivc->get_vk())); } /** diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/pg_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/pg_recursion_constraint.cpp index a9fce6c6e8e9..418414f4b8c8 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/pg_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/pg_recursion_constraint.cpp @@ -162,8 +162,6 @@ void mock_ivc_accumulation(const std::shared_ptr& ivc, ClientIVC::QUE ivc->goblin.merge_verification_queue.emplace_back(acir_format::create_mock_merge_proof()); // If the type is PG_FINAL, we also need to populate the ivc instance with a mock decider proof if (type == ClientIVC::QUEUE_TYPE::PG_FINAL) { - // we have to create a mock honk vk - ivc->honk_vk = entry.honk_vk; ivc->decider_proof = acir_format::create_mock_decider_proof(); } ivc->num_circuits_accumulated++; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/pg_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/pg_recursion_constraint.test.cpp index db0b6ddf5c25..51a4229b1f65 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/pg_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/pg_recursion_constraint.test.cpp @@ -313,7 +313,8 @@ TEST_F(IvcRecursionConstraintTest, AccumulateSingleApp) // add the trailing kernels construct_and_accumulate_trailing_kernels(ivc, trace_settings); - EXPECT_TRUE(ivc->prove_and_verify()); + auto proof = ivc->prove(); + EXPECT_TRUE(ClientIVC::verify(proof, ivc->get_vk())); } /** @@ -343,7 +344,8 @@ TEST_F(IvcRecursionConstraintTest, AccumulateTwoApps) // Accumulate the trailing kernels construct_and_accumulate_trailing_kernels(ivc, trace_settings); - EXPECT_TRUE(ivc->prove_and_verify()); + auto proof = ivc->prove(); + EXPECT_TRUE(ClientIVC::verify(proof, ivc->get_vk())); } // Test generation of "init" kernel VK via dummy IVC data @@ -584,7 +586,8 @@ TEST_F(IvcRecursionConstraintTest, RecursiveVerifierAppCircuitTest) construct_and_accumulate_trailing_kernels(ivc, trace_settings); - EXPECT_TRUE(ivc->prove_and_verify()); + auto proof = ivc->prove(); + EXPECT_TRUE(ClientIVC::verify(proof, ivc->get_vk())); } /** @@ -607,5 +610,6 @@ TEST_F(IvcRecursionConstraintTest, BadRecursiveVerifierAppCircuitTest) construct_and_accumulate_trailing_kernels(ivc, trace_settings); // We expect the CIVC proof to fail due to the app with a failed UH recursive verification - EXPECT_FALSE(ivc->prove_and_verify()); + auto proof = ivc->prove(); + EXPECT_FALSE(ClientIVC::verify(proof, ivc->get_vk())); } diff --git a/noir-projects/noir-protocol-circuits/bootstrap.sh b/noir-projects/noir-protocol-circuits/bootstrap.sh index 119cb9081082..9b7e67c529c0 100755 --- a/noir-projects/noir-protocol-circuits/bootstrap.sh +++ b/noir-projects/noir-protocol-circuits/bootstrap.sh @@ -28,11 +28,11 @@ export circuits_hash=$(hash_str "$NOIR_HASH" $(cache_content_hash "^noir-project # Circuits matching these patterns we have client-ivc keys computed, rather than ultra-honk. readarray -t ivc_patterns < <(jq -r '.[]' "../client_ivc_circuits.json") -readarray -t ivc_tail_patterns < <(jq -r '.[]' "../client_ivc_tail_circuits.json") +ivc_hiding_pattern=("hiding") readarray -t rollup_honk_patterns < <(jq -r '.[]' "../rollup_honk_circuits.json") # Convert to regex string here and export for use in exported functions. export ivc_regex=$(IFS="|"; echo "${ivc_patterns[*]}") -export ivc_tail_regex=$(IFS="|"; echo "${ivc_tail_patterns[*]}") +export hiding_kernel_regex=$(IFS="|"; echo "${ivc_hiding_pattern[*]}") export rollup_honk_regex=$(IFS="|"; echo "${rollup_honk_patterns[*]}") function on_exit { @@ -90,7 +90,7 @@ function compile { local outdir=$(mktemp -d) trap "rm -rf $outdir" EXIT function write_vk { - if echo "$name" | grep -qE "${ivc_tail_regex}"; then + if echo "$name" | grep -qE "${hiding_kernel_regex}"; then # We still need the standalone IVC vk. We also create the final IVC vk from the tail (specifically, the number of public inputs is used from it). denoise "$BB write_vk --scheme client_ivc --verifier_type standalone_hiding -b - -o $outdir" elif echo "$name" | grep -qE "${ivc_regex}"; then @@ -130,7 +130,7 @@ function compile { echo_stderr "Root rollup verifier at: $verifier_path (${SECONDS}s)" # Include the verifier path if we create it. cache_upload vk-$hash.tar.gz $key_path $verifier_path &> /dev/null - elif echo "$name" | grep -qE "${ivc_tail_regex}"; then + elif echo "$name" | grep -qE "${hiding_kernel_regex}"; then # If we are a tail kernel circuit, we also need to generate the ivc vk. SECONDS=0 local ivc_vk_path="$key_dir/${name}.ivc.vk"