diff --git a/barretenberg/cpp/src/barretenberg/benchmark/honk_bench/benchmark_utilities.hpp b/barretenberg/cpp/src/barretenberg/benchmark/honk_bench/benchmark_utilities.hpp index 6f223939a4b..0ef9fabfa0a 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/honk_bench/benchmark_utilities.hpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/honk_bench/benchmark_utilities.hpp @@ -218,7 +218,7 @@ void construct_proof_with_specified_num_iterations( Composer composer; for (auto _ : state) { - // Constuct circuit and prover; don't include this part in measurement + // Construct circuit and prover; don't include this part in measurement state.PauseTiming(); auto prover = get_prover(composer, test_circuit_function, num_iterations); state.ResumeTiming(); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp index 16d3f3a9cfb..f9b9fb6ea46 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp @@ -345,31 +345,34 @@ template void ECCVMProver_::execute_transcript_cons transcript->send_to_verifier("Translation:hack_evaluation", hack.evaluate(evaluation_challenge_x)); // Get another challenge for batching the univariate claims - FF batching_challenge = transcript->get_challenge("Translation:batching_challenge"); + FF ipa_batching_challenge = transcript->get_challenge("Translation:ipa_batching_challenge"); // Collect the polynomials and evaluations to be batched RefArray univariate_polynomials{ key->transcript_op, key->transcript_Px, key->transcript_Py, key->transcript_z1, key->transcript_z2, hack }; std::array univariate_evaluations; - // Constuct the batched polynomial and batched evaluation + // Construct the batched polynomial and batched evaluation Polynomial batched_univariate{ key->circuit_size }; FF batched_evaluation{ 0 }; auto batching_scalar = FF(1); for (auto [polynomial, eval] : zip_view(univariate_polynomials, univariate_evaluations)) { batched_univariate.add_scaled(polynomial, batching_scalar); batched_evaluation += eval * batching_scalar; - batching_scalar *= batching_challenge; + batching_scalar *= ipa_batching_challenge; } // Compute a proof for the batched univariate opening PCS::compute_opening_proof( commitment_key, { evaluation_challenge_x, batched_evaluation }, batched_univariate, transcript); + + // Get another challenge for batching the univariate claims + translation_batching_challenge_v = transcript->get_challenge("Translation:batching_challenge"); } template plonk::proof& ECCVMProver_::export_proof() { - proof.proof_data = transcript->proof_data; + proof.proof_data = transcript->export_proof(); return proof; } diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp index 39b42b89688..af6f4ec457f 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp @@ -69,6 +69,7 @@ template class ECCVMProver_ { Polynomial quotient_W; FF evaluation_challenge_x; + FF translation_batching_challenge_v; // to be rederived by the translator verifier sumcheck::SumcheckOutput sumcheck_output; pcs::gemini::ProverOutput gemini_output; diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp index d7f26ce7299..76058a9c800 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp @@ -253,16 +253,17 @@ template bool ECCVMVerifier_::verify_proof(const plonk transcript->template receive_from_prover("Translation:hack_evaluation") }; - FF batching_challenge = transcript->get_challenge("Translation:batching_challenge"); + // Get another challenge for batching the univariate claims + FF ipa_batching_challenge = transcript->get_challenge("Translation:ipa_batching_challenge"); - // Constuct batched commitment and batched evaluation + // Construct batched commitment and batched evaluation auto batched_commitment = transcript_commitments[0]; auto batched_transcript_eval = transcript_evaluations[0]; - auto batching_scalar = batching_challenge; + auto batching_scalar = ipa_batching_challenge; for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) { batched_commitment = batched_commitment + transcript_commitments[idx] * batching_scalar; batched_transcript_eval += batching_scalar * transcript_evaluations[idx]; - batching_scalar *= batching_challenge; + batching_scalar *= ipa_batching_challenge; } // Construct and verify batched opening claim diff --git a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp index 79b573b02e1..fb6e0751361 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp @@ -107,7 +107,7 @@ template class GoblinUltraRecursive_ { * @param builder * @param native_key Native verification key from which to extract the precomputed commitments */ - VerificationKey(CircuitBuilder* builder, std::shared_ptr native_key) + VerificationKey(CircuitBuilder* builder, const 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 5f068a5c2d4..df0ac96c120 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp @@ -251,7 +251,7 @@ template class UltraRecursive_ { * @param builder * @param native_key Native verification key from which to extract the precomputed commitments */ - VerificationKey(CircuitBuilder* builder, std::shared_ptr native_key) + VerificationKey(CircuitBuilder* builder, const 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/goblin/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt index 248b05c02e6..a6c3c61383a 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(goblin ultra_honk eccvm translator_vm) \ No newline at end of file +barretenberg_module(goblin ultra_honk eccvm translator_vm transcript) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp index 09563987010..7503307881a 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp @@ -1,4 +1,6 @@ #include "barretenberg/eccvm/eccvm_composer.hpp" +#include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" #include "barretenberg/goblin/translation_evaluations.hpp" #include "barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp" #include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" @@ -8,14 +10,11 @@ #include +using namespace barretenberg; using namespace proof_system::honk; namespace test_full_goblin_composer { -namespace { -auto& engine = numeric::random::get_debug_engine(); -} - class FullGoblinComposerTests : public ::testing::Test { protected: static void SetUpTestSuite() @@ -30,110 +29,11 @@ class FullGoblinComposerTests : public ::testing::Test { using Point = Curve::AffineElement; using CommitmentKey = pcs::CommitmentKey; using OpQueue = proof_system::ECCOpQueue; + using GoblinUltraBuilder = proof_system::GoblinUltraCircuitBuilder; using ECCVMFlavor = flavor::ECCVM; using ECCVMBuilder = proof_system::ECCVMCircuitBuilder; using ECCVMComposer = ECCVMComposer_; - - static constexpr size_t NUM_OP_QUEUE_COLUMNS = flavor::GoblinUltra::NUM_WIRES; - - /** - * @brief Generate a simple test circuit with some ECC op gates and conventional arithmetic gates - * - * @param builder - */ - static void generate_test_circuit(proof_system::GoblinUltraCircuitBuilder& builder) - { - // Add some arbitrary ecc op gates - for (size_t i = 0; i < 3; ++i) { - auto point = Point::random_element(); - auto scalar = FF::random_element(); - builder.queue_ecc_add_accum(point); - builder.queue_ecc_mul_accum(point, scalar); - } - // queues the result of the preceding ECC - builder.queue_ecc_eq(); // should be eq and reset - - // Add some conventional gates that utilize public inputs - for (size_t i = 0; i < 10; ++i) { - FF a = FF::random_element(); - FF b = FF::random_element(); - FF c = FF::random_element(); - FF d = a + b + c; - uint32_t a_idx = builder.add_public_variable(a); - uint32_t b_idx = builder.add_variable(b); - uint32_t c_idx = builder.add_variable(c); - uint32_t d_idx = builder.add_variable(d); - - builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) }); - } - } - - /** - * @brief Mock the interactions of a simple curcuit with the op_queue - * @details The transcript aggregation protocol in the Goblin proof system can not yet support an empty "previous - * transcript" (see issue #723). This function mocks the interactions with the op queue of a fictional "first" - * circuit. This way, when we go to generate a proof over our first "real" circuit, the transcript aggregation - * protocol can proceed nominally. The mock data is valid in the sense that it can be processed by all stages of - * Goblin as if it came from a genuine circuit. - * - * @todo WOKTODO: this is a zero commitments issue - * - * @param op_queue - */ - static void perform_op_queue_interactions_for_mock_first_circuit( - std::shared_ptr& op_queue) - { - proof_system::GoblinUltraCircuitBuilder builder{ op_queue }; - - // Add a mul accum op and an equality op - auto point = Point::one() * FF::random_element(); - auto scalar = FF::random_element(); - builder.queue_ecc_mul_accum(point, scalar); - builder.queue_ecc_eq(); - - op_queue->set_size_data(); - - // Manually compute the op queue transcript commitments (which would normally be done by the prover) - auto crs_factory_ = barretenberg::srs::get_crs_factory(); - auto commitment_key = CommitmentKey(op_queue->get_current_size(), crs_factory_); - std::array op_queue_commitments; - size_t idx = 0; - for (auto& entry : op_queue->get_aggregate_transcript()) { - op_queue_commitments[idx++] = commitment_key.commit(entry); - } - // Store the commitment data for use by the prover of the next circuit - op_queue->set_commitment_data(op_queue_commitments); - } - - /** - * @brief Construct and a verify a Honk proof - * - */ - static bool construct_and_verify_honk_proof(GoblinUltraComposer& composer, - proof_system::GoblinUltraCircuitBuilder& builder) - { - auto instance = composer.create_instance(builder); - auto prover = composer.create_prover(instance); - auto verifier = composer.create_verifier(instance); - auto proof = prover.construct_proof(); - bool verified = verifier.verify_proof(proof); - - return verified; - } - - /** - * @brief Construct and verify a Goblin ECC op queue merge proof - * - */ - static bool construct_and_verify_merge_proof(GoblinUltraComposer& composer, std::shared_ptr& op_queue) - { - auto merge_prover = composer.create_merge_prover(op_queue); - auto merge_verifier = composer.create_merge_verifier(/*srs_size=*/10); - auto merge_proof = merge_prover.construct_proof(); - bool verified = merge_verifier.verify_proof(merge_proof); - - return verified; - } + using KernelInput = Goblin::AccumulationOutput; }; /** @@ -145,52 +45,23 @@ class FullGoblinComposerTests : public ::testing::Test { */ TEST_F(FullGoblinComposerTests, SimpleCircuit) { - auto op_queue = std::make_shared(); - - // Add mock data to op queue to simulate interaction with a "first" circuit - perform_op_queue_interactions_for_mock_first_circuit(op_queue); + barretenberg::Goblin goblin; + GoblinUltraBuilder initial_circuit{ goblin.op_queue }; + GoblinTestingUtils::construct_simple_initial_circuit(initial_circuit); + KernelInput kernel_input = goblin.accumulate(initial_circuit); // Construct a series of simple Goblin circuits; generate and verify their proofs - size_t NUM_CIRCUITS = 3; + size_t NUM_CIRCUITS = 2; for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - proof_system::GoblinUltraCircuitBuilder builder{ op_queue }; - - generate_test_circuit(builder); - - // The same composer is used to manage Honk and Merge prover/verifier - proof_system::honk::GoblinUltraComposer composer; - - // Construct and verify Ultra Goblin Honk proof - bool honk_verified = construct_and_verify_honk_proof(composer, builder); - EXPECT_TRUE(honk_verified); - - // Construct and verify op queue merge proof - bool merge_verified = construct_and_verify_merge_proof(composer, op_queue); - EXPECT_TRUE(merge_verified); + GoblinUltraBuilder circuit_builder{ goblin.op_queue }; + GoblinTestingUtils::construct_arithmetic_circuit(circuit_builder); + kernel_input = goblin.accumulate(circuit_builder); } - // Execute the ECCVM - // TODO(https://github.com/AztecProtocol/barretenberg/issues/785) Properly initialize transcript - auto eccvm_builder = ECCVMBuilder(op_queue); - auto eccvm_composer = ECCVMComposer(); - auto eccvm_prover = eccvm_composer.create_prover(eccvm_builder); - auto eccvm_verifier = eccvm_composer.create_verifier(eccvm_builder); - auto eccvm_proof = eccvm_prover.construct_proof(); - bool eccvm_verified = eccvm_verifier.verify_proof(eccvm_proof); - EXPECT_TRUE(eccvm_verified); - - // Execute the Translator - // TODO(https://github.com/AztecProtocol/barretenberg/issues/786) Properly derive batching_challenge - auto batching_challenge = Fbase::random_element(); - auto evaluation_input = eccvm_prover.evaluation_challenge_x; - proof_system::GoblinTranslatorCircuitBuilder translator_builder{ batching_challenge, evaluation_input, op_queue }; - GoblinTranslatorComposer translator_composer; - GoblinTranslatorProver translator_prover = translator_composer.create_prover(translator_builder); - GoblinTranslatorVerifier translator_verifier = translator_composer.create_verifier(translator_builder); - proof_system::plonk::proof translator_proof = translator_prover.construct_proof(); - bool accumulator_construction_verified = translator_verifier.verify_proof(translator_proof); - bool translation_verified = translator_verifier.verify_translation(eccvm_prover.translation_evaluations); - EXPECT_TRUE(accumulator_construction_verified && translation_verified); + Goblin::Proof proof = goblin.prove(); + bool verified = goblin.verify(proof); + EXPECT_TRUE(verified); } + // TODO(https://github.com/AztecProtocol/barretenberg/issues/787) Expand these tests. } // namespace test_full_goblin_composer diff --git a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp new file mode 100644 index 00000000000..315b66657f3 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include "barretenberg/eccvm/eccvm_composer.hpp" +#include "barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp" +#include "barretenberg/proof_system/circuit_builder/goblin_translator_circuit_builder.hpp" +#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/translator_vm/goblin_translator_composer.hpp" +#include "barretenberg/ultra_honk/ultra_composer.hpp" + +namespace barretenberg { + +class Goblin { + using HonkProof = proof_system::plonk::proof; + + public: + /** + * @brief Output of goblin::accumulate; an Ultra proof and the corresponding verification key + * + */ + struct AccumulationOutput { + using NativeVerificationKey = proof_system::honk::flavor::GoblinUltra::VerificationKey; + HonkProof proof; + std::shared_ptr verification_key; + }; + + struct Proof { + HonkProof eccvm_proof; + HonkProof translator_proof; + TranslationEvaluations translation_evaluations; + }; + + using Fr = barretenberg::fr; + using Fq = barretenberg::fq; + + using Transcript = proof_system::honk::BaseTranscript; + using GoblinUltraComposer = proof_system::honk::GoblinUltraComposer; + using GoblinUltraCircuitBuilder = proof_system::GoblinUltraCircuitBuilder; + using OpQueue = proof_system::ECCOpQueue; + using ECCVMFlavor = proof_system::honk::flavor::ECCVM; + using ECCVMBuilder = proof_system::ECCVMCircuitBuilder; + using ECCVMComposer = proof_system::honk::ECCVMComposer; + using TranslatorBuilder = proof_system::GoblinTranslatorCircuitBuilder; + using TranslatorComposer = proof_system::honk::GoblinTranslatorComposer; + + std::shared_ptr op_queue = std::make_shared(); + + private: + // TODO(https://github.com/AztecProtocol/barretenberg/issues/798) unique_ptr use is a hack + std::unique_ptr eccvm_builder; + std::unique_ptr translator_builder; + std::unique_ptr eccvm_composer; + std::unique_ptr translator_composer; + + public: + /** + * @brief + * + * @param circuit_builder + */ + AccumulationOutput accumulate(GoblinUltraCircuitBuilder& circuit_builder) + { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/797) Complete the "kernel" logic by recursively + // verifying previous merge proof + + GoblinUltraComposer composer; + auto instance = composer.create_instance(circuit_builder); + auto prover = composer.create_prover(instance); + auto ultra_proof = prover.construct_proof(); + + auto merge_prover = composer.create_merge_prover(op_queue); + [[maybe_unused]] auto merge_proof = merge_prover.construct_proof(); + + return { ultra_proof, instance->verification_key }; + }; + + Proof prove() + { + Proof proof; + eccvm_builder = std::make_unique(op_queue); + eccvm_composer = std::make_unique(); + auto eccvm_prover = eccvm_composer->create_prover(*eccvm_builder); + proof.eccvm_proof = eccvm_prover.construct_proof(); + proof.translation_evaluations = eccvm_prover.translation_evaluations; + + translator_builder = std::make_unique( + eccvm_prover.translation_batching_challenge_v, eccvm_prover.evaluation_challenge_x, op_queue); + translator_composer = std::make_unique(); + auto translator_prover = translator_composer->create_prover(*translator_builder, eccvm_prover.transcript); + proof.translator_proof = translator_prover.construct_proof(); + return proof; + }; + + bool verify(const Proof& proof) + { + auto eccvm_verifier = eccvm_composer->create_verifier(*eccvm_builder); + bool eccvm_verified = eccvm_verifier.verify_proof(proof.eccvm_proof); + + auto translator_verifier = translator_composer->create_verifier(*translator_builder, eccvm_verifier.transcript); + bool accumulator_construction_verified = translator_verifier.verify_proof(proof.translator_proof); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/799): + // Ensure translation_evaluations are passed correctly + bool translation_verified = translator_verifier.verify_translation(proof.translation_evaluations); + return eccvm_verified && accumulator_construction_verified && translation_verified; + }; +}; +} // namespace barretenberg \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp new file mode 100644 index 00000000000..cb12bb08c37 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -0,0 +1,101 @@ +#include "barretenberg/commitment_schemes/commitment_key.hpp" +#include "barretenberg/eccvm/eccvm_composer.hpp" +#include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/goblin/translation_evaluations.hpp" +#include "barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp" +#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" +#include "barretenberg/translator_vm/goblin_translator_composer.hpp" +#include "barretenberg/ultra_honk/ultra_composer.hpp" + +namespace barretenberg { +class GoblinTestingUtils { + public: + using Curve = curve::BN254; + using FF = Curve::ScalarField; + using Fbase = Curve::BaseField; + using Point = Curve::AffineElement; + using CommitmentKey = proof_system::honk::pcs::CommitmentKey; + using OpQueue = proof_system::ECCOpQueue; + using GoblinUltraBuilder = proof_system::GoblinUltraCircuitBuilder; + using Flavor = proof_system::honk::flavor::GoblinUltra; + static constexpr size_t NUM_OP_QUEUE_COLUMNS = Flavor::NUM_WIRES; + + static void construct_arithmetic_circuit(GoblinUltraBuilder& builder) + { + // Add some arithmetic gates that utilize public inputs + for (size_t i = 0; i < 10; ++i) { + FF a = FF::random_element(); + FF b = FF::random_element(); + FF c = FF::random_element(); + FF d = a + b + c; + uint32_t a_idx = builder.add_public_variable(a); + uint32_t b_idx = builder.add_variable(b); + uint32_t c_idx = builder.add_variable(c); + uint32_t d_idx = builder.add_variable(d); + + builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) }); + } + } + + /** + * @brief Mock the interactions of a simple curcuit with the op_queue + * @todo The transcript aggregation protocol in the Goblin proof system can not yet support an empty "previous + * transcript" (see issue #723) because the corresponding commitments are zero / the point at infinity. This + * function mocks the interactions with the op queue of a fictional "first" circuit. This way, when we go to + * generate a proof over our first "real" circuit, the transcript aggregation protocol can proceed nominally. The + * mock data is valid in the sense that it can be processed by all stages of Goblin as if it came from a genuine + * circuit. + * + * + * @param op_queue + */ + static void perform_op_queue_interactions_for_mock_first_circuit( + std::shared_ptr& op_queue) + { + proof_system::GoblinUltraCircuitBuilder builder{ op_queue }; + + // Add a mul accum op and an equality op + auto point = Point::one() * FF::random_element(); + auto scalar = FF::random_element(); + builder.queue_ecc_mul_accum(point, scalar); + builder.queue_ecc_eq(); + + op_queue->set_size_data(); + + // Manually compute the op queue transcript commitments (which would normally be done by the merge prover) + auto crs_factory_ = barretenberg::srs::get_crs_factory(); + auto commitment_key = CommitmentKey(op_queue->get_current_size(), crs_factory_); + std::array op_queue_commitments; + size_t idx = 0; + for (auto& entry : op_queue->get_aggregate_transcript()) { + op_queue_commitments[idx++] = commitment_key.commit(entry); + } + // Store the commitment data for use by the prover of the next circuit + op_queue->set_commitment_data(op_queue_commitments); + } + + /** + * @brief Generate a simple test circuit with some ECC op gates and conventional arithmetic gates + * + * @param builder + */ + static void construct_simple_initial_circuit(GoblinUltraBuilder& builder) + { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/800) Testing cleanup + perform_op_queue_interactions_for_mock_first_circuit(builder.op_queue); + + // Add some arbitrary ecc op gates + for (size_t i = 0; i < 3; ++i) { + auto point = Point::random_element(); + auto scalar = FF::random_element(); + builder.queue_ecc_add_accum(point); + builder.queue_ecc_mul_accum(point, scalar); + } + // queues the result of the preceding ECC + builder.queue_ecc_eq(); // should be eq and reset + + construct_arithmetic_circuit(builder); + } +}; +} // namespace barretenberg \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_translator_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_translator_circuit_builder.hpp index 92164873e44..9527e5283d3 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_translator_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_translator_circuit_builder.hpp @@ -367,6 +367,7 @@ class GoblinTranslatorCircuitBuilder : public CircuitBuilderBase class ProtoGalaxyProver_ { num_threads = num_threads > 0 ? num_threads : 1; // ensure num threads is >= 1 size_t iterations_per_thread = common_circuit_size / num_threads; // actual iterations per thread - // Constuct univariate accumulator containers; one per thread + // Construct univariate accumulator containers; one per thread std::vector thread_univariate_accumulators(num_threads); for (auto& accum : thread_univariate_accumulators) { Utils::zero_univariates(accum); } - // Constuct extended univariates containers; one per thread + // Construct extended univariates containers; one per thread std::vector extended_univariates; extended_univariates.resize(num_threads); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt index 7ba574b2604..3b7a634c740 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(stdlib_recursion ecc proof_system stdlib_primitives stdlib_pedersen_commitment stdlib_blake3s ultra_honk) \ No newline at end of file +barretenberg_module(stdlib_recursion ecc proof_system stdlib_primitives stdlib_pedersen_commitment stdlib_blake3s ultra_honk eccvm translator_vm) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/goblin/full_goblin_recursion.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/goblin/full_goblin_recursion.test.cpp new file mode 100644 index 00000000000..51d9f1943cb --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/goblin/full_goblin_recursion.test.cpp @@ -0,0 +1,96 @@ +#include "barretenberg/eccvm/eccvm_composer.hpp" +#include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/goblin/translation_evaluations.hpp" +#include "barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp" +#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" +#include "barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp" +#include "barretenberg/translator_vm/goblin_translator_composer.hpp" +#include "barretenberg/ultra_honk/ultra_composer.hpp" + +#include + +using namespace proof_system::honk; +namespace goblin_recursion_tests { + +class GoblinRecursionTests : public ::testing::Test { + protected: + static void SetUpTestSuite() + { + barretenberg::srs::init_crs_factory("../srs_db/ignition"); + barretenberg::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + } + + using Curve = curve::BN254; + using FF = Curve::ScalarField; + using Fbase = Curve::BaseField; + using Point = Curve::AffineElement; + using CommitmentKey = pcs::CommitmentKey; + using OpQueue = proof_system::ECCOpQueue; + using GoblinUltraBuilder = proof_system::GoblinUltraCircuitBuilder; + using ECCVMFlavor = flavor::ECCVM; + using ECCVMBuilder = proof_system::ECCVMCircuitBuilder; + using ECCVMComposer = ECCVMComposer_; + using TranslatorFlavor = flavor::GoblinTranslator; + using TranslatorBuilder = proof_system::GoblinTranslatorCircuitBuilder; + using TranslatorComposer = GoblinTranslatorComposer; + using TranslatorConsistencyData = barretenberg::TranslationEvaluations; + using Proof = proof_system::plonk::proof; + using NativeVerificationKey = flavor::GoblinUltra::VerificationKey; + using RecursiveFlavor = flavor::GoblinUltraRecursive_; + using RecursiveVerifier = proof_system::plonk::stdlib::recursion::honk::UltraRecursiveVerifier_; + using KernelInput = Goblin::AccumulationOutput; + + /** + * @brief Construct a mock kernel circuit + * @details This circuit contains (1) some basic/arbitrary arithmetic gates, (2) a genuine recursive verification of + * the proof provided as input. It does not contain any other real kernel logic. + * + * @param builder + * @param kernel_input A proof to be recursively verified and the corresponding native verification key + */ + static void construct_mock_kernel_circuit(GoblinUltraBuilder& builder, KernelInput& kernel_input) + { + // Generic operations e.g. state updates (just arith gates for now) + GoblinTestingUtils::construct_arithmetic_circuit(builder); + + // Execute recursive aggregation of previous kernel proof + RecursiveVerifier verifier{ &builder, kernel_input.verification_key }; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/801): Aggregation + auto pairing_points = verifier.verify_proof(kernel_input.proof); // app function proof + pairing_points = verifier.verify_proof(kernel_input.proof); // previous kernel proof + } +}; + +/** + * @brief A full Goblin test that mimicks the basic aztec client architecture + * + */ +TEST_F(GoblinRecursionTests, Pseudo) +{ + barretenberg::Goblin goblin; + + // Construct an initial circuit; its proof will be recursively verified by the first kernel + GoblinUltraBuilder initial_circuit{ goblin.op_queue }; + GoblinTestingUtils::construct_simple_initial_circuit(initial_circuit); + KernelInput kernel_input = goblin.accumulate(initial_circuit); + + // Construct a series of simple Goblin circuits; generate and verify their proofs + size_t NUM_CIRCUITS = 2; + for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { + // Construct a circuit with logic resembling that of the "kernel circuit" + GoblinUltraBuilder circuit_builder{ goblin.op_queue }; + construct_mock_kernel_circuit(circuit_builder, kernel_input); + + // Construct proof of the current kernel circuit to be recursively verified by the next one + kernel_input = goblin.accumulate(circuit_builder); + } + + Goblin::Proof proof = goblin.prove(); + bool verified = goblin.verify(proof); + EXPECT_TRUE(verified); +} + +// TODO(https://github.com/AztecProtocol/barretenberg/issues/787) Expand these tests. +} // namespace goblin_recursion_tests 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 785f2c8331a..6c303d15e25 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 @@ -8,8 +8,8 @@ namespace proof_system::plonk::stdlib::recursion::honk { template -UltraRecursiveVerifier_::UltraRecursiveVerifier_(Builder* builder, - std::shared_ptr native_verifier_key) +UltraRecursiveVerifier_::UltraRecursiveVerifier_( + Builder* builder, const 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 67b5411025b..62803746d5d 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 @@ -19,7 +19,8 @@ template class UltraRecursiveVerifier_ { using Builder = typename Flavor::CircuitBuilder; using PairingPoints = std::array; - explicit UltraRecursiveVerifier_(Builder* builder, std::shared_ptr native_verifier_key); + explicit UltraRecursiveVerifier_(Builder* builder, + const 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/sumcheck/sumcheck_round.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp index b5c366be750..7ec78a73799 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp @@ -123,13 +123,13 @@ template class SumcheckProverRound { barretenberg::thread_utils::calculate_num_threads_pow2(round_size, min_iterations_per_thread); size_t iterations_per_thread = round_size / num_threads; // actual iterations per thread - // Constuct univariate accumulator containers; one per thread + // Construct univariate accumulator containers; one per thread std::vector thread_univariate_accumulators(num_threads); for (auto& accum : thread_univariate_accumulators) { Utils::zero_univariates(accum); } - // Constuct extended edge containers; one per thread + // Construct extended edge containers; one per thread std::vector extended_edges; extended_edges.resize(num_threads); diff --git a/barretenberg/cpp/src/barretenberg/transcript/transcript.hpp b/barretenberg/cpp/src/barretenberg/transcript/transcript.hpp index 018001ec101..e734829460c 100644 --- a/barretenberg/cpp/src/barretenberg/transcript/transcript.hpp +++ b/barretenberg/cpp/src/barretenberg/transcript/transcript.hpp @@ -4,8 +4,16 @@ #include "barretenberg/crypto/blake3s/blake3s.hpp" #include "barretenberg/crypto/pedersen_hash/pedersen.hpp" +// #define LOG_CHALLENGES +// #define LOG_INTERACTIONS + namespace proof_system::honk { +template +concept Loggable = (std::same_as || std::same_as || + std::same_as || + std::same_as || std::same_as); + // class TranscriptManifest; class TranscriptManifest { struct RoundData { @@ -268,6 +276,11 @@ class BaseTranscript { auto element_bytes = to_buffer(element); proof_data.insert(proof_data.end(), element_bytes.begin(), element_bytes.end()); +#ifdef LOG_INTERACTIONS + if constexpr (Loggable) { + info("sent: ", label, ": ", element); + } +#endif BaseTranscript::consume_prover_element_bytes(label, element_bytes); } @@ -289,6 +302,11 @@ class BaseTranscript { T element = from_buffer(element_bytes); +#ifdef LOG_INTERACTIONS + if constexpr (Loggable) { + info("received: ", label, ": ", element); + } +#endif return element; } @@ -320,7 +338,14 @@ class BaseTranscript { return verifier_transcript; }; - uint256_t get_challenge(const std::string& label) { return get_challenges(label)[0]; } + uint256_t get_challenge(const std::string& label) + { + uint256_t result = get_challenges(label)[0]; +#if defined LOG_CHALLENGES || defined LOG_INTERACTIONS + info("challenge: ", label, ": ", result); +#endif + return result; + } [[nodiscard]] TranscriptManifest get_manifest() const { return manifest; }; diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.test.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.test.cpp index ed7c1274d5e..d186ebc6eb7 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.test.cpp @@ -9,6 +9,9 @@ #include using namespace proof_system::honk; +using CircuitBuilder = flavor::GoblinTranslator::CircuitBuilder; +using Transcript = flavor::GoblinTranslator::Transcript; +using OpQueue = proof_system::ECCOpQueue; namespace test_goblin_translator_composer { @@ -25,15 +28,6 @@ std::vector add_variables(auto& circuit_constructor, std::vector(); @@ -68,14 +62,24 @@ TEST_F(GoblinTranslatorComposerTests, Basic) op_queue->add_accumulate(P1); op_queue->mul_accumulate(P2, z); } - Fq batching_challenge = Fq::random_element(); - Fq x = Fq::random_element(); - auto circuit_builder = proof_system::GoblinTranslatorCircuitBuilder(batching_challenge, x); - circuit_builder.feed_ecc_op_queue_into_circuit(op_queue); + + auto prover_transcript = std::make_shared(); + prover_transcript->send_to_verifier("init", Fq::random_element()); + prover_transcript->export_proof(); + Fq translation_batching_challenge = prover_transcript->get_challenge("Translation:batching_challenge"); + Fq translation_evaluation_challenge = Fq::random_element(); + auto circuit_builder = CircuitBuilder(translation_batching_challenge, translation_evaluation_challenge, op_queue); EXPECT_TRUE(circuit_builder.check_circuit()); auto composer = GoblinTranslatorComposer(); - prove_and_verify(circuit_builder, composer, /*expected_result=*/true); + auto prover = composer.create_prover(circuit_builder, prover_transcript); + auto proof = prover.construct_proof(); + + auto verifier_transcript = std::make_shared(prover_transcript->proof_data); + verifier_transcript->template receive_from_prover("init"); + auto verifier = composer.create_verifier(circuit_builder, verifier_transcript); + bool verified = verifier.verify_proof(proof); + EXPECT_TRUE(verified); } } // namespace test_goblin_translator_composer diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.cpp index cd3e010b0d9..aeaa9a2719a 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.cpp @@ -256,13 +256,12 @@ void GoblinTranslatorProver::execute_preamble_round() const auto SHIFT = uint256_t(1) << Flavor::NUM_LIMB_BITS; const auto SHIFTx2 = uint256_t(1) << (Flavor::NUM_LIMB_BITS * 2); const auto SHIFTx3 = uint256_t(1) << (Flavor::NUM_LIMB_BITS * 3); - const auto accumulated_result = typename Flavor::BF(uint256_t(key->accumulators_binary_limbs_0[1]) + - uint256_t(key->accumulators_binary_limbs_1[1]) * SHIFT + - uint256_t(key->accumulators_binary_limbs_2[1]) * SHIFTx2 + - uint256_t(key->accumulators_binary_limbs_3[1]) * SHIFTx3); + const auto accumulated_result = + BF(uint256_t(key->accumulators_binary_limbs_0[1]) + uint256_t(key->accumulators_binary_limbs_1[1]) * SHIFT + + uint256_t(key->accumulators_binary_limbs_2[1]) * SHIFTx2 + + uint256_t(key->accumulators_binary_limbs_3[1]) * SHIFTx3); transcript->send_to_verifier("circuit_size", circuit_size); transcript->send_to_verifier("evaluation_input_x", key->evaluation_input_x); - transcript->send_to_verifier("batching_challenge_v", key->batching_challenge_v); transcript->send_to_verifier("accumulated_result", accumulated_result); } @@ -366,7 +365,7 @@ void GoblinTranslatorProver::execute_zeromorph_rounds() plonk::proof& GoblinTranslatorProver::export_proof() { - proof.proof_data = transcript->proof_data; + proof.proof_data = transcript->export_proof(); return proof; } diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.hpp index 3a2db24407d..99bf48c490d 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.hpp @@ -12,6 +12,7 @@ class GoblinTranslatorProver { using Flavor = honk::flavor::GoblinTranslator; using FF = typename Flavor::FF; + using BF = typename Flavor::BF; using Commitment = typename Flavor::Commitment; using CommitmentKey = typename Flavor::CommitmentKey; using ProvingKey = typename Flavor::ProvingKey; diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.cpp index 089c2c37451..44e736b71d5 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.cpp @@ -68,9 +68,8 @@ void GoblinTranslatorVerifier::put_translation_data_in_relation_parameters(const */ bool GoblinTranslatorVerifier::verify_proof(const plonk::proof& proof) { - transcript = std::make_shared(proof.proof_data); - - transcript = std::make_shared(proof.proof_data); + batching_challenge_v = transcript->get_challenge("Translation:batching_challenge"); + transcript->load_proof(proof.proof_data); Flavor::VerifierCommitments commitments{ key }; Flavor::CommitmentLabels commitment_labels; @@ -78,7 +77,6 @@ bool GoblinTranslatorVerifier::verify_proof(const plonk::proof& proof) // TODO(Adrian): Change the initialization of the transcript to take the VK hash? const auto circuit_size = transcript->template receive_from_prover("circuit_size"); evaluation_input_x = transcript->template receive_from_prover("evaluation_input_x"); - batching_challenge_v = transcript->template receive_from_prover("batching_challenge_v"); const BF accumulated_result = transcript->template receive_from_prover("accumulated_result"); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp index a27b2091f3a..671634a3073 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp @@ -94,7 +94,7 @@ template plonk::proof& MergeProver_::construct_proof() FF alpha = transcript->get_challenge("alpha"); - // Constuct batched polynomial to opened via KZG + // Construct batched polynomial to opened via KZG auto batched_polynomial = Polynomial(N); auto batched_eval = FF(0); auto alpha_pow = FF(1); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp index 1d913b342fb..c93257af7de 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp @@ -62,7 +62,7 @@ template bool MergeVerifier_::verify_proof(const plonk FF alpha = transcript->get_challenge("alpha"); - // Constuct batched commitment and evaluation from constituents + // Construct batched commitment and evaluation from constituents auto batched_commitment = opening_claims[0].commitment; auto batched_eval = opening_claims[0].opening_pair.evaluation; auto alpha_pow = alpha;