diff --git a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp index e1146ea4b701..ab98bb22fdb2 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp @@ -46,7 +46,8 @@ template class GoblinUltraRecursive_ { using FF = typename Curve::ScalarField; using Commitment = typename Curve::Element; using CommitmentHandle = typename Curve::Element; - using NativeVerificationKey = flavor::GoblinUltra::VerificationKey; + using NativeFlavor = flavor::GoblinUltra; + using NativeVerificationKey = NativeFlavor::VerificationKey; // Note(luke): Eventually this may not be needed at all using VerifierCommitmentKey = pcs::VerifierCommitmentKey; diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp index 94955d72f759..f6e58733ad2c 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp @@ -54,7 +54,8 @@ template class UltraRecursive_ { using Commitment = typename Curve::Element; using CommitmentHandle = typename Curve::Element; using FF = typename Curve::ScalarField; - using NativeVerificationKey = flavor::Ultra::VerificationKey; + using NativeFlavor = flavor::Ultra; + using NativeVerificationKey = NativeFlavor::VerificationKey; // Note(luke): Eventually this may not be needed at all using VerifierCommitmentKey = pcs::VerifierCommitmentKey; @@ -416,6 +417,44 @@ template class UltraRecursive_ { this->z_lookup = commitments.z_lookup; } } + + VerifierCommitments(const std::shared_ptr& verification_key, + const WitnessCommitments& witness_commitments) + { + this->q_m = verification_key->q_m; + this->q_l = verification_key->q_l; + this->q_r = verification_key->q_r; + this->q_o = verification_key->q_o; + this->q_4 = verification_key->q_4; + this->q_c = verification_key->q_c; + this->q_arith = verification_key->q_arith; + this->q_sort = verification_key->q_sort; + this->q_elliptic = verification_key->q_elliptic; + this->q_aux = verification_key->q_aux; + this->q_lookup = verification_key->q_lookup; + this->sigma_1 = verification_key->sigma_1; + this->sigma_2 = verification_key->sigma_2; + this->sigma_3 = verification_key->sigma_3; + this->sigma_4 = verification_key->sigma_4; + this->id_1 = verification_key->id_1; + this->id_2 = verification_key->id_2; + this->id_3 = verification_key->id_3; + this->id_4 = verification_key->id_4; + this->table_1 = verification_key->table_1; + this->table_2 = verification_key->table_2; + this->table_3 = verification_key->table_3; + this->table_4 = verification_key->table_4; + this->lagrange_first = verification_key->lagrange_first; + this->lagrange_last = verification_key->lagrange_last; + + this->w_l = witness_commitments.w_l; + this->w_r = witness_commitments.w_r; + this->w_o = witness_commitments.w_o; + this->sorted_accum = witness_commitments.sorted_accum; + this->w_4 = witness_commitments.w_4; + this->z_perm = witness_commitments.z_perm; + this->z_lookup = witness_commitments.z_lookup; + } }; using Transcript = bb::stdlib::recursion::honk::Transcript; diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.cpp index ef86acfcb2e9..d8f29d1f9830 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.cpp @@ -103,7 +103,6 @@ template bool DeciderVerifier_::verify_proof(const hon transcript); auto verified = pcs_verification_key->pairing_check(pairing_points[0], pairing_points[1]); - return sumcheck_verified.value() && verified; } diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp index 6a138c51a516..132c37682087 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp @@ -34,6 +34,27 @@ void ProtoGalaxyProver_::finalise_and_send_instance(std::shared transcript->send_to_verifier(domain_separator + "_" + wire_labels[idx], wire_comms[idx]); } + if constexpr (IsGoblinFlavor) { + // Commit to Goblin ECC op wires + witness_commitments.ecc_op_wire_1 = commitment_key->commit(instance->proving_key->ecc_op_wire_1); + witness_commitments.ecc_op_wire_2 = commitment_key->commit(instance->proving_key->ecc_op_wire_2); + witness_commitments.ecc_op_wire_3 = commitment_key->commit(instance->proving_key->ecc_op_wire_3); + witness_commitments.ecc_op_wire_4 = commitment_key->commit(instance->proving_key->ecc_op_wire_4); + + auto op_wire_comms = instance->witness_commitments.get_ecc_op_wires(); + auto labels = commitment_labels.get_ecc_op_wires(); + for (size_t idx = 0; idx < Flavor::NUM_WIRES; ++idx) { + transcript->send_to_verifier(domain_separator + "_" + labels[idx], op_wire_comms[idx]); + } + // Commit to DataBus columns + witness_commitments.calldata = commitment_key->commit(instance->proving_key->calldata); + witness_commitments.calldata_read_counts = commitment_key->commit(instance->proving_key->calldata_read_counts); + transcript->send_to_verifier(domain_separator + "_" + commitment_labels.calldata, + instance->witness_commitments.calldata); + transcript->send_to_verifier(domain_separator + "_" + commitment_labels.calldata_read_counts, + instance->witness_commitments.calldata_read_counts); + } + auto eta = transcript->get_challenge(domain_separator + "_eta"); instance->compute_sorted_accumulator_polynomials(eta); @@ -47,6 +68,16 @@ void ProtoGalaxyProver_::finalise_and_send_instance(std::shared transcript->send_to_verifier(domain_separator + "_" + commitment_labels.w_4, witness_commitments.w_4); auto [beta, gamma] = transcript->get_challenges(domain_separator + "_beta", domain_separator + "_gamma"); + + if constexpr (IsGoblinFlavor) { + // Compute and commit to the logderivative inverse used in DataBus + instance->compute_logderivative_inverse(beta, gamma); + instance->witness_commitments.lookup_inverses = + commitment_key->commit(instance->prover_polynomials.lookup_inverses); + transcript->send_to_verifier(domain_separator + "_" + commitment_labels.lookup_inverses, + instance->witness_commitments.lookup_inverses); + } + instance->compute_grand_product_polynomials(beta, gamma); witness_commitments.z_perm = commitment_key->commit(instance->prover_polynomials.z_perm); @@ -150,6 +181,8 @@ std::shared_ptr ProtoGalaxyProver_(challenge); + auto combiner_quotient_at_challenge = combiner_quotient.evaluate(challenge); // Given the challenge \gamma, compute Z(\gamma) and {L_0(\gamma),L_1(\gamma)} @@ -274,14 +307,13 @@ FoldingResult ProtoGalaxyProver_get_challenge("delta"); - auto accumulator = get_accumulator(); auto deltas = compute_round_challenge_pows(accumulator->log_instance_size, delta); - auto perturbator = compute_perturbator(accumulator, deltas); for (size_t idx = 0; idx <= accumulator->log_instance_size; idx++) { transcript->send_to_verifier("perturbator_" + std::to_string(idx), perturbator[idx]); } + auto perturbator_challenge = transcript->get_challenge("perturbator_challenge"); instances.next_gate_challenges = update_gate_challenges(perturbator_challenge, accumulator->gate_challenges, deltas); @@ -289,7 +321,6 @@ FoldingResult ProtoGalaxyProver_(instances.next_gate_challenges); auto combiner = compute_combiner(instances, pow_polynomial); - auto compressed_perturbator = perturbator.evaluate(perturbator_challenge); auto combiner_quotient = compute_combiner_quotient(compressed_perturbator, combiner); @@ -303,7 +334,6 @@ FoldingResult ProtoGalaxyProver_proof_data; res.accumulator = next_accumulator; - return res; } diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp index 5a0794c3dd78..83f5ecab49d3 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp @@ -127,16 +127,23 @@ template class ProtoGalaxyProver_ { std::shared_ptr get_accumulator() { return instances[0]; } /** - * @brief Compute the values of the full Honk relation at each row in the execution trace, f_i(ω) in the - * ProtoGalaxy paper, given the evaluations of all the prover polynomials and α (the parameter that helps establish - * each subrelation is independently valid in Honk - from the Plonk paper, DO NOT confuse with α in ProtoGalaxy), + * @brief Compute the values of the full Honk relation at each row in the execution trace, representing f_i(ω) in + * the ProtoGalaxy paper, given the evaluations of all the prover polynomials and \vec{α} (the batching challenges + * that help establishing each subrelation is independently valid in Honk - from the Plonk paper, DO NOT confuse + * with α in ProtoGalaxy). + * + * @details When folding GoblinUltra instances, one of the relations is linearly dependent. We define such relations + * as acting on the entire execution trace and hence requiring to be accumulated separately as we iterate over each + * row. At the end of the function, the linearly dependent contribution is accumulated at index 0 representing the + * sum f_0(ω) + α_j*g(ω) where f_0 represents the full honk evaluation at row 0, g(ω) is the linearly dependent + * subrelation and α_j is its corresponding batching challenge. */ static std::vector compute_full_honk_evaluations(const ProverPolynomials& instance_polynomials, const RelationSeparator& alpha, const RelationParameters& relation_parameters) { auto instance_size = instance_polynomials.get_polynomial_size(); - + FF linearly_dependent_contribution = FF(0); std::vector full_honk_evaluations(instance_size); for (size_t row = 0; row < instance_size; row++) { auto row_evaluations = instance_polynomials.get_row(row); @@ -150,16 +157,21 @@ template class ProtoGalaxyProver_ { auto output = FF(0); auto running_challenge = FF(1); - Utils::scale_and_batch_elements(relation_evaluations, alpha, running_challenge, output); + + // Sum relation evaluations, batched by their corresponding relation separator challenge, to get the value + // of the full honk relation at a specific row + Utils::scale_and_batch_elements_without_linear_contributions( + relation_evaluations, alpha, running_challenge, output, linearly_dependent_contribution); full_honk_evaluations[row] = output; } + full_honk_evaluations[0] += linearly_dependent_contribution; return full_honk_evaluations; } /** * @brief Recursively compute the parent nodes of each level in there, starting from the leaves. Note that at each - * level, the resulting parent nodes will be polynomials of degree (level + 1) because we multiply by an additional + * level, the resulting parent nodes will be polynomials of degree (level+1) because we multiply by an additional. * factor of X. */ static std::vector construct_coefficients_tree(const std::vector& betas, @@ -308,6 +320,7 @@ template class ProtoGalaxyProver_ { // Accumulate the i-th row's univariate contribution. Note that the relation parameters passed to this // function have already been folded + // This will not add pow stuff to relation dependent stuff accumulate_relation_univariates( thread_univariate_accumulators[thread_idx], extended_univariates[thread_idx], @@ -323,6 +336,7 @@ template class ProtoGalaxyProver_ { // Batch the univariate contributions from each sub-relation to obtain the round univariate return batch_over_relations(univariate_accumulators, instances.alphas); } + static ExtendedUnivariateWithRandomization batch_over_relations(TupleOfTuplesOfUnivariates& univariate_accumulators, const CombinedRelationSeparator& alpha) { @@ -331,7 +345,7 @@ template class ProtoGalaxyProver_ { auto result = std::get<0>(std::get<0>(univariate_accumulators)) .template extend_to(); size_t idx = 0; - auto scale_and_sum = [&](auto& element) { + auto scale_and_sum = [&](auto& element) { auto extended = element.template extend_to(); extended *= alpha[idx]; result += extended; diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp index 5c8331e0aaf7..09872f29b59f 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp @@ -87,6 +87,22 @@ void ProtoGalaxyVerifier_::receive_and_finalise_instance(cons witness_commitments.w_r = transcript->template receive_from_prover(domain_separator + "_" + labels.w_r); witness_commitments.w_o = transcript->template receive_from_prover(domain_separator + "_" + labels.w_o); + if constexpr (IsGoblinFlavor) { + // Get commitments to the ECC wire polynomials + witness_commitments.ecc_op_wire_1 = + transcript->template receive_from_prover(domain_separator + "_" + labels.ecc_op_wire_1); + witness_commitments.ecc_op_wire_2 = + transcript->template receive_from_prover(domain_separator + "_" + labels.ecc_op_wire_2); + witness_commitments.ecc_op_wire_3 = + transcript->template receive_from_prover(domain_separator + "_" + labels.ecc_op_wire_3); + witness_commitments.ecc_op_wire_4 = + transcript->template receive_from_prover(domain_separator + "_" + labels.ecc_op_wire_4); + witness_commitments.calldata = + transcript->template receive_from_prover(domain_separator + "_" + labels.calldata); + witness_commitments.calldata_read_counts = + transcript->template receive_from_prover(domain_separator + "_" + labels.calldata_read_counts); + } + // Get challenge for sorted list batching and wire four memory records commitment auto eta = transcript->get_challenge(domain_separator + "_eta"); witness_commitments.sorted_accum = @@ -95,6 +111,13 @@ void ProtoGalaxyVerifier_::receive_and_finalise_instance(cons // Get permutation challenges and commitment to permutation and lookup grand products auto [beta, gamma] = transcript->get_challenges(domain_separator + "_beta", domain_separator + "_gamma"); + + // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial + if constexpr (IsGoblinFlavor) { + witness_commitments.lookup_inverses = transcript->template receive_from_prover( + domain_separator + "_" + commitment_labels.lookup_inverses); + } + witness_commitments.z_perm = transcript->template receive_from_prover(domain_separator + "_" + labels.z_perm); witness_commitments.z_lookup = diff --git a/barretenberg/cpp/src/barretenberg/relations/utils.hpp b/barretenberg/cpp/src/barretenberg/relations/utils.hpp index 331967359f3e..e198f380be22 100644 --- a/barretenberg/cpp/src/barretenberg/relations/utils.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/utils.hpp @@ -113,8 +113,7 @@ template class RelationUtils { template static constexpr void add_tuples(std::tuple& tuple_1, const std::tuple& tuple_2) { - auto add_tuples_helper = [&](std::index_sequence) - { + auto add_tuples_helper = [&](std::index_sequence) { ((std::get(tuple_1) += std::get(tuple_2)), ...); }; @@ -184,7 +183,7 @@ template class RelationUtils { }; /** - * @brief Scale elements, which in sumcheck represent evaluations of subrelations, by different challenges then sum + * @brief Scale elements, representing evaluations of subrelations, by separate challenges and afterwards sum them * @param challenges Array of NUM_SUBRELATIONS - 1 challenges (because the first subrelation does not need to be * scaled) * @param result Batched result @@ -197,7 +196,9 @@ template class RelationUtils { { size_t idx = 0; std::array tmp{ current_scalar }; + std::copy(challenges.begin(), challenges.end(), tmp.begin() + 1); + auto scale_by_challenges_and_accumulate = [&](auto& element) { for (auto& entry : element) { result += entry * tmp[idx]; @@ -208,7 +209,49 @@ template class RelationUtils { } /** - * @brief Scale elements by consecutive powers of the challenge then sum + * @brief Scales elements, representing evaluations of polynomials in subrelations, by separate challenges and then + * sum them together. This function has identical functionality with the one above with the caveat that one such + * evaluation is part of a linearly dependent subrelation and hence needs to be accumulated separately. + * + * @details Such functionality is needed when computing the evaluation of the full relation at a specific row in + * the execution trace because a linearly dependent subrelation does not act on a specific row but rather on the + * entire execution trace. + * + * @param tuple + * @param challenges + * @param current_scalar + * @param result + * @param linearly_dependent_contribution + */ + static void scale_and_batch_elements_without_linear_contributions(auto& tuple, + const RelationSeparator& challenges, + FF current_scalar, + FF& result, + FF& linearly_dependent_contribution) + requires bb::IsFoldingFlavor + { + size_t idx = 0; + std::array tmp{ current_scalar }; + + std::copy(challenges.begin(), challenges.end(), tmp.begin() + 1); + + auto scale_by_challenge_and_accumulate = + [&](Element& element) { + using Relation = typename std::tuple_element_t; + const bool is_subrelation_linearly_independent = + bb::subrelation_is_linearly_independent(); + if (is_subrelation_linearly_independent) { + result += element * tmp[idx]; + } else { + linearly_dependent_contribution += element * tmp[idx]; + } + idx++; + }; + apply_to_tuple_of_arrays_elements(scale_by_challenge_and_accumulate, tuple); + } + + /** + * @brief Scale elements by consecutive powers of a given challenge then sum the result * @param result Batched result */ static void scale_and_batch_elements(auto& tuple, const RelationSeparator& challenge, FF current_scalar, FF& result) @@ -240,5 +283,26 @@ template class RelationUtils { apply_to_tuple_of_arrays(operation, tuple); } } + + // Recursive template function to apply a specific operation on each element of several arrays in a tuple + template + static void apply_to_tuple_of_arrays_elements(Operation&& operation, std::tuple& tuple) + { + using Relation = typename std::tuple_element_t; + const auto subrel = Relation::SUBRELATION_PARTIAL_LENGTHS.size(); + auto& element = std::get(tuple); + + // Invoke the operation with outer_idx (array index) and inner_idx (element index) as template arguments + operation.template operator()(element[inner_idx]); + + if constexpr (inner_idx + 1 < subrel) { + // Recursively call for the next element within the same array + apply_to_tuple_of_arrays_elements(std::forward(operation), + tuple); + } else if constexpr (outer_idx + 1 < sizeof...(Ts)) { + // Move to the next array in the tuple + apply_to_tuple_of_arrays_elements(std::forward(operation), tuple); + } + } }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp index d1fdaeee01ba..2fb12a704840 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp @@ -94,6 +94,21 @@ void ProtoGalaxyRecursiveVerifier_::receive_and_finalise_inst witness_commitments.w_r = transcript->template receive_from_prover(domain_separator + "_" + labels.w_r); witness_commitments.w_o = transcript->template receive_from_prover(domain_separator + "_" + labels.w_o); + if constexpr (IsGoblinFlavor) { + witness_commitments.ecc_op_wire_1 = + transcript->template receive_from_prover(domain_separator + "_" + labels.ecc_op_wire_1); + witness_commitments.ecc_op_wire_2 = + transcript->template receive_from_prover(domain_separator + "_" + labels.ecc_op_wire_2); + witness_commitments.ecc_op_wire_3 = + transcript->template receive_from_prover(domain_separator + "_" + labels.ecc_op_wire_3); + witness_commitments.ecc_op_wire_4 = + transcript->template receive_from_prover(domain_separator + "_" + labels.ecc_op_wire_4); + witness_commitments.calldata = + transcript->template receive_from_prover(domain_separator + "_" + labels.calldata); + witness_commitments.calldata_read_counts = + transcript->template receive_from_prover(domain_separator + "_" + labels.calldata_read_counts); + } + // Get challenge for sorted list batching and wire four memory records commitment auto eta = transcript->get_challenge(domain_separator + "_eta"); witness_commitments.sorted_accum = @@ -102,6 +117,13 @@ void ProtoGalaxyRecursiveVerifier_::receive_and_finalise_inst // Get permutation challenges and commitment to permutation and lookup grand products auto [beta, gamma] = transcript->get_challenges(domain_separator + "_beta", domain_separator + "_gamma"); + + // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial + if constexpr (IsGoblinFlavor) { + witness_commitments.lookup_inverses = transcript->template receive_from_prover( + domain_separator + "_" + commitment_labels.lookup_inverses); + } + witness_commitments.z_perm = transcript->template receive_from_prover(domain_separator + "_" + labels.z_perm); witness_commitments.z_lookup = diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp index 61dee74084fa..e94e3a0b8523 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp @@ -8,7 +8,7 @@ #include "barretenberg/ultra_honk/ultra_composer.hpp" namespace bb::stdlib::recursion::honk { -class ProtogalaxyRecursiveTest : public testing::Test { +template class ProtoGalaxyRecursiveTests : public testing::Test { public: // Define types relevant for testing using UltraFlavor = ::bb::honk::flavor::Ultra; @@ -16,18 +16,18 @@ class ProtogalaxyRecursiveTest : public testing::Test { using UltraComposer = ::bb::honk::UltraComposer_; using GoblinUltraComposer = ::bb::honk::UltraComposer_; - using InnerFlavor = UltraFlavor; - using InnerComposer = UltraComposer; + using InnerFlavor = RecursiveFlavor::NativeFlavor; + using InnerComposer = ::bb::honk::UltraComposer_; using Instance = ::bb::honk::ProverInstance_; using InnerBuilder = typename InnerComposer::CircuitBuilder; using InnerCurve = bn254; using Commitment = InnerFlavor::Commitment; using FF = InnerFlavor::FF; - // Types for recursive verifier circuit - // cannot do on Goblin + // Types for veryfing a recursive verifier circuit using OuterBuilder = GoblinUltraCircuitBuilder; - using RecursiveFlavor = ::bb::honk::flavor::UltraRecursive_; + using OuterComposer = GoblinUltraComposer; + using RecursiveVerifierInstances = ::bb::honk::VerifierInstances_; using FoldingRecursiveVerifier = ProtoGalaxyRecursiveVerifier_; using DeciderRecursiveVerifier = DeciderRecursiveVerifier_; @@ -35,6 +35,8 @@ class ProtogalaxyRecursiveTest : public testing::Test { using NativeVerifierInstances = ::bb::honk::VerifierInstances_; using NativeFoldingVerifier = bb::honk::ProtoGalaxyVerifier_; + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } + // Helper for getting composer for prover/verifier of recursive (outer) circuit template static auto get_outer_composer() { @@ -63,6 +65,7 @@ class ProtogalaxyRecursiveTest : public testing::Test { using witness_ct = InnerCurve::witness_ct; using byte_array_ct = InnerCurve::byte_array_ct; using fr = typename InnerCurve::ScalarFieldNative; + using point = typename InnerCurve::AffineElementNative; // Create 2^log_n many add gates based on input log num gates const size_t num_gates = 1 << log_num_gates; @@ -101,10 +104,14 @@ class ProtogalaxyRecursiveTest : public testing::Test { fq_ct big_b(fr_ct(witness_ct(&builder, bigfield_data_b.to_montgomery_form())), fr_ct(witness_ct(&builder, 0))); big_a* big_b; - }; - public: - static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } + if constexpr (IsGoblinBuilder) { + auto p = point::one() * fr::random_element(); + auto scalar = fr::random_element(); + builder.queue_ecc_mul_accum(p, scalar); + builder.queue_ecc_eq(); + } + }; static std::shared_ptr fold_and_verify(const std::vector>& instances, InnerComposer& inner_composer) @@ -117,7 +124,7 @@ class ProtogalaxyRecursiveTest : public testing::Test { OuterBuilder outer_folding_circuit; FoldingRecursiveVerifier verifier{ &outer_folding_circuit }; verifier.verify_folding_proof(inner_folding_proof.folding_data); - info("Recursive Verifier with Ultra instances: num gates = ", outer_folding_circuit.num_gates); + info("Folding Recursive Verifier: num gates = ", outer_folding_circuit.num_gates); // Perform native folding verification and ensure it returns the same result (either true or false) as calling // check_circuit on the recursive folding verifier @@ -139,19 +146,203 @@ class ProtogalaxyRecursiveTest : public testing::Test { return inner_folding_proof.accumulator; } + + static void test_inner_circuit() + { + InnerBuilder builder; + + create_inner_circuit(builder); + + bool result = builder.check_circuit(); + EXPECT_EQ(result, true); + }; + + // static void test_new_evaluate() + // { + // OuterBuilder builder; + // using fr_ct = bn254::ScalarField; + // using fr = bn254::ScalarFieldNative; + + // std::vector coeffs; + // std::vector coeffs_ct; + // for (size_t idx = 0; idx < 8; idx++) { + // auto el = fr::random_element(); + // coeffs.emplace_back(el); + // coeffs_ct.emplace_back(fr_ct(&builder, el)); + // } + // Polynomial poly(coeffs); + // fr point = fr::random_element(); + // fr_ct point_ct(fr_ct(&builder, point)); + // auto res1 = poly.evaluate(point); + + // auto res2 = FoldingRecursiveVerifier::evaluate_perturbator(coeffs_ct, point_ct); + // EXPECT_EQ(res1, res2.get_value()); + // }; + + static void test_recursive_folding() + { + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + fold_and_verify(instances, inner_composer); + }; + + static void test_full_protogalaxy_recursive() + { + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + auto accumulator = fold_and_verify(instances, inner_composer); + + // Create another circuit to do a second round of folding + InnerBuilder builder3; + create_inner_circuit(builder3); + auto instance3 = inner_composer.create_instance(builder3); + instances = std::vector>{ accumulator, instance3 }; + + accumulator = fold_and_verify(instances, inner_composer); + + // Create a decider proof for the relaxed instance obtained through folding + auto inner_decider_prover = inner_composer.create_decider_prover(accumulator); + auto inner_decider_proof = inner_decider_prover.construct_proof(); + + // Create a decider verifier circuit for recursively verifying the decider proof + OuterBuilder outer_decider_circuit; + DeciderRecursiveVerifier decider_verifier{ &outer_decider_circuit }; + auto pairing_points = decider_verifier.verify_proof(inner_decider_proof); + info("Decider Recursive Verifier: num gates = ", outer_decider_circuit.num_gates); + // Check for a failure flag in the recursive verifier circuit + EXPECT_EQ(outer_decider_circuit.failed(), false) << outer_decider_circuit.err(); + + // Perform native verification then perform the pairing on the outputs of the recursive + // decider verifier and check that the result agrees. + DeciderVerifier native_decider_verifier = inner_composer.create_decider_verifier(accumulator); + auto native_result = native_decider_verifier.verify_proof(inner_decider_proof); + auto recursive_result = native_decider_verifier.pcs_verification_key->pairing_check( + pairing_points[0].get_value(), pairing_points[1].get_value()); + EXPECT_EQ(native_result, recursive_result); + + // Ensure that the underlying native and recursive decider verification algorithms agree by ensuring + // the manifests produced are the same. + auto recursive_decider_manifest = decider_verifier.transcript->get_manifest(); + auto native_decider_manifest = native_decider_verifier.transcript->get_manifest(); + for (size_t i = 0; i < recursive_decider_manifest.size(); ++i) { + EXPECT_EQ(recursive_decider_manifest[i], native_decider_manifest[i]); + } + + // Construct and verify a proof of the recursive decider verifier circuit + { + auto composer = OuterComposer(); + auto instance = composer.create_instance(outer_decider_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); + } + }; + + static void test_tampered_decider_proof() + { + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + auto accumulator = fold_and_verify(instances, inner_composer); + + // Tamper with the accumulator by changing the target sum + accumulator->target_sum = FF::random_element(); + + // Create a decider proof for the relaxed instance obtained through folding + auto inner_decider_prover = inner_composer.create_decider_prover(accumulator); + auto inner_decider_proof = inner_decider_prover.construct_proof(); + + // Create a decider verifier circuit for recursively verifying the decider proof + OuterBuilder outer_decider_circuit; + DeciderRecursiveVerifier decider_verifier{ &outer_decider_circuit }; + decider_verifier.verify_proof(inner_decider_proof); + info("Decider Recursive Verifier: num gates = ", outer_decider_circuit.num_gates); + + // We expect the decider circuit check to fail due to the bad proof + EXPECT_FALSE(outer_decider_circuit.check_circuit()); + }; + + static void test_tampered_accumulator() + { + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + auto accumulator = fold_and_verify(instances, inner_composer); + + // Create another circuit to do a second round of folding + InnerBuilder builder3; + create_inner_circuit(builder3); + auto instance3 = inner_composer.create_instance(builder3); + + // Tamper with the accumulator + instances = std::vector>{ accumulator, instance3 }; + accumulator->prover_polynomials.w_l[1] = FF::random_element(); + + // Generate a folding proof + auto inner_folding_prover = inner_composer.create_folding_prover(instances); + auto inner_folding_proof = inner_folding_prover.fold_instances(); + + // Create a recursive folding verifier circuit for the folding proof of the two instances + OuterBuilder outer_folding_circuit; + FoldingRecursiveVerifier verifier{ &outer_folding_circuit }; + verifier.verify_folding_proof(inner_folding_proof.folding_data); + EXPECT_EQ(outer_folding_circuit.check_circuit(), false); + }; }; -/** - * @brief Create inner circuit and call check_circuit on it - * - */ -TEST_F(ProtogalaxyRecursiveTest, InnerCircuit) -{ - InnerBuilder builder; - create_inner_circuit(builder); +using FlavorTypes = testing::Types<::bb::honk::flavor::GoblinUltraRecursive_, + ::bb::honk::flavor::UltraRecursive_>; +TYPED_TEST_SUITE(ProtoGalaxyRecursiveTests, FlavorTypes); +/**@brief Create inner circuit and call check_circuit on it*/ - bool result = builder.check_circuit(); - EXPECT_EQ(result, true); +TYPED_TEST(ProtoGalaxyRecursiveTests, InnerCircuit) +{ + TestFixture::test_inner_circuit(); } /** @@ -159,48 +350,18 @@ TEST_F(ProtogalaxyRecursiveTest, InnerCircuit) * evaluating in Polynomial class. * */ -TEST_F(ProtogalaxyRecursiveTest, NewEvaluate) -{ - OuterBuilder builder; - using fr_ct = bn254::ScalarField; - using fr = bn254::ScalarFieldNative; - - std::vector coeffs; - std::vector coeffs_ct; - for (size_t idx = 0; idx < 8; idx++) { - auto el = fr::random_element(); - coeffs.emplace_back(el); - coeffs_ct.emplace_back(fr_ct(&builder, el)); - } - Polynomial poly(coeffs); - fr point = fr::random_element(); - fr_ct point_ct(fr_ct(&builder, point)); - auto res1 = poly.evaluate(point); - - auto res2 = FoldingRecursiveVerifier::evaluate_perturbator(coeffs_ct, point_ct); - EXPECT_EQ(res1, res2.get_value()); -} +// TYPED_TEST(ProtoGalaxyRecursiveTests, NewEvaluate) +// { +// TestFixture::test_new_evaluate(); +// } /** * @brief Tests a simple recursive fold that is valid works as expected. * */ -TEST_F(ProtogalaxyRecursiveTest, RecursiveFoldingTest) +TYPED_TEST(ProtoGalaxyRecursiveTests, RecursiveFoldingTest) { - // Create two arbitrary circuits for the first round of folding - InnerBuilder builder1; - - create_inner_circuit(builder1); - InnerBuilder builder2; - builder2.add_public_variable(FF(1)); - create_inner_circuit(builder2); - - InnerComposer inner_composer = InnerComposer(); - auto instance1 = inner_composer.create_instance(builder1); - auto instance2 = inner_composer.create_instance(builder2); - auto instances = std::vector>{ instance1, instance2 }; - - fold_and_verify(instances, inner_composer); + TestFixture::test_recursive_folding(); } /** @@ -209,142 +370,20 @@ TEST_F(ProtogalaxyRecursiveTest, RecursiveFoldingTest) * are identical by checking the manifests */ -TEST_F(ProtogalaxyRecursiveTest, FullProtogalaxyRecursiveTest) +TYPED_TEST(ProtoGalaxyRecursiveTests, FullProtogalaxyRecursiveTest) { - // Create two arbitrary circuits for the first round of folding - InnerBuilder builder1; - - create_inner_circuit(builder1); - InnerBuilder builder2; - builder2.add_public_variable(FF(1)); - create_inner_circuit(builder2); - - InnerComposer inner_composer = InnerComposer(); - auto instance1 = inner_composer.create_instance(builder1); - auto instance2 = inner_composer.create_instance(builder2); - auto instances = std::vector>{ instance1, instance2 }; - - auto accumulator = fold_and_verify(instances, inner_composer); - - // Create another circuit to do a second round of folding - InnerBuilder builder3; - create_inner_circuit(builder3); - auto instance3 = inner_composer.create_instance(builder3); - instances = std::vector>{ accumulator, instance3 }; - - accumulator = fold_and_verify(instances, inner_composer); - - // Create a decider proof for the relaxed instance obtained through folding - auto inner_decider_prover = inner_composer.create_decider_prover(accumulator); - auto inner_decider_proof = inner_decider_prover.construct_proof(); - - // Create a decider verifier circuit for recursively verifying the decider proof - OuterBuilder outer_decider_circuit; - DeciderRecursiveVerifier decider_verifier{ &outer_decider_circuit }; - auto pairing_points = decider_verifier.verify_proof(inner_decider_proof); - info("Decider Recursive Verifier: num gates = ", outer_decider_circuit.num_gates); - // Check for a failure flag in the recursive verifier circuit - EXPECT_EQ(outer_decider_circuit.failed(), false) << outer_decider_circuit.err(); - - // Perform native verification then perform the pairing on the outputs of the recursive - // decider verifier and check that the result agrees. - DeciderVerifier native_decider_verifier = inner_composer.create_decider_verifier(accumulator); - auto native_result = native_decider_verifier.verify_proof(inner_decider_proof); - auto recursive_result = native_decider_verifier.pcs_verification_key->pairing_check(pairing_points[0].get_value(), - pairing_points[1].get_value()); - EXPECT_EQ(native_result, recursive_result); - - // Ensure that the underlying native and recursive decider verification algorithms agree by ensuring - // the manifests produced are the same. - auto recursive_decider_manifest = decider_verifier.transcript->get_manifest(); - auto native_decider_manifest = native_decider_verifier.transcript->get_manifest(); - for (size_t i = 0; i < recursive_decider_manifest.size(); ++i) { - EXPECT_EQ(recursive_decider_manifest[i], native_decider_manifest[i]); - } - - // Construct and verify a proof of the recursive decider verifier circuit - { - auto composer = get_outer_composer(); - auto instance = composer.create_instance(outer_decider_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); - } + TestFixture::test_full_protogalaxy_recursive(); } -TEST_F(ProtogalaxyRecursiveTest, TamperedDeciderProof) +TYPED_TEST(ProtoGalaxyRecursiveTests, TamperedDeciderProof) { - // Create two arbitrary circuits for the first round of folding - InnerBuilder builder1; - - create_inner_circuit(builder1); - InnerBuilder builder2; - builder2.add_public_variable(FF(1)); - create_inner_circuit(builder2); - - InnerComposer inner_composer = InnerComposer(); - auto instance1 = inner_composer.create_instance(builder1); - auto instance2 = inner_composer.create_instance(builder2); - auto instances = std::vector>{ instance1, instance2 }; - - auto accumulator = fold_and_verify(instances, inner_composer); - - // Tamper with the accumulator by changing the target sum - accumulator->target_sum = FF::random_element(); - - // Create a decider proof for the relaxed instance obtained through folding - auto inner_decider_prover = inner_composer.create_decider_prover(accumulator); - auto inner_decider_proof = inner_decider_prover.construct_proof(); - - // Create a decider verifier circuit for recursively verifying the decider proof - OuterBuilder outer_decider_circuit; - DeciderRecursiveVerifier decider_verifier{ &outer_decider_circuit }; - decider_verifier.verify_proof(inner_decider_proof); - info("Decider Recursive Verifier: num gates = ", outer_decider_circuit.num_gates); - - // We expect the decider circuit check to fail due to the bad proof - EXPECT_FALSE(outer_decider_circuit.check_circuit()); + TestFixture::test_tampered_decider_proof(); } -TEST_F(ProtogalaxyRecursiveTest, TamperedAccumulator) +TYPED_TEST(ProtoGalaxyRecursiveTests, TamperedAccumulator) { - // Create two arbitrary circuits for the first round of folding - InnerBuilder builder1; - - create_inner_circuit(builder1); - InnerBuilder builder2; - builder2.add_public_variable(FF(1)); - create_inner_circuit(builder2); - - InnerComposer inner_composer = InnerComposer(); - auto instance1 = inner_composer.create_instance(builder1); - auto instance2 = inner_composer.create_instance(builder2); - auto instances = std::vector>{ instance1, instance2 }; - - auto accumulator = fold_and_verify(instances, inner_composer); - - // Create another circuit to do a second round of folding - InnerBuilder builder3; - create_inner_circuit(builder3); - auto instance3 = inner_composer.create_instance(builder3); - - // Tamper with the accumulator - instances = std::vector>{ accumulator, instance3 }; - accumulator->prover_polynomials.w_l[1] = FF::random_element(); - - // Generate a folding proof - auto inner_folding_prover = inner_composer.create_folding_prover(instances); - auto inner_folding_proof = inner_folding_prover.fold_instances(); - - // Create a recursive folding verifier circuit for the folding proof of the two instances - OuterBuilder outer_folding_circuit; - FoldingRecursiveVerifier verifier{ &outer_folding_circuit }; - verifier.verify_folding_proof(inner_folding_proof.folding_data); - EXPECT_EQ(outer_folding_circuit.check_circuit(), false); + TestFixture::test_tampered_accumulator(); } } // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp index 1f41c5325abf..ed610d57ada1 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp @@ -1,345 +1,412 @@ +#include "barretenberg/goblin/mock_circuits.hpp" #include "barretenberg/polynomials/pow.hpp" #include "barretenberg/protogalaxy/protogalaxy_prover.hpp" #include "barretenberg/ultra_honk/ultra_composer.hpp" #include using namespace bb; using namespace bb::honk; - -using Flavor = flavor::Ultra; -using VerificationKey = Flavor::VerificationKey; -using Instance = ProverInstance_; -using Instances = ProverInstances_; -using ProtoGalaxyProver = ProtoGalaxyProver_; -using FF = Flavor::FF; -using Affine = Flavor::Commitment; -using Projective = Flavor::GroupElement; -using Builder = Flavor::CircuitBuilder; -using ProverPolynomials = Flavor::ProverPolynomials; -using WitnessCommitments = typename Flavor::WitnessCommitments; -using CommitmentKey = Flavor::CommitmentKey; - -const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; - namespace { auto& engine = numeric::get_debug_randomness(); } -// TODO(https://github.com/AztecProtocol/barretenberg/issues/744): make testing utility with functionality shared -// amongst test files in the proof system -Polynomial get_random_polynomial(size_t size) -{ - auto poly = bb::Polynomial(size); - for (auto& coeff : poly) { - coeff = FF::random_element(); - } - return poly; -} -ProverPolynomials construct_ultra_full_polynomials(auto& input_polynomials) -{ - ProverPolynomials full_polynomials; - for (auto [prover_poly, input_poly] : zip_view(full_polynomials.get_all(), input_polynomials)) { - prover_poly = input_poly.share(); - } - return full_polynomials; -} +template class ProtoGalaxyTests : public testing::Test { + public: + using Composer = UltraComposer_; + using VerificationKey = Flavor::VerificationKey; + using Instance = ProverInstance_; + using Instances = ProverInstances_; + using ProtoGalaxyProver = ProtoGalaxyProver_; + using FF = Flavor::FF; + using Affine = Flavor::Commitment; + using Projective = Flavor::GroupElement; + using Builder = Flavor::CircuitBuilder; + using Polynomial = typename Flavor::Polynomial; + using ProverPolynomials = Flavor::ProverPolynomials; + using RelationParameters = bb::RelationParameters; + using WitnessCommitments = typename Flavor::WitnessCommitments; + using CommitmentKey = Flavor::CommitmentKey; + using PowPolynomial = bb::PowPolynomial; -std::shared_ptr fold_and_verify(const std::vector>& instances, - UltraComposer& composer, - bool expected_result) -{ - auto folding_prover = composer.create_folding_prover(instances); - auto folding_verifier = composer.create_folding_verifier(); - - auto proof = folding_prover.fold_instances(); - auto next_accumulator = proof.accumulator; - auto res = folding_verifier.verify_folding_proof(proof.folding_data); - EXPECT_EQ(res, expected_result); - return next_accumulator; -} + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } -void check_accumulator_target_sum_manual(std::shared_ptr& accumulator, bool expected_result) -{ - auto instance_size = accumulator->instance_size; - auto expected_honk_evals = ProtoGalaxyProver::compute_full_honk_evaluations( - accumulator->prover_polynomials, accumulator->alphas, accumulator->relation_parameters); - // Construct pow(\vec{betas*}) as in the paper - auto expected_pows = PowPolynomial(accumulator->gate_challenges); - expected_pows.compute_values(); - - // Compute the corresponding target sum and create a dummy accumulator - auto expected_target_sum = FF(0); - for (size_t i = 0; i < instance_size; i++) { - expected_target_sum += expected_honk_evals[i] * expected_pows[i]; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/744): make testing utility with functionality shared + // amongst test files in the proof system + static bb::Polynomial get_random_polynomial(size_t size) + { + auto poly = bb::Polynomial(size); + for (auto& coeff : poly) { + coeff = FF::random_element(); + } + return poly; } - EXPECT_EQ(accumulator->target_sum == expected_target_sum, expected_result); -} -void decide_and_verify(std::shared_ptr& accumulator, UltraComposer& composer, bool expected_result) -{ - auto decider_prover = composer.create_decider_prover(accumulator); - auto decider_verifier = composer.create_decider_verifier(accumulator); - auto decision = decider_prover.construct_proof(); - auto verified = decider_verifier.verify_proof(decision); - EXPECT_EQ(verified, expected_result); -} + static void construct_circuit(Builder& builder) + { + if constexpr (IsGoblinFlavor) { + GoblinMockCircuits::construct_goblin_ecc_op_circuit(builder); + GoblinMockCircuits::construct_arithmetic_circuit(builder); + + } else { + 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) }); + } + } -class ProtoGalaxyTests : public ::testing::Test { - public: - static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } -}; + static ProverPolynomials construct_full_prover_polynomials(auto& input_polynomials) + { + ProverPolynomials full_polynomials; + for (auto [prover_poly, input_poly] : zip_view(full_polynomials.get_all(), input_polynomials)) { + prover_poly = input_poly.share(); + } + return full_polynomials; + } -TEST_F(ProtoGalaxyTests, FullHonkEvaluationsValidCircuit) -{ - auto builder = Builder(); - FF a = FF::one(); - uint32_t a_idx = builder.add_public_variable(a); - FF b = FF::one(); - FF c = a + b; - uint32_t b_idx = builder.add_variable(b); - uint32_t c_idx = builder.add_variable(c); - builder.create_add_gate({ a_idx, b_idx, c_idx, 1, 1, -1, 0 }); - builder.create_add_gate({ a_idx, b_idx, c_idx, 1, 1, -1, 0 }); - - auto composer = UltraComposer(); - auto instance = composer.create_instance(builder); - instance->initialize_prover_polynomials(); - - auto eta = FF::random_element(); - auto beta = FF::random_element(); - auto gamma = FF::random_element(); - instance->compute_sorted_accumulator_polynomials(eta); - instance->compute_grand_product_polynomials(beta, gamma); - - for (auto& alpha : instance->alphas) { - alpha = FF::random_element(); + static std::shared_ptr fold_and_verify(const std::vector>& instances, + Composer& composer, + bool expected_result) + { + auto folding_prover = composer.create_folding_prover(instances); + auto folding_verifier = composer.create_folding_verifier(); + + auto proof = folding_prover.fold_instances(); + auto next_accumulator = proof.accumulator; + auto res = folding_verifier.verify_folding_proof(proof.folding_data); + EXPECT_EQ(res, expected_result); + return next_accumulator; } - auto full_honk_evals = ProtoGalaxyProver::compute_full_honk_evaluations( - instance->prover_polynomials, instance->alphas, instance->relation_parameters); - // Evaluations should be 0 for valid circuit - for (const auto& eval : full_honk_evals) { - EXPECT_EQ(eval, FF(0)); + static void check_accumulator_target_sum_manual(std::shared_ptr& accumulator, bool expected_result) + { + auto instance_size = accumulator->instance_size; + auto expected_honk_evals = ProtoGalaxyProver::compute_full_honk_evaluations( + accumulator->prover_polynomials, accumulator->alphas, accumulator->relation_parameters); + // Construct pow(\vec{betas*}) as in the paper + auto expected_pows = PowPolynomial(accumulator->gate_challenges); + expected_pows.compute_values(); + + // Compute the corresponding target sum and create a dummy accumulator + auto expected_target_sum = FF(0); + for (size_t i = 0; i < instance_size; i++) { + expected_target_sum += expected_honk_evals[i] * expected_pows[i]; + } + info("expected_target_sum", expected_target_sum); + + EXPECT_EQ(accumulator->target_sum == expected_target_sum, expected_result); } -} -TEST_F(ProtoGalaxyTests, PerturbatorCoefficients) -{ - std::vector betas = { FF(5), FF(8), FF(11) }; - std::vector deltas = { FF(2), FF(4), FF(8) }; - std::vector full_honk_evaluations = { FF(1), FF(1), FF(1), FF(1), FF(1), FF(1), FF(1), FF(1) }; - auto perturbator = ProtoGalaxyProver::construct_perturbator_coefficients(betas, deltas, full_honk_evaluations); - std::vector expected_values = { FF(648), FF(936), FF(432), FF(64) }; - EXPECT_EQ(perturbator.size(), 4); // log(instance_size) + 1 - for (size_t i = 0; i < perturbator.size(); i++) { - EXPECT_EQ(perturbator[i], expected_values[i]); + static void decide_and_verify(std::shared_ptr& accumulator, Composer& composer, bool expected_result) + { + auto decider_prover = composer.create_decider_prover(accumulator); + auto decider_verifier = composer.create_decider_verifier(accumulator); + auto decision = decider_prover.construct_proof(); + auto verified = decider_verifier.verify_proof(decision); + EXPECT_EQ(verified, expected_result); } -} -TEST_F(ProtoGalaxyTests, PerturbatorPolynomial) -{ - using RelationSeparator = Flavor::RelationSeparator; - const size_t log_instance_size(3); - const size_t instance_size(1 << log_instance_size); + static void full_honk_evaluations_valid() + { + auto builder = typename Flavor::CircuitBuilder(); + construct_circuit(builder); + + auto composer = Composer(); + auto instance = composer.create_instance(builder); + instance->initialize_prover_polynomials(); + + auto eta = FF::random_element(); + auto beta = FF::random_element(); + auto gamma = FF::random_element(); + instance->compute_sorted_accumulator_polynomials(eta); + if constexpr (IsGoblinFlavor) { + instance->compute_logderivative_inverse(beta, gamma); + } + instance->compute_grand_product_polynomials(beta, gamma); + + for (auto& alpha : instance->alphas) { + alpha = FF::random_element(); + } + auto full_honk_evals = ProtoGalaxyProver::compute_full_honk_evaluations( + instance->prover_polynomials, instance->alphas, instance->relation_parameters); + + // Evaluations should be 0 for valid circuit + for (const auto& eval : full_honk_evals) { + EXPECT_EQ(eval, FF(0)); + } + } - std::array, NUM_POLYNOMIALS> random_polynomials; - for (auto& poly : random_polynomials) { - poly = get_random_polynomial(instance_size); + static void test_pertubator_coefficients() + { + std::vector betas = { FF(5), FF(8), FF(11) }; + std::vector deltas = { FF(2), FF(4), FF(8) }; + std::vector full_honk_evaluations = { FF(1), FF(1), FF(1), FF(1), FF(1), FF(1), FF(1), FF(1) }; + auto perturbator = ProtoGalaxyProver::construct_perturbator_coefficients(betas, deltas, full_honk_evaluations); + std::vector expected_values = { FF(648), FF(936), FF(432), FF(64) }; + EXPECT_EQ(perturbator.size(), 4); // log(instance_size) + 1 + for (size_t i = 0; i < perturbator.size(); i++) { + EXPECT_EQ(perturbator[i], expected_values[i]); + } } - auto full_polynomials = construct_ultra_full_polynomials(random_polynomials); - auto relation_parameters = RelationParameters::get_random(); - RelationSeparator alphas; - for (auto& alpha : alphas) { - alpha = FF::random_element(); + + static void test_pertubator_polynomial() + { + using RelationSeparator = Flavor::RelationSeparator; + const size_t log_instance_size(3); + const size_t instance_size(1 << log_instance_size); + std::array, Flavor::NUM_ALL_ENTITIES> random_polynomials; + for (auto& poly : random_polynomials) { + poly = get_random_polynomial(instance_size); + } + auto full_polynomials = construct_full_prover_polynomials(random_polynomials); + auto relation_parameters = bb::RelationParameters::get_random(); + RelationSeparator alphas; + for (auto& alpha : alphas) { + alpha = FF::random_element(); + } + + auto full_honk_evals = + ProtoGalaxyProver::compute_full_honk_evaluations(full_polynomials, alphas, relation_parameters); + std::vector betas(log_instance_size); + for (size_t idx = 0; idx < log_instance_size; idx++) { + betas[idx] = FF::random_element(); + } + + // Construct pow(\vec{betas}) as in the paper + auto pow_beta = bb::PowPolynomial(betas); + pow_beta.compute_values(); + + // Compute the corresponding target sum and create a dummy accumulator + auto target_sum = FF(0); + for (size_t i = 0; i < instance_size; i++) { + target_sum += full_honk_evals[i] * pow_beta[i]; + } + + auto accumulator = std::make_shared(); + accumulator->prover_polynomials = std::move(full_polynomials); + accumulator->gate_challenges = betas; + accumulator->target_sum = target_sum; + accumulator->relation_parameters = relation_parameters; + accumulator->alphas = alphas; + + auto deltas = ProtoGalaxyProver::compute_round_challenge_pows(log_instance_size, FF::random_element()); + auto perturbator = ProtoGalaxyProver::compute_perturbator(accumulator, deltas); + + // Ensure the constant coefficient of the perturbator is equal to the target sum as indicated by the paper + EXPECT_EQ(perturbator[0], target_sum); } - auto full_honk_evals = - ProtoGalaxyProver::compute_full_honk_evaluations(full_polynomials, alphas, relation_parameters); - std::vector betas(log_instance_size); - for (size_t idx = 0; idx < log_instance_size; idx++) { - betas[idx] = FF::random_element(); + static void test_combiner_quotient() + { + auto compressed_perturbator = FF(2); // F(\alpha) in the paper + auto combiner = + bb::Univariate(std::array{ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }); + auto combiner_quotient = ProtoGalaxyProver::compute_combiner_quotient(compressed_perturbator, combiner); + + // K(i) = (G(i) - ( L_0(i) * F(\alpha)) / Z(i), i = {2,.., 13} for ProverInstances::NUM = 2 + // K(i) = (G(i) - (1 - i) * F(\alpha)) / i * (i - 1) + auto expected_evals = bb::Univariate(std::array{ + (FF(22) - (FF(1) - FF(2)) * compressed_perturbator) / (FF(2) * FF(2 - 1)), + (FF(23) - (FF(1) - FF(3)) * compressed_perturbator) / (FF(3) * FF(3 - 1)), + (FF(24) - (FF(1) - FF(4)) * compressed_perturbator) / (FF(4) * FF(4 - 1)), + (FF(25) - (FF(1) - FF(5)) * compressed_perturbator) / (FF(5) * FF(5 - 1)), + (FF(26) - (FF(1) - FF(6)) * compressed_perturbator) / (FF(6) * FF(6 - 1)), + (FF(27) - (FF(1) - FF(7)) * compressed_perturbator) / (FF(7) * FF(7 - 1)), + (FF(28) - (FF(1) - FF(8)) * compressed_perturbator) / (FF(8) * FF(8 - 1)), + (FF(29) - (FF(1) - FF(9)) * compressed_perturbator) / (FF(9) * FF(9 - 1)), + (FF(30) - (FF(1) - FF(10)) * compressed_perturbator) / (FF(10) * FF(10 - 1)), + (FF(31) - (FF(1) - FF(11)) * compressed_perturbator) / (FF(11) * FF(11 - 1)), + (FF(32) - (FF(1) - FF(12)) * compressed_perturbator) / (FF(12) * FF(12 - 1)), + }); + + for (size_t idx = 2; idx < 7; idx++) { + EXPECT_EQ(combiner_quotient.value_at(idx), expected_evals.value_at(idx)); + } } - // Construct pow(\vec{betas}) as in the paper - auto pow_beta = bb::PowPolynomial(betas); - pow_beta.compute_values(); + static void test_combine_relation_parameters() + { + using Instances = ProverInstances_; + using Instance = typename Instances::Instance; + + Builder builder1; + auto instance1 = std::make_shared(builder1); + instance1->relation_parameters.eta = 1; - // Compute the corresponding target sum and create a dummy accumulator - auto target_sum = FF(0); - for (size_t i = 0; i < instance_size; i++) { - target_sum += full_honk_evals[i] * pow_beta[i]; + Builder builder2; + builder2.add_variable(3); + auto instance2 = std::make_shared(builder2); + instance2->relation_parameters.eta = 3; + + Instances instances{ { instance1, instance2 } }; + ProtoGalaxyProver::combine_relation_parameters(instances); + + bb::Univariate expected_eta{ { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 } }; + EXPECT_EQ(instances.relation_parameters.eta, expected_eta); } - auto accumulator = std::make_shared(); - accumulator->prover_polynomials = std::move(full_polynomials); - accumulator->gate_challenges = betas; - accumulator->target_sum = target_sum; - accumulator->relation_parameters = relation_parameters; - accumulator->alphas = alphas; + static void test_combine_alpha() + { + using Instances = ProverInstances_; + using Instance = typename Instances::Instance; - auto deltas = ProtoGalaxyProver::compute_round_challenge_pows(log_instance_size, FF::random_element()); - auto perturbator = ProtoGalaxyProver::compute_perturbator(accumulator, deltas); + Builder builder1; + auto instance1 = std::make_shared(builder1); + instance1->alphas.fill(2); - // Ensure the constant coefficient of the perturbator is equal to the target sum as indicated by the paper - EXPECT_EQ(perturbator[0], target_sum); -} + Builder builder2; + builder2.add_variable(3); + auto instance2 = std::make_shared(builder2); + instance2->alphas.fill(4); -TEST_F(ProtoGalaxyTests, CombinerQuotient) -{ - auto compressed_perturbator = FF(2); // F(\alpha) in the paper - auto combiner = Univariate(std::array{ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }); - auto combiner_quotient = ProtoGalaxyProver::compute_combiner_quotient(compressed_perturbator, combiner); - - // K(i) = (G(i) - ( L_0(i) * F(\alpha)) / Z(i), i = {2,.., 13} for ProverInstances::NUM = 2 - // K(i) = (G(i) - (1 - i) * F(\alpha)) / i * (i - 1) - auto expected_evals = Univariate(std::array{ - (FF(22) - (FF(1) - FF(2)) * compressed_perturbator) / (FF(2) * FF(2 - 1)), - (FF(23) - (FF(1) - FF(3)) * compressed_perturbator) / (FF(3) * FF(3 - 1)), - (FF(24) - (FF(1) - FF(4)) * compressed_perturbator) / (FF(4) * FF(4 - 1)), - (FF(25) - (FF(1) - FF(5)) * compressed_perturbator) / (FF(5) * FF(5 - 1)), - (FF(26) - (FF(1) - FF(6)) * compressed_perturbator) / (FF(6) * FF(6 - 1)), - (FF(27) - (FF(1) - FF(7)) * compressed_perturbator) / (FF(7) * FF(7 - 1)), - (FF(28) - (FF(1) - FF(8)) * compressed_perturbator) / (FF(8) * FF(8 - 1)), - (FF(29) - (FF(1) - FF(9)) * compressed_perturbator) / (FF(9) * FF(9 - 1)), - (FF(30) - (FF(1) - FF(10)) * compressed_perturbator) / (FF(10) * FF(10 - 1)), - (FF(31) - (FF(1) - FF(11)) * compressed_perturbator) / (FF(11) * FF(11 - 1)), - (FF(32) - (FF(1) - FF(12)) * compressed_perturbator) / (FF(12) * FF(12 - 1)), - }); - - for (size_t idx = 2; idx < 7; idx++) { - EXPECT_EQ(combiner_quotient.value_at(idx), expected_evals.value_at(idx)); + Instances instances{ { instance1, instance2 } }; + ProtoGalaxyProver::combine_alpha(instances); + + bb::Univariate expected_alpha{ { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26 } }; + for (const auto& alpha : instances.alphas) { + EXPECT_EQ(alpha, expected_alpha); + } } -} -TEST_F(ProtoGalaxyTests, CombineRelationParameters) -{ - using Instances = ProverInstances_; - using Instance = typename Instances::Instance; + static void test_full_protogalaxy() + { + auto composer = Composer(); + auto builder_1 = typename Flavor::CircuitBuilder(); + construct_circuit(builder_1); - Builder builder1; - auto instance1 = std::make_shared(builder1); - instance1->relation_parameters.eta = 1; + auto instance_1 = composer.create_instance(builder_1); - Builder builder2; - builder2.add_variable(3); - auto instance2 = std::make_shared(builder2); - instance2->relation_parameters.eta = 3; + auto builder_2 = typename Flavor::CircuitBuilder(); + construct_circuit(builder_2); - Instances instances{ { instance1, instance2 } }; - ProtoGalaxyProver::combine_relation_parameters(instances); + auto instance_2 = composer.create_instance(builder_2); - Univariate expected_eta{ { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 } }; - EXPECT_EQ(instances.relation_parameters.eta, expected_eta); -} + auto instances = std::vector>{ instance_1, instance_2 }; + auto first_accumulator = fold_and_verify(instances, composer, true); + check_accumulator_target_sum_manual(first_accumulator, true); -TEST_F(ProtoGalaxyTests, CombineAlpha) -{ - using Instances = ProverInstances_; - using Instance = typename Instances::Instance; + auto builder_3 = typename Flavor::CircuitBuilder(); + construct_circuit(builder_3); + auto instance_3 = composer.create_instance(builder_3); - Builder builder1; - auto instance1 = std::make_shared(builder1); - instance1->alphas.fill(2); + instances = std::vector>{ first_accumulator, instance_3 }; + auto second_accumulator = fold_and_verify(instances, composer, true); + check_accumulator_target_sum_manual(second_accumulator, true); - Builder builder2; - builder2.add_variable(3); - auto instance2 = std::make_shared(builder2); - instance2->alphas.fill(4); + decide_and_verify(first_accumulator, composer, true); + } - Instances instances{ { instance1, instance2 } }; - ProtoGalaxyProver::combine_alpha(instances); + static void test_tampered_commitment() + { + auto composer = Composer(); - Univariate expected_alpha{ { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26 } }; - for (const auto& alpha : instances.alphas) { - EXPECT_EQ(alpha, expected_alpha); - } -} + auto builder_1 = typename Flavor::CircuitBuilder(); + construct_circuit(builder_1); -// Check both manually and using the protocol two rounds of folding -TEST_F(ProtoGalaxyTests, FullProtogalaxyTest) -{ - auto composer = UltraComposer(); + auto instance_1 = composer.create_instance(builder_1); - auto builder_1 = typename Flavor::CircuitBuilder(); - builder_1.add_public_variable(FF(1)); + auto builder_2 = typename Flavor::CircuitBuilder(); + construct_circuit(builder_2); - auto instance_1 = composer.create_instance(builder_1); + auto instance_2 = composer.create_instance(builder_2); - auto builder_2 = typename Flavor::CircuitBuilder(); - builder_2.add_public_variable(FF(1)); + auto instances = std::vector>{ instance_1, instance_2 }; + auto first_accumulator = fold_and_verify(instances, composer, true); + check_accumulator_target_sum_manual(first_accumulator, true); - auto instance_2 = composer.create_instance(builder_2); + auto builder_3 = typename Flavor::CircuitBuilder(); + construct_circuit(builder_3); + auto instance_3 = composer.create_instance(builder_3); - auto instances = std::vector>{ instance_1, instance_2 }; - auto first_accumulator = fold_and_verify(instances, composer, true); - check_accumulator_target_sum_manual(first_accumulator, true); + // tampering with the commitment should cause the decider to fail + first_accumulator->witness_commitments.w_l = Projective(Affine::random_element()); + instances = std::vector>{ first_accumulator, instance_3 }; - auto builder_3 = typename Flavor::CircuitBuilder(); - builder_3.add_public_variable(FF(1)); - auto instance_3 = composer.create_instance(builder_3); + auto second_accumulator = fold_and_verify(instances, composer, true); - instances = std::vector>{ first_accumulator, instance_3 }; - auto second_accumulator = fold_and_verify(instances, composer, true); - check_accumulator_target_sum_manual(second_accumulator, true); + decide_and_verify(second_accumulator, composer, false); + } - decide_and_verify(second_accumulator, composer, true); -} + static void test_tampered_accumulator_polynomial() + { + auto composer = Composer(); -TEST_F(ProtoGalaxyTests, TamperedCommitment) -{ - auto composer = UltraComposer(); + auto builder_1 = typename Flavor::CircuitBuilder(); + construct_circuit(builder_1); - auto builder_1 = typename Flavor::CircuitBuilder(); - builder_1.add_public_variable(FF(1)); + auto instance_1 = composer.create_instance(builder_1); - auto instance_1 = composer.create_instance(builder_1); + auto builder_2 = typename Flavor::CircuitBuilder(); + construct_circuit(builder_2); - auto builder_2 = typename Flavor::CircuitBuilder(); - builder_2.add_public_variable(FF(1)); + auto instance_2 = composer.create_instance(builder_2); - auto instance_2 = composer.create_instance(builder_2); + auto instances = std::vector>{ instance_1, instance_2 }; + auto first_accumulator = fold_and_verify(instances, composer, true); + check_accumulator_target_sum_manual(first_accumulator, true); - auto instances = std::vector>{ instance_1, instance_2 }; - auto first_accumulator = fold_and_verify(instances, composer, true); - check_accumulator_target_sum_manual(first_accumulator, true); + auto builder_3 = typename Flavor::CircuitBuilder(); + construct_circuit(builder_3); + auto instance_3 = composer.create_instance(builder_3); - auto builder_3 = typename Flavor::CircuitBuilder(); - builder_3.add_public_variable(FF(1)); - auto instance_3 = composer.create_instance(builder_3); + // tampering with accumulator's polynomial should cause both folding and deciding to fail + instances = std::vector>{ first_accumulator, instance_3 }; + first_accumulator->prover_polynomials.w_l[1] = FF::random_element(); + auto second_accumulator = fold_and_verify(instances, composer, false); - // tampering with the commitment should cause the decider to fail - first_accumulator->witness_commitments.w_l = Projective(Affine::random_element()); - instances = std::vector>{ first_accumulator, instance_3 }; + decide_and_verify(second_accumulator, composer, false); + } +}; - auto second_accumulator = fold_and_verify(instances, composer, true); +using FlavorTypes = testing::Types; +TYPED_TEST_SUITE(ProtoGalaxyTests, FlavorTypes); - decide_and_verify(second_accumulator, composer, false); +TYPED_TEST(ProtoGalaxyTests, PerturbatorCoefficients) +{ + TestFixture::test_pertubator_coefficients(); } -TEST_F(ProtoGalaxyTests, TamperedAccumulatorPolynomial) +TYPED_TEST(ProtoGalaxyTests, FullHonkEvaluationsValidCircuit) { - auto composer = UltraComposer(); - - auto builder_1 = typename Flavor::CircuitBuilder(); - builder_1.add_public_variable(FF(1)); + TestFixture::full_honk_evaluations_valid(); +} - auto instance_1 = composer.create_instance(builder_1); +TYPED_TEST(ProtoGalaxyTests, PerturbatorPolynomial) +{ + TestFixture::test_pertubator_polynomial(); +} - auto builder_2 = typename Flavor::CircuitBuilder(); - builder_2.add_public_variable(FF(1)); +TYPED_TEST(ProtoGalaxyTests, CombinerQuotient) +{ + TestFixture::test_combiner_quotient(); +} - auto instance_2 = composer.create_instance(builder_2); +TYPED_TEST(ProtoGalaxyTests, CombineRelationParameters) +{ + TestFixture::test_combine_relation_parameters(); +} - auto instances = std::vector>{ instance_1, instance_2 }; - auto first_accumulator = fold_and_verify(instances, composer, true); - check_accumulator_target_sum_manual(first_accumulator, true); +TYPED_TEST(ProtoGalaxyTests, CombineAlpha) +{ + TestFixture::test_combine_alpha(); +} - auto builder_3 = typename Flavor::CircuitBuilder(); - builder_3.add_public_variable(FF(1)); - auto instance_3 = composer.create_instance(builder_3); +// Check both manually and using the protocol two rounds of folding +TYPED_TEST(ProtoGalaxyTests, FullProtogalaxyTest) +{ + TestFixture::test_full_protogalaxy(); +} - // tampering with accumulator's polynomial should cause both folding and deciding to fail - instances = std::vector>{ first_accumulator, instance_3 }; - first_accumulator->prover_polynomials.w_l[1] = FF::random_element(); - auto second_accumulator = fold_and_verify(instances, composer, false); +TYPED_TEST(ProtoGalaxyTests, TamperedCommitment) +{ + TestFixture::test_tampered_commitment(); +} - decide_and_verify(second_accumulator, composer, false); +TYPED_TEST(ProtoGalaxyTests, TamperedAccumulatorPolynomial) +{ + TestFixture::test_tampered_accumulator_polynomial(); } \ No newline at end of file